/*
containerPanel.java
This graphical component manages the display and editing of all
fields in a given object tab. Used in window Panels.
Created: 11 August 1997
Module By: Michael Mulvaney
-----------------------------------------------------------------------
Ganymede Directory Management System
Copyright (C) 1996-2014
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.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.rmi.RemoteException;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import arlut.csd.JDataComponent.JAddValueObject;
import arlut.csd.JDataComponent.JAddVectorValueObject;
import arlut.csd.JDataComponent.JDeleteValueObject;
import arlut.csd.JDataComponent.JDeleteVectorValueObject;
import arlut.csd.JDataComponent.JErrorValueObject;
import arlut.csd.JDataComponent.JIPField;
import arlut.csd.JDataComponent.JLabelPanel;
import arlut.csd.JDataComponent.JParameterValueObject;
import arlut.csd.JDataComponent.JResetDateObject;
import arlut.csd.JDataComponent.JStretchPanel;
import arlut.csd.JDataComponent.JValueObject;
import arlut.csd.JDataComponent.JdateField;
import arlut.csd.JDataComponent.JfloatField;
import arlut.csd.JDataComponent.JnumberField;
import arlut.csd.JDataComponent.JpassField;
import arlut.csd.JDataComponent.JsetValueCallback;
import arlut.csd.JDataComponent.JstringArea;
import arlut.csd.JDataComponent.JstringField;
import arlut.csd.JDataComponent.StringSelector;
import arlut.csd.JDataComponent.TimedKeySelectionManager;
import arlut.csd.JDataComponent.listHandle;
import arlut.csd.Util.TranslationService;
import arlut.csd.Util.VecSortInsert;
import arlut.csd.ganymede.common.FieldInfo;
import arlut.csd.ganymede.common.FieldTemplate;
import arlut.csd.ganymede.common.FieldType;
import arlut.csd.ganymede.common.Invid;
import arlut.csd.ganymede.common.IPAddress;
import arlut.csd.ganymede.common.QueryResult;
import arlut.csd.ganymede.common.ReturnVal;
import arlut.csd.ganymede.common.SchemaConstants;
import arlut.csd.ganymede.rmi.date_field;
import arlut.csd.ganymede.rmi.db_field;
import arlut.csd.ganymede.rmi.db_object;
import arlut.csd.ganymede.rmi.invid_field;
import arlut.csd.ganymede.rmi.ip_field;
import arlut.csd.ganymede.rmi.pass_field;
import arlut.csd.ganymede.rmi.perm_field;
import arlut.csd.ganymede.rmi.string_field;
import arlut.csd.ganymede.rmi.field_option_field;
/*------------------------------------------------------------------------------
class
containerPanel
------------------------------------------------------------------------------*/
/**
* <p>One of the basic building blocks of the ganymede client, a
* containerPanel is a GUI panel which allows the user to view and/or
* edit all the custom fields for an object tab in the Ganymede
* database.</p>
*
* <p>Each containerPanel displays a single {@link
* arlut.csd.ganymede.rmi.db_object db_object}, and allows the user to
* edit or view each {@link arlut.csd.ganymede.rmi.db_field db_field}
* in the object. On loading, containerPanel loops through the fields
* of the object, adding the appropriate type of input for each field.
* This includes text fields, number fields, boolean fields, and
* string selector fields(fields that can have multiple values).</p>
*
* <p>containerPanel handles the connection between GUI components and
* server fields, translating GUI activity to attempted changes to
* server fields. Any attempted change that the server refuses will
* cause a dialog to be popped up via gclient's {@link
* arlut.csd.ganymede.client.gclient#handleReturnVal(arlut.csd.ganymede.common.ReturnVal)
* handleReturnVal()} method, and the GUI component that caused the
* change will be reverted to its pre-change status.</p>
*
* <p>The gclient's handleReturnVal() method also supports extracting a
* list of objects and fields that need to be refreshed when one
* change on the server is reflected across more than one object.
* This is handled by containerPanel's {@link
* arlut.csd.ganymede.client.containerPanel#update(java.util.Vector)
* update()} method.</p>
*
* @author Mike Mulvaney
*/
public class containerPanel extends JStretchPanel implements ActionListener, JsetValueCallback, ItemListener {
static final boolean debug = false;
static final boolean debug_persona = false;
/**
* Number of columns to size our string fields to, one of our
* primary references for our layout.
*/
static final int FIELDWIDTH = 35;
/**
* TranslationService object for handling string localization in the
* Ganymede client.
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.client.containerPanel");
static final String edit_action = ts.l("global.edit_action"); // "Edit Object"
static final String view_action = ts.l("global.view_action"); // "View Object"
// ---
/**
* Reference to the client's main class, used for some utility functions.
*/
gclient gc;
/**
* Remote reference to the server-side object we are viewing or editing.
*/
private db_object object;
/**
* Database id for the object we are viewing or editing
*/
private Invid invid;
/**
* Reference to the desktop pane containing the client's internal windows. Used to access
* some GUI resources.
*/
windowPanel winP;
/**
* The window we are contained in, may be null if we are embedded in a
* {@link arlut.csd.ganymede.client.vectorPanel vectorPanel}.
*/
protected framePanel frame;
/**
* All of the components in this containerPanel are placed in this
* {@link arlut.csd.JDataComponent.JLabelPanel JLabelPanel}, which
* automatically takes care of the layout and management of labeled
* fields in this panel.
*/
private JLabelPanel contentsPanel;
/**
* Vector of Short field id's used to track fields for which we
* receive update requests while we are still loading. After we
* finish loading this panel, we'll go back and refresh any fields
* whose field id's are listed in this vector.
*/
private Vector<Short> updatesWhileLoading = new Vector();
/**
* Vector used to list vectorPanels embedded in this object window.
* This variable is used by {@link
* arlut.csd.ganymede.client.vectorPanel#expandAllLevels()
* vectorPanel.expandAllLevels()} to do recursive expansion of
* embedded objects.
*/
Vector<vectorPanel> vectorPanelList = new Vector();
/**
* To help avoid recursive problems, we keep track of any
* arlut.csd.JDataComponent GUI components that are currently having
* their change notification messages handled, and refuse to try to
* refresh them re-entrantly.
*/
private JComponent currentlyChangingComponent = null;
/**
* Hashtable mapping GUI components to their associated
* {@link arlut.csd.ganymede.rmi.db_field db_field}'s.
*/
private Hashtable<Component, db_field> objectHash = new Hashtable<Component, db_field>();
/**
* Hashtable mapping GUI components to their associated
* FieldTemplate objects.
*/
private Hashtable<JComponent, FieldTemplate> objectTemplateHash = new Hashtable<JComponent, FieldTemplate>();
/**
* Hashtable mapping Short Field ID numbers to their associated
* GUI components.
*/
private Hashtable<Short, Component> idHash = new Hashtable<Short, Component>();
/**
* <p>Hashtable mapping the combo boxes contained within
* {@link arlut.csd.ganymede.client.JInvidChooser JInvidChooser}
* GUI components to their associated
* {@link arlut.csd.ganymede.rmi.invid_field invid_field}'s.</p>
*
* <p>This is required because while we want to hide or reveal the JInvidChooser
* as a whole, we'll get itemStateChanged() calls from the combo
* box within the JInvidChooser.</p>
*/
private Hashtable<JComboBox, invid_field> invidChooserHash = new Hashtable<JComboBox, invid_field>();
/**
* Vector of {@link arlut.csd.ganymede.common.FieldInfo FieldInfo} objects
* holding the values for fields in this object. Used during loading
* and update.
*/
private Vector<FieldInfo> infoVector = null;
/**
* Vector of {@link arlut.csd.ganymede.common.FieldTemplate FieldTemplate}
* objects holding the constant field type information for fields in
* this object.
*/
private Vector<FieldTemplate> templates = null;
/**
* The name of the tab this containerPanel is limited to showing
* when the load() method is called. If this variable is null,
* load() will load and display all custom field definitions from
* the server.
*/
private String tabName = null;
boolean
isCreating,
editable;
boolean
isEmbedded,
loading = false,
loaded = false;
/**
* If progressBar is not null, the load() method for containerPanel will
* update this progressBar as the panel is loaded from the server.
*/
private JProgressBar progressBar;
int
vectorElementsAdded = 0;
/**
* Object type id for this object.. should be equal to invid.getType().
*/
short type;
/**
* <p>If true, this containerPanel is being displayed in a persona
* pane in a frame panel, and we'll hide the associated user field,
* which is implicit when we embedded a persona panel in a
* framePanel showing a user object.</p>
*
* <p>This is a dirty hack to make the client a little extra smart
* about one particular kind of mandatory Ganymede server
* object.</p>
*/
boolean isPersonaPanel = false;
/* -- */
/**
* Constructor with default values for progressBar set to false, loadNow
* set to true, and isCreating set to false.
*
* @param object The object to be displayed
* @param editable If true, the fields presented will be enabled for editing
* @param gc Parent gclient of this container
* @param window windowPanel containing this containerPanel
* @param frame framePanel holding this containerPanel(although this cp is not necessarily in the "General" tab)
* @param context An object that can be provided to identify the context in
* which this containerPanel is being created.
*/
public containerPanel(db_object object,
Invid invid,
boolean editable,
gclient gc,
windowPanel window,
framePanel frame,
Object context)
{
this(object, invid, editable, gc, window, frame, null, true, context);
}
/**
* <p>Constructor with default values for loadNow set to true, and
* isCreating set to false.</p>
*
* <p>The <progressBar> parameter is used so that
* containerpanel can increment an external JProgressBar as
* information on the fields for this object are loaded from the
* server. progressBar should be null if this containerPanel is not
* serving as the main panel for a framePanel.</p>a
*
* @param object The object to be displayed
* @param editable If true, the fields presented will be enabled for editing
* @param gc Parent gclient of this container
* @param window windowPanel containing this containerPanel
* @param frame framePanel holding this containerPanel
* @param progressBar JProgressBar to be updated, can be null
* @param context An object that can be provided to identify the context in
* which this containerPanel is being created.
*/
public containerPanel(db_object object,
Invid invid,
boolean editable,
gclient gc,
windowPanel window,
framePanel frame,
JProgressBar progressBar,
Object context)
{
this(object, invid, editable, gc, window, frame, progressBar, true, context);
}
/**
* <p>Constructor with default value for isCreating set to
* false.</p>
*
* <p>The <progressBar> parameter is used so that
* containerpanel can increment an external JProgressBar as
* information on the fields for this object are loaded from
* the server.</p>
*
* @param object The object to be displayed
* @param editable If true, the fields presented will be enabled for editing
* @param gc Parent gclient of this container
* @param window windowPanel containing this containerPanel
* @param progressBar JProgressBar to be updated, can be null
* @param loadNow If true, container panel will be loaded immediately
* @param context An object that can be provided to identify the context in
* which this containerPanel is being created.
*/
public containerPanel(db_object object,
Invid invid,
boolean editable,
gclient gc,
windowPanel window,
framePanel frame,
JProgressBar progressBar,
boolean loadNow,
Object context)
{
this(object, invid, editable, gc, window, frame, progressBar, loadNow, false, context);
}
/**
* <p>Primary constructor for containerPanel</p>
*
* <p>The <progressBar> parameter is used so that
* containerpanel can increment an external JProgressBar as
* information on the fields for this object are loaded from the
* server. progressBar should be null if this containerPanel is not
* serving as the main panel for a framePanel.</p>
*
* @param object The object to be displayed
* @param editable If true, the fields presented will be enabled for editing
* @param gc Parent gclient of this container
* @param window windowPanel containing this containerPanel
* @param progressBar JProgressBar to be updated, can be null
* @param loadNow If true, container panel will be loaded immediately
* @param isCreating
* @param context An object that can be provided to identify the context in
* which this containerPanel is being created.
*/
public containerPanel(db_object object,
Invid invid,
boolean editable,
gclient gc,
windowPanel window,
framePanel frame,
JProgressBar progressBar,
boolean loadNow,
boolean isCreating,
Object context)
{
super(false);
/* -- */
this.gc = gc;
if (object == null)
{
throw new NullPointerException("null object passed to containerPanel constructor");
}
this.winP = window;
this.object = object;
this.invid = invid;
this.editable = editable;
this.frame = frame;
this.progressBar = progressBar;
this.isCreating = isCreating;
if (context != null && (context instanceof personaContainer))
{
this.isPersonaPanel = true;
}
frame.addContainerPanel(this);
// initialize layout
contentsPanel = new JLabelPanel();
contentsPanel.setLeftInsets(4,4,4,4);
contentsPanel.setRightInsets(4,4,4,0); // no right margin
contentsPanel.setFixedSizeLabelCells(true);
setComponent(contentsPanel);
if (loadNow)
{
load();
}
}
/**
* <p>This method is used to set the name of the tab that this
* containerPanel is limited to showing.</p>
*
* <p>If the tabName is null (or if setTabName() has not been
* called), the load() method in this class will simply display all
* fields.</p>
*
* <p>This method will not have any effect if it is called after
* load()</p>
*/
public void setTabName(String tabName)
{
this.tabName = tabName;
}
/**
* This method is used to pre-load a Vector of {@link
* arlut.csd.ganymede.common.FieldInfo} objects into this
* containerPanel. The goal is to allow multiple tabs to share a
* FieldInfo Vector that only needs to be downloaded once.
*/
public void setInfoVector(Vector<FieldInfo> infoVector)
{
this.infoVector = infoVector;
}
/**
* Downloads all necessary information from the server
* about the object being viewed or edited. Typically this is called
* when the containerPanel is initialized by the containerPanel
* constructor, but we defer loading when we are placed in a vector
* panel hierarchy.
*/
public void load()
{
loading = true;
int infoSize;
FieldInfo
fieldInfo = null;
FieldTemplate
fieldTemplate = null;
short ID;
/* -- */
if (loaded)
{
printErr("Container panel is already loaded!");
return;
}
if (debug)
{
println("Loading container panel");
}
try
{
// if we are a top-level container panel in a general pane
// or persona pane, we'll have a progress bar.. we'll want
// to update it as we go along loading field information.
if (progressBar != null)
{
progressBar.setMinimum(0);
progressBar.setMaximum(20);
progressBar.setValue(0);
}
// Get the list of fields
if (debug)
{
println("Getting list of fields");
}
type = invid.getType();
setProgressBar(1);
// pull static field type information from the client's caches
templates = gc.getTemplateVector(type);
if (templates == null || templates.size() == 0)
{
printErr("No fields defined for this object type.. ??");
if (templates == null)
{
printErr("templates is *null*");
}
else
{
printErr("templates is empty");
}
return;
}
setProgressBar(2);
//
// ok, got the list of field definitions. Now we need to get
// the current values and visibility information for the fields
// in this object.
//
// Note that if we have previously been pre-loaded with a
// FieldInfo Vector, we won't bother calling the server to get
// one.
//
if (infoVector == null)
{
try
{
infoVector = object.getFieldInfoVector();
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
if (infoVector.size() == 0)
{
printErr("No field info in getFieldInfoVector()");
}
// keep a copy of the infoVector size so we don't have to
// continously call the synchronized infoVector.size()
// method during our loops
infoSize = infoVector.size();
// now we know how many fields are actually present in this
// object, we can set the max size of the progress bar (plus
// how many elements in each vector panel.)
if (progressBar != null)
{
int totalSize = 0;
for (FieldInfo info: infoVector)
{
FieldTemplate template = findtemplate(info.getID());
if (this.tabName == null)
{
totalSize++;
}
else if (this.tabName.equals(template.getTabName()))
{
totalSize++;
}
if (template.isArray())
{
if ((template.getType() == FieldType.INVID) && template.isEditInPlace())
{
totalSize += ((Vector)info.getValue()).size();
}
}
}
progressBar.setMaximum(totalSize);
progressBar.setValue(3);
}
if (debug)
{
println("Entering big loop");
}
for (int i = 0; i < infoSize; i++)
{
// let the gclient interrupt us
if (!keepLoading())
{
break;
}
setProgressBar(i + 3 + vectorElementsAdded);
try
{
fieldInfo = infoVector.get(i);
ID = fieldInfo.getID();
fieldTemplate = findtemplate(ID);
if (fieldTemplate == null)
{
throw new RuntimeException("Could not find the template for this field: " +
fieldInfo.getField());
}
// Skip some fields. custom panels hold the built ins, and a few others.
// If we are a persona panel, hide the associated user field.
if ((ID == SchemaConstants.BackLinksField)
|| ((type == SchemaConstants.UserBase) &&
(ID == SchemaConstants.UserAdminPersonae))
|| ((ID == SchemaConstants.ContainerField) &&
object.isEmbedded())
|| (isPersonaPanel &&
(type == SchemaConstants.PersonaBase) &&
(ID == SchemaConstants.PersonaAssocUser)))
{
if (debug)
{
println("Skipping a special field: " + fieldTemplate.getName());
}
continue;
}
// skip fields for other tabs
if (this.tabName != null && !this.tabName.equals(fieldTemplate.getTabName()))
{
continue;
}
// and do the work. If we're read only, we don't want
// to bother showing fields that are undefined. In
// fact, we should only see undefined fields if we are
// viewing an object that has been checked out for
// editing by the current transaction. Normally, when
// we view an object from the server and a read-only
// copy is created for us, fields without values
// simply aren't part of the object.
if (editable || fieldInfo.isDefined())
{
addFieldComponent(fieldInfo.getField(), fieldInfo, fieldTemplate);
}
}
catch (Exception ex)
{
gc.processExceptionRethrow(ex);
}
}
if (debug)
{
println("Done with loop");
}
}
finally
{
loaded = true;
loading = false;
if (!keepLoading())
{
return;
}
// If update(Vector) was called during the load, then any
// fields to be updated were added to the updatesWhileLoading
// vector. So call update with that vector now, if it has any
// size.
synchronized (updatesWhileLoading)
{
if (updatesWhileLoading.size() > 0)
{
if (debug)
{
println("Calling update with the updatesWhileLoading vector.");
}
update(updatesWhileLoading);
}
}
}
}
/**
* Helper method to keep the load() method clean.
*/
private final void setProgressBar(int count)
{
if (progressBar != null)
{
progressBar.setValue(count);
}
}
/**
* Helper method to keep the load() method clean.
*/
private final FieldTemplate findtemplate(short type)
{
for (FieldTemplate template: templates)
{
if (template.getID() == type)
{
return template;
}
}
return null;
}
/**
* This is a convenience method for other client classes to access
* our gclient reference.
*/
public final gclient getgclient()
{
return gc;
}
/**
* Use this to print stuff out, so we know it is from the containerPanel
*/
private final void println(String s)
{
System.out.println("containerPanel: " + s);
}
private final void printErr(String s)
{
System.err.println("containerPanel err: " + s);
}
/**
* Get the object contained in this containerPanel.
*/
public db_object getObject()
{
return object;
}
/**
* Get the invid for the object in this containerPanel.
*/
public Invid getObjectInvid()
{
if (invid == null)
{
try
{
invid = object.getInvid().intern();
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
return invid;
}
/**
* This method returns true if this containerPanel has already
* been loaded.
*/
public boolean isLoaded()
{
return loaded;
}
/**
* This method returns false when the containerPanel loading has
* been interupted. The vectorPanel checks this.
*/
public boolean keepLoading()
{
return !frame.isStopped();
}
/**
* <p>Goes through all the components and checks to see if they
* should be visible, and updates their contents.</p>
*
* <p>If this containerPanel is attached to an object that is not
* being edited, this method will return without doing anything.</p>
*/
public void updateAll()
{
// View windows can't be updated.
if (!editable)
{
return;
}
if (debug)
{
println("Updating container panel");
}
gc.setWaitCursor();
try
{
for (Component comp: objectHash.keySet())
{
updateComponent(comp);
}
invalidate();
frame.validate();
}
finally
{
if (debug)
{
println("Done updating container panel");
}
gc.setNormalCursor();
}
}
/**
* <p>Goes through all the components and checks to see if any of
* them are Invid fields that contain a reference to invid.</p>
*
* <p>If so, we'll refresh the label for invid.</p>
*/
public void updateInvidLabels(Invid invid, String newLabel)
{
if (debug)
{
println("Updating container panel");
}
gc.setWaitCursor();
try
{
for (Component element: objectHash.keySet())
{
db_field field = objectHash.get(element);
if (field instanceof invid_field)
{
relabelInvidComponent((invid_field)field, element, invid, newLabel);
}
}
}
finally
{
if (debug)
{
println("Done updating container panel");
}
gc.setNormalCursor();
}
}
private void relabelInvidComponent(invid_field field, Component element, Invid invid, String newLabel)
{
if (element instanceof StringSelector)
{
((StringSelector) element).relabelObject(invid, newLabel);
}
else if (element instanceof JInvidChooser)
{
((JInvidChooser) element).relabelObject(invid, newLabel);
}
else if (element instanceof JButton)
{
try
{
if (field.getValue().equals(invid))
{
((JButton) element).setText(newLabel);
}
}
catch (RemoteException ex)
{
gc.processExceptionRethrow(ex);
}
}
}
/**
* Updates a subset of the fields in this containerPanel.
*
* @param fields Vector of Shorts, field ID's
*/
public void update(Vector<Short> fields)
{
if (!editable)
{
return;
}
if (fields == null)
{
return;
}
Component c;
/* -- */
if (debug)
{
println("Updating a few fields...");
}
// If the containerPanel is not loaded, then we need to keep track
// of all the fields that need to be updated, and call update on
// them after the load is finished.
synchronized (updatesWhileLoading)
{
if (!loaded)
{
// If we are not loading yet, then we don't need to worry
// about keeping track of the fields. They will current when
// they are first loaded.
if (loading)
{
for (Short key: fields)
{
updatesWhileLoading.add(key);
}
}
return;
}
}
gc.setWaitCursor();
try
{
for (Short fieldID: fields)
{
if (!keepLoading())
{
return;
}
// if we're not an embedded container panel, check to see if
// we should update either the expiration field or the removal
// field.
if (frame != null)
{
if (fieldID.shortValue() == SchemaConstants.ExpirationField)
{
frame.refresh_expiration_date_panel();
continue;
}
else if (fieldID.shortValue() == SchemaConstants.RemovalField)
{
frame.refresh_removal_date_panel();
continue;
}
}
c = idHash.get(fieldID);
if (c == null)
{
if (debug)
{
println("Could not find this component: ID = " + fieldID);
}
}
else
{
updateComponent(c);
}
}
invalidate();
frame.validate();
if (debug)
{
println("Done updating container panel");
}
}
finally
{
gc.setNormalCursor();
}
}
/**
* Updates the contents and visibility status of
* a component in this containerPanel.
*
* @param comp An AWT/Swing component that we need to refresh
*/
private void updateComponent(Component comp)
{
if (debug)
{
printErr("containerPanel.updateComponent(" + comp + ")");
}
try
{
db_field field = objectHash.get(comp);
// by getting a FieldInfo, we'll save a call to the server by
// not having to repeatedly probe the field for elements of
// its state
FieldInfo currentInfo = field.getFieldInfo();
if (debug)
{
println("Updating " + field.getName() + " " + comp);
}
// if the field is not visible, just hide it and
// return.. otherwise, set it visible and update
// the value and choices for the field
if (!currentInfo.isVisible())
{
contentsPanel.setRowVisible(comp, false);
return;
}
contentsPanel.setRowVisible(comp, true);
if (comp instanceof JstringField)
{
if (comp.equals(currentlyChangingComponent))
{
// the server apparently triggered a refresh of the
// field that we are processing a callback from. the
// JstringField has specific support for this, to
// allow the server to canonicalize strings entered by
// the user. We'll call the appropriate method on the
// JstringField so that it will redraw itself with the
// canonicalized value when our callstack unwinds to
// the callback origination in this JstringField.
((JstringField)comp).substituteValueByCallBack(this, (String)currentInfo.getValue());
}
else
{
((JstringField)comp).setText((String)currentInfo.getValue());
}
}
else if (comp instanceof JstringArea)
{
// JstringArea handles re-entrant refresh okay, no need to
// worry about self-refresh
((JstringArea)comp).setText((String)currentInfo.getValue());
}
else if (comp instanceof JdateField)
{
// JdateField handles re-entrant refresh okay as well
date_field datef = (date_field) field;
((JdateField)comp).setDate((Date)currentInfo.getValue());
if (currentInfo.isEditable())
{
if (datef.limited())
{
((JdateField)comp).setLimits(datef.minDate(), datef.maxDate());
}
else
{
((JdateField)comp).setLimits(null, null);
}
}
}
else if (comp instanceof JnumberField)
{
Integer value = (Integer)currentInfo.getValue();
if (comp.equals(currentlyChangingComponent))
{
// the server apparently triggered a refresh of the field
// that we are processing a callback from. the
// JnumberField has specific support for this, etc.
((JnumberField)comp).substituteValueByCallBack(this, value);
}
else
{
((JnumberField)comp).setValue(value);
}
}
else if (comp instanceof JfloatField)
{
Double value = (Double)currentInfo.getValue();
if (comp.equals(currentlyChangingComponent))
{
// the server apparently triggered a refresh of the field
// that we are processing a callback from. the
// JfloatField has specific support for this, etc.
((JfloatField)comp).substituteValueByCallBack(this, value);
}
else
{
((JfloatField)comp).setValue(value);
}
}
else if (comp instanceof JCheckBox)
{
Boolean value = (Boolean)currentInfo.getValue();
JCheckBox cb = (JCheckBox) comp;
// make sure we don't trigger a callback here
cb.removeActionListener(this);
cb.setSelected((value == null) ? false : value.booleanValue());
}
else if (comp instanceof JComboBox)
{
JComboBox cb = (JComboBox) comp;
string_field sf = (string_field) field;
/* -- */
// remove this as an item listener so we don't get tricked
// into thinking this update came from the user
cb.removeItemListener(this);
if (debug)
{
println("Updating the combo box.");
}
// First we need to rebuild the list of choices
Vector<String> labels = null;
Object key = sf.choicesKey();
// if our choices key is null, we're not going to use a cached copy..
// pull down a new list of choices for this field.
if (key == null)
{
QueryResult qr = sf.choices();
if (qr != null)
{
labels = qr.getLabels();
}
}
else
{
if (debug)
{
println("key = " + key);
}
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("key in there, using cached list");
}
labels = gc.cachedLists.getLabels(key, false);
}
else
{
if (debug)
{
println("JComboBox contents not cached, downloading stringfield choices.");
}
QueryResult choicesV = sf.choices();
// if we got a null result, assume we have no choices,
// otherwise we're going to cache this result
if (choicesV == null)
{
labels = new Vector<String>();
}
else
{
gc.cachedLists.putList(key, choicesV);
labels = choicesV.getLabels();
}
}
}
// reset the combo box.
boolean mustChoose = sf.mustChoose();
String currentValue = (String) sf.getValue();
if (!mustChoose || currentValue == null)
{
// "<none>"
labels.add(ts.l("global.none"));
}
if (currentValue == null)
{
// "<none>"
currentValue = ts.l("global.none");
}
// create a new model to avoid O(n^2) order time hassles when
// we add items one-by-one to an extant JComboBox
cb.setModel(new DefaultComboBoxModel(labels));
cb.setSelectedItem(currentValue);
if (debug)
{
printErr("setting currentvalue in JComboBox to " + currentValue);
}
// put us back on as an item listener so we are live for updates
// from the user again
cb.repaint();
cb.addItemListener(this);
}
else if (comp instanceof JInvidChooser)
{
JInvidChooser chooser = (JInvidChooser) comp;
invid_field invf = (invid_field) field;
// "<none">
listHandle noneHandle = new listHandle(ts.l("global.none"), null);
boolean mustChoose;
/* -- */
// remove this as an item listener so we don't get tricked
// into thinking this update came from the user
chooser.removeItemListener(this);
if (debug)
{
println("Updating the InvidChooser.");
}
// First we need to rebuild the list of choices
Vector<listHandle> choiceHandles = null;
Object key = invf.choicesKey();
// if our choices key is null, we're not going to use a cached copy..
// pull down a new list of choices for this field.
if (key == null)
{
if (debug)
{
println("key is null, getting new copy, not caching.");
}
QueryResult qr = invf.choices();
if (qr == null)
{
choiceHandles = new Vector<listHandle>(); // empty
}
else
{
choiceHandles = qr.getListHandles(); // pre-sorted
}
}
else
{
if (debug)
{
println("key = " + key);
}
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("key in there, using cached list");
}
choiceHandles = gc.cachedLists.getListHandles(key, false); // pre-sorted
}
else
{
if (debug)
{
println("JInvidChooser contents not cached, downloading invid field choices.");
}
QueryResult choicesV = invf.choices();
// if we got a null result, assume we have no choices
// otherwise, we're going to cache this result
if (choicesV == null)
{
choiceHandles = new Vector<listHandle>();
}
else
{
gc.cachedLists.putList(key, choicesV);
choiceHandles = choicesV.getListHandles(); // sorted
}
}
}
// reset the combo box.
Invid currentValue = (Invid) invf.getValue();
String currentLabel = gc.getSession().viewObjectLabel(currentValue);
listHandle currentValueHandle = new listHandle(currentLabel, currentValue);
listHandle currentHandle = null;
if (debug)
{
printErr("containerPanel.updateComponent(): updating invid chooser combo box");
printErr("containerPanel.updateComponent(): searching for value " + currentValue);
}
for (int i = 0; i < choiceHandles.size(); i++)
{
currentHandle = (listHandle) choiceHandles.elementAt(i);
if (currentHandle.getObject().equals(currentValue))
{
break;
}
else
{
currentHandle = null;
}
}
// in many cases, the list of choices may not include the
// current value held in an invid field. In this case,
// we need to synthesize a handle for the current value
if (currentHandle == null && currentValue != null)
{
VecSortInsert inserter = new VecSortInsert(new Comparator()
{
public int compare(Object o_a, Object o_b)
{
listHandle a, b;
a = (listHandle) o_a;
b = (listHandle) o_b;
int compResult = 0;
compResult = a.toString().compareToIgnoreCase(b.toString());
if (compResult < 0)
{
return -1;
}
else if (compResult > 0)
{
return 1;
}
else
{
return 0;
}
}
});
inserter.insert(choiceHandles, currentValueHandle);
currentHandle = currentValueHandle;
}
// now we need to decide whether we should allow the user
// to set this field back to the <none> selection.
mustChoose = invf.mustChoose();
if (!mustChoose || (currentHandle == null))
{
choiceHandles.insertElementAt(noneHandle, 0);
}
if (debug)
{
printErr("containerPanel.updateComponent(): got handles, setting model");
}
// aaaand resort
// choiceHandles = gc.sortListHandleVector(choiceHandles);
if (currentHandle == null)
{
chooser.setVectorContents(choiceHandles, noneHandle);
}
else
{
chooser.setVectorContents(choiceHandles, currentHandle);
}
if (debug)
{
printErr("setting currentvalue in JInvidChooser to " + currentHandle);
}
// put us back on as an item listener so we are live for updates
// from the user again
chooser.repaint();
chooser.addItemListener(this);
}
else if (comp instanceof JLabel)
{
((JLabel)comp).setText((String)currentInfo.getValue());
}
else if (comp instanceof JButton)
{
// This is an invid field, non-editable.
Invid inv = (Invid)currentInfo.getValue();
((JButton)comp).setText(gc.getSession().viewObjectLabel(inv));
}
else if (comp instanceof JpassField)
{
if (debug)
{
println("Passfield, ingnoring");
}
}
else if (comp instanceof StringSelector)
{
if (field instanceof invid_field)
{
updateInvidStringSelector((StringSelector)comp, (invid_field)field);
}
else // must be a string_field
{
updateStringStringSelector((StringSelector)comp, (string_field)field,
currentInfo);
}
// In the case of self-refresh on server command, prevent
// the StringSelector from trying to finish up its
// graphical state changing with the old data.
if (comp.equals(currentlyChangingComponent))
{
((StringSelector) comp).substituteValueByCallBack(this);
}
}
else if (comp instanceof vectorPanel)
{
((vectorPanel)comp).refresh();
}
else if (comp instanceof JIPField)
{
if (debug)
{
println("Updating JIPField.");
}
IPAddress address = (IPAddress) currentInfo.getValue();
if (comp.equals(currentlyChangingComponent))
{
// the server apparently triggered a refresh of the field
// that we are processing a callback from. the
// JIPField has specific support for this, etc.
((JIPField)comp).substituteValueByCallBack(this, address);
}
else
{
((JIPField)comp).setValue(address);
}
}
else
{
printErr("field of unknown type: " + comp);
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
/**
* Updates the contents of a vector {@link arlut.csd.ganymede.rmi.string_field string_field}
* value selector against the current contents of the field on the server.
*
* @param ss The StringSelector GUI component being updated
* @param field The server-side string_field attached to the StringSelector to be updated
* @param currentInfo A download of the string_field's current value
*/
public void updateStringStringSelector(StringSelector ss, string_field field,
FieldInfo currentInfo) throws RemoteException
{
Vector available = null;
Vector chosen = null;
Object key = null;
/* -- */
// If the field is not editable, there will be no available vector
if (ss.isEditable())
{
key = field.choicesKey();
if (key == null)
{
QueryResult qr = field.choices();
if (qr != null)
{
available = qr.getListHandles();
}
}
else
{
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("key in there, using cached list");
}
available = gc.cachedLists.getListHandles(key, false);
}
else
{
if (debug)
{
println("list for updateStringStringSelector() not loaded, downloading a new one.");
}
QueryResult choicesV = field.choices();
// if we got a null result, assume we have no choices
// otherwise, we're going to cache this result
if (choicesV == null)
{
available = new Vector();
}
else
{
gc.cachedLists.putList(key, choicesV);
available = choicesV.getListHandles();
}
}
}
}
// now find the chosen vector
chosen = (Vector) currentInfo.getValue();
ss.update(available, true, null, chosen, false, null);
}
/**
* Updates the contents of a vector {@link arlut.csd.ganymede.rmi.invid_field invid_field}
* value selector against the current contents of the field on the server.
*
* @param ss The StringSelector GUI component being updated
* @param field The server-side invid_field attached to the StringSelector to be updated
*/
public void updateInvidStringSelector(StringSelector ss, invid_field field) throws RemoteException
{
Vector available = null;
Vector chosen = null;
Object key = null;
/* -- */
// Only editable fields have available vectors
if (ss.isEditable())
{
key = field.choicesKey();
if (key == null)
{
QueryResult qr = field.choices();
if (qr != null)
{
available = qr.getListHandles();
}
}
else
{
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("key in there, using cached list");
}
available = gc.cachedLists.getListHandles(key, false);
}
else
{
if (debug)
{
println("list for updateInvidStringSelector() not loaded, downloading a new one.");
}
QueryResult choicesV = field.choices();
// if we got a null result, assume we have no choices
// otherwise, we're going to cache this result
if (choicesV == null)
{
available = new Vector();
}
else
{
gc.cachedLists.putList(key, choicesV);
available = choicesV.getListHandles();
}
}
}
}
QueryResult res = field.encodedValues();
if (res != null)
{
chosen = res.getListHandles();
}
try
{
ss.update(available, true, null, chosen, false, null);
}
catch (Exception e)
{
println("Caught exception updating StringSelector: " + e);
}
}
/**
* <p>This method comprises the JsetValueCallback interface, and is
* how the customized data-carrying components in this
* containerPanel notify us when something changes.</p>
*
* <p>Note that we don't use this method for checkboxes, or
* comboboxes.</p>
*
* @see arlut.csd.JDataComponent.JsetValueCallback
* @see arlut.csd.JDataComponent.JValueObject
*
* @return false if the JDataComponent that is calling us should
* reject the value change operation and revert back to the prior
* value.
*/
public boolean setValuePerformed(JValueObject v)
{
ReturnVal returnValue = null;
FieldTemplate fieldTemplate = null;
/* -- */
if (v instanceof JErrorValueObject)
{
gc.showErrorMessage((String)v.getValue());
return true;
}
currentlyChangingComponent = (JComponent)v.getSource();
try
{
// ok, now we have to connect the field change report coming
// from the JDataComponent to the appropriate field object
// on the Ganymede server. First we'll try the simplest,
// generic case.
if ((v.getSource() instanceof JstringField) ||
(v.getSource() instanceof JnumberField) ||
(v.getSource() instanceof JfloatField) ||
(v.getSource() instanceof JIPField) ||
(v.getSource() instanceof JdateField) ||
(v.getSource() instanceof JstringArea))
{
db_field field = objectHash.get(v.getSource());
/* -- */
try
{
if (debug)
{
println(field.getTypeDesc() + " trying to set to " + v.getValue());
}
returnValue = field.setValue(v.getValue());
}
catch (Exception rx)
{
gc.processException(rx);
return false;
}
}
else if (v.getSource() instanceof JpassField)
{
pass_field field = (pass_field) objectHash.get(v.getSource());
/* -- */
try
{
if (debug)
{
println(field.getTypeDesc() + " trying to set to " + v.getValue());
}
returnValue = field.setPlainTextPass((String)v.getValue());
}
catch (Exception rx)
{
gc.processException(rx);
return false;
}
}
else if (v.getSource() instanceof vectorPanel)
{
// no vectorPanel should really ever call this
// method, so wtf?
if (debug)
{
println("Something happened in the vector panel");
}
}
else if (v.getSource() instanceof StringSelector)
{
StringSelector sourceComponent = (StringSelector) v.getSource();
/* -- */
if (debug)
{
println("value performed from StringSelector");
}
// a StringSelector data component could be feeding us any of a
// number of conditions, that we need to check.
// First, are we being given a menu operation from StringSelector?
if (v instanceof JParameterValueObject)
{
if (debug)
{
println("MenuItem selected in a StringSelector");
}
String command = (String) v.getParameter();
if (command.equals(edit_action))
{
if (debug)
{
println("Edit object: " + v.getValue());
}
Invid invid = (Invid) v.getValue();
gc.editObject(invid);
return true;
}
else if (command.equals(view_action))
{
if (debug)
{
println("View object: " + v.getValue());
}
Invid invid = (Invid) v.getValue();
gc.viewObject(invid);
return true;
}
else
{
println("Unknown action command from popup: " + command);
}
}
else if (objectHash.get(sourceComponent) instanceof invid_field)
{
invid_field field = (invid_field) objectHash.get(sourceComponent);
/* -- */
if (field == null)
{
throw new RuntimeException("Could not find field in objectHash");
}
try
{
if (v instanceof JAddValueObject)
{
if (debug)
{
println("Adding new value to string selector");
}
returnValue = field.addElement(v.getValue());
}
else if (v instanceof JAddVectorValueObject)
{
if (debug)
{
println("Adding new value vector to string selector");
}
returnValue = field.addElements((Vector) v.getValue());
}
else if (v instanceof JDeleteValueObject)
{
if (debug)
{
println("Removing value from field(string selector)");
}
returnValue = field.deleteElement(v.getValue());
}
else if (v instanceof JDeleteVectorValueObject)
{
if (debug)
{
println("Removing value vector from field(string selector)");
}
returnValue = field.deleteElements((Vector) v.getValue());
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx, "Could not change add/delete invid from field");
}
}
else if (objectHash.get(v.getSource()) instanceof string_field)
{
string_field field = (string_field) objectHash.get(v.getSource());
/* -- */
if (field == null)
{
throw new RuntimeException("Could not find field in objectHash");
}
try
{
if (v instanceof JAddValueObject)
{
returnValue = field.addElement(v.getValue());
}
else if (v instanceof JAddVectorValueObject)
{
returnValue = field.addElements((Vector) v.getValue());
}
else if (v instanceof JDeleteValueObject)
{
returnValue = field.deleteElement(v.getValue());
}
else if (v instanceof JDeleteVectorValueObject)
{
returnValue = field.deleteElements((Vector) v.getValue());
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx, "Could not add/remove string from string_field: ");
}
}
else
{
println("Not an Invid in string selector.");
}
}
else
{
println("Value performed from unknown source");
}
// Handle any wizards, error dialogs, or rescan commands
returnValue = gc.handleReturnVal(returnValue);
if (returnValue == null) // Success, no need to do anything else
{
if (debug)
{
println("retVal is null: returning true");
}
gc.somethingChanged();
return true;
}
if (returnValue.didSucceed())
{
if (debug)
{
println("didSucceed: Returning true.");
}
gc.somethingChanged();
return true;
}
else
{
if (debug)
{
println("didSucceed: Returning false.");
}
return false;
}
}
catch (NullPointerException ne)
{
ne.printStackTrace();
return false;
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
return false;
}
catch (RuntimeException e)
{
e.printStackTrace();
return false;
}
finally
{
currentlyChangingComponent = null;
}
}
/**
* Some of our components, most notably the checkboxes, don't
* go through JDataComponent.setValuePerformed(), but instead
* give us direct feedback. Those we take care of here.
*
* @see java.awt.event.ActionListener
*/
public void actionPerformed(ActionEvent e)
{
ReturnVal returnValue = null;
db_field field = null;
FieldTemplate fieldTemplate = null;
boolean newValue;
// we are only acting as an action listener for checkboxes..
// we'll just throw a ClassCastException if this changes
// and we haven't fixed this code to match.
JCheckBox cb = (JCheckBox) e.getSource();
/* -- */
field = objectHash.get(cb);
if (field == null)
{
throw new RuntimeException("Whoa, null field for a JCheckBox: " + e);
}
try
{
newValue = cb.isSelected();
try
{
returnValue = field.setValue(Boolean.valueOf(newValue));
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx, "Could not set field value: ");
}
// Handle any wizards or error dialogs resulting from the
// field.setValue()
returnValue = gc.handleReturnVal(returnValue);
if (returnValue == null)
{
gc.somethingChanged();
}
else if (returnValue.didSucceed())
{
gc.somethingChanged();
}
else
{
// we need to undo things
// We need to turn off ourselves as an action listener
// while we flip this back, so we don't go through this
// method again.
cb.removeActionListener(this);
cb.setSelected(!newValue);
// and we re-enable event notification
cb.addActionListener(this);
}
}
catch (Exception ex)
{
// An exception was thrown, most likely from the server. We need to revert the check box.
printErr("Exception occured in containerPanel.actionPerformed: " + ex);
try
{
Boolean b = (Boolean)field.getValue();
cb.setSelected((b == null) ? false : b.booleanValue());
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
gc.processExceptionRethrow(ex);
}
}
/**
* Some of our components, most notably the JComboBoxes, don't
* go through JDataComponent.setValuePerformed(), but instead
* give us direct feedback. Those we take care of here.
*
* @see java.awt.event.ItemListener
*/
public void itemStateChanged(ItemEvent e)
{
ReturnVal returnValue = null;
// we are only acting as an action listener for comboboxes..
// we'll just throw a ClassCastException if this changes
// and we haven't fixed this code to match.
JComboBox cb = (JComboBox) e.getSource();
/* -- */
// We don't care about deselect reports
if (e.getStateChange() != ItemEvent.SELECTED)
{
return;
}
if (debug)
{
println("containerPanel.itemStateChanged(): Item selected: " + e.getItem());
}
// Find the field that is associated with this combo box. Some
// combo boxes are all by themselves, and they will be in the
// objectHash. Other comboBoxes are part of JInvidChoosers, and
// they will be in the invidChooserHash
db_field field = objectHash.get(cb);
if (field == null)
{
field = invidChooserHash.get(cb);
if (field == null)
{
throw new RuntimeException("Whoa, null field for a JComboBox: " + e);
}
}
try
{
Object newValue = e.getItem();
Object oldValue = field.getValue();
if (newValue.equals(oldValue))
{
return; // what else is new?
}
if (newValue instanceof String)
{
returnValue = field.setValue(newValue);
}
else if (newValue instanceof listHandle)
{
listHandle lh = (listHandle) newValue;
if (debug)
{
if (field == null)
{
println("Field is null.");
}
}
returnValue = field.setValue(lh.getObject());
}
else
{
throw new RuntimeException("Unknown type from JComboBox: " + newValue);
}
// handle any wizards and/or error dialogs
returnValue = gc.handleReturnVal(returnValue);
if (returnValue == null)
{
gc.somethingChanged();
if (debug)
{
println("field setValue returned true");
}
}
else if (returnValue.didSucceed())
{
if (debug)
{
println("field setValue returned true!!");
}
gc.somethingChanged();
}
else
{
// Failure.. need to revert the combobox
// turn off callbacks
cb.removeItemListener(this);
if (oldValue == null)
{
cb.setSelectedItem(null);
}
else if (newValue instanceof String)
{
cb.setSelectedItem(oldValue);
}
else if (newValue instanceof listHandle)
{
listHandle lh = new listHandle(gc.getSession().viewObjectLabel((Invid) oldValue), oldValue);
cb.setSelectedItem(lh);
}
// turn callbacks back on
cb.addItemListener(this);
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
/**
* Helper method to add a component during constructor operation. This
* is the top-level field component adding method.
*/
private void addFieldComponent(db_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
short fieldType;
boolean isVector;
/* -- */
if (!keepLoading())
{
return;
}
if (field == null)
{
throw new IllegalArgumentException("null field");
}
fieldType = fieldTemplate.getType();
isVector = fieldTemplate.isArray();
if (debug)
{
println(" Name: " + fieldTemplate.getName() + " Field type desc: " + fieldType);
}
if (isVector)
{
if (fieldType == FieldType.STRING)
{
addStringVector((string_field) field, fieldInfo, fieldTemplate);
}
else if (fieldType == FieldType.INVID && !fieldTemplate.isEditInPlace())
{
addInvidVector((invid_field) field, fieldInfo, fieldTemplate);
}
else // generic vector
{
addVectorPanel(field, fieldInfo, fieldTemplate);
}
}
else
{
// plain old component
switch (fieldType)
{
case -1:
printErr("**** Could not get field information");
break;
case FieldType.STRING:
addStringField((string_field) field, fieldInfo, fieldTemplate);
break;
case FieldType.PASSWORD:
addPasswordField((pass_field) field, fieldInfo, fieldTemplate);
break;
case FieldType.NUMERIC:
addNumericField(field, fieldInfo, fieldTemplate);
break;
case FieldType.FLOAT:
addFloatField(field, fieldInfo, fieldTemplate);
break;
case FieldType.DATE:
addDateField(field, fieldInfo, fieldTemplate);
break;
case FieldType.BOOLEAN:
addBooleanField(field, fieldInfo, fieldTemplate);
break;
case FieldType.PERMISSIONMATRIX:
addPermissionField(field, fieldInfo, fieldTemplate);
break;
case FieldType.FIELDOPTIONS:
addFieldOptionsField(field, fieldInfo, fieldTemplate);
break;
case FieldType.INVID:
addInvidField((invid_field)field, fieldInfo, fieldTemplate);
break;
case FieldType.IP:
addIPField((ip_field) field, fieldInfo, fieldTemplate);
break;
default:
JLabel label = new JLabel("(Unknown)Field type ID = " + fieldType);
contentsPanel.addRow(fieldTemplate.getName(), label);
}
}
}
/**
* private helper method to instantiate a string vector in this
* container panel
*/
private void addStringVector(string_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
objectList list = null;
/* -- */
if (debug)
{
println("Adding StringSelector, its a vector of strings!");
}
if (field == null)
{
throw new NullPointerException();
}
if (editable && fieldInfo.isEditable())
{
QueryResult qr = null;
if (debug)
{
println("Getting choicesKey()");
}
Object id = field.choicesKey();
if (id == null)
{
if (debug)
{
println("Key is null, Getting choices");
}
qr = field.choices();
if (qr != null)
{
list = new objectList(qr);
}
}
else
{
if (gc.cachedLists.containsList(id))
{
list = gc.cachedLists.getList(id);
}
else
{
if (debug)
{
println("Getting QueryResult now");
}
qr = field.choices();
if (qr != null)
{
gc.cachedLists.putList(id, qr);
list = gc.cachedLists.getList(id);
}
}
}
if (!keepLoading())
{
if (debug)
{
println("Stopping containerPanel in the midst of loading a StringSelector");
}
return;
}
if (list == null)
{
StringSelector ss = new StringSelector(this,
true, // editable
false, // canChoose
false); // mustChoose
ss.setCellWidth(300);
ss.setMinimumRowCount(3);
ss.setMaximumRowCount(8);
ss.update(null, true, null, (Vector) fieldInfo.getValue(), true, null);
registerComponent(ss, field, fieldTemplate);
ss.setCallback(this);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
ss.setToolTipText(comment);
}
associateFieldId(fieldInfo, ss);
contentsPanel.addFillRow(fieldTemplate.getName(), ss);
contentsPanel.setRowVisible(ss, fieldInfo.isVisible());
}
else
{
Vector available = list.getLabels(false);
StringSelector ss = new StringSelector(this,
true, // editable
true, // canChoose
false); // mustChoose
ss.setCellWidth(150);
ss.setMinimumRowCount(3);
ss.setMaximumRowCount(8);
ss.update(available, true, null, (Vector) fieldInfo.getValue(), true, null);
registerComponent(ss, field, fieldTemplate);
ss.setCallback(this);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
ss.setToolTipText(comment);
}
associateFieldId(fieldInfo, ss);
contentsPanel.addFillRow(fieldTemplate.getName(), ss, 2);
contentsPanel.setRowVisible(ss, fieldInfo.isVisible());
}
}
else //not editable, don't need whole list of things
{
StringSelector ss = new StringSelector(this,
false, // not editable
false, // canChoose
false); // mustChoose
ss.setCellWidth(300);
ss.setMinimumRowCount(3);
ss.setMaximumRowCount(8);
ss.update(null, false, null, (Vector) fieldInfo.getValue(), true, null);
registerComponent(ss, field, fieldTemplate);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
ss.setToolTipText(comment);
}
associateFieldId(fieldInfo, ss);
contentsPanel.addFillRow(fieldTemplate.getName(), ss, 1);
contentsPanel.setRowVisible(ss, fieldInfo.isVisible());
}
}
/**
* private helper method to instantiate an invid vector in this
* container panel
*/
private void addInvidVector(invid_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
Vector
valueHandles = null,
choiceHandles = null;
objectList
list = null;
/* -- */
if (debug)
{
println("Adding StringSelector, it's a vector of invids!");
}
QueryResult qres = field.encodedValues();
if (qres != null)
{
valueHandles = qres.getListHandles();
}
if (!keepLoading())
{
if (debug)
{
println("Stopping containerPanel in the midst of loading a StringSelector");
}
return;
}
if (editable && fieldInfo.isEditable())
{
Object key = field.choicesKey();
if (key == null)
{
if (debug)
{
println("key is null, downloading new copy");
}
QueryResult choices = field.choices();
if (choices != null)
{
// the server may not automatically restrict inactive
// objects from the list, so we want to exclude inactive
// objects. We don't want to exclude non-editable objects,
// however, because the server may include nominally
// non-editable objects that we are granted permission
// to link to by the DBEditObject.anonymousLinkOK()
// method.
choiceHandles = choices.getListHandles(false, true);
}
else
{
if (debug)
{
println("choices is null");
}
choiceHandles = null;
}
}
else
{
if (debug)
{
println("key= " + key);
}
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("It's in there, using cached list");
}
// when we are drawing a list of choices from the cache,
// we know that the server didn't filter that list for
// us so that the 'non-editables' are actually valid
// choices in this context, so we don't want either
// inactive nor non-editable choices.
choiceHandles = gc.cachedLists.getListHandles(key, false, false);
}
else
{
if (debug)
{
println("Choice list for addInvidVector not cached, downloading choices.");
}
QueryResult qr = field.choices();
if (qr == null)
{
choiceHandles = null;
}
else
{
gc.cachedLists.putList(key, qr);
list = gc.cachedLists.getList(key);
// when the server gives us a choice key, we know
// that the server didn't filter that list for us
// so that the 'non-editables' are actually valid
// choices in this context, so we don't want
// either inactive nor non-editable choices.
choiceHandles = list.getListHandles(false, false);
}
// debuging stuff
if (debug_persona)
{
System.out.println();
for (int i = 0; i < choiceHandles.size(); i++)
{
println(" choices: " + (listHandle)choiceHandles.elementAt(i));
}
System.out.println();
}
}
}
}
else
{
if (debug)
{
println("Not editable, not downloading choices");
}
}
// ss is canChoose, mustChoose
JPopupMenu invidTablePopup = new JPopupMenu();
JMenuItem viewO = new JMenuItem(view_action);
JMenuItem editO = new JMenuItem(edit_action);
invidTablePopup.add(viewO);
invidTablePopup.add(editO);
JPopupMenu invidTablePopup2 = new JPopupMenu();
JMenuItem viewO2 = new JMenuItem(view_action);
JMenuItem editO2 = new JMenuItem(edit_action);
invidTablePopup2.add(viewO2);
invidTablePopup2.add(editO2);
if (debug)
{
println("Creating StringSelector");
}
StringSelector ss = new StringSelector(this,
editable && fieldInfo.isEditable(),
true, true);
ss.setMinimumRowCount(3);
ss.setMaximumRowCount(8);
ss.setCellWidth(editable && fieldInfo.isEditable() ? 150: 300);
ss.update(choiceHandles, true, null, valueHandles, true, null);
ss.setPopups(invidTablePopup, invidTablePopup2);
registerComponent(ss, field, fieldTemplate);
ss.setCallback(this);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
ss.setToolTipText(comment);
}
associateFieldId(fieldInfo, ss);
contentsPanel.addFillRow(fieldTemplate.getName(), ss, choiceHandles == null ? 1 : 2);
contentsPanel.setRowVisible(ss, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a vector panel in this
* container panel
*/
private void addVectorPanel(db_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
boolean isEditInPlace = fieldTemplate.isEditInPlace();
/* -- */
if (debug)
{
if (isEditInPlace)
{
println("Adding editInPlace vector panel");
}
else
{
println("Adding normal vector panel");
}
}
vectorPanel vp = new vectorPanel(field, fieldTemplate, winP, editable && fieldInfo.isEditable(),
isEditInPlace, this, isCreating);
vectorPanelList.add(vp);
registerComponent(vp, field, fieldTemplate);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
vp.setToolTipText(comment);
}
associateFieldId(fieldInfo, vp);
contentsPanel.addFillRow(fieldTemplate.getName(), vp, 3);
contentsPanel.setRowVisible(vp, fieldInfo.isVisible());
}
/**
* If we contain any {@link arlut.csd.ganymede.client.vectorPanel vectorPanel}s,
* they will call this method during loading to let us update our progress bar if
* we have it still up. This is used to let us include the time it will take
* to get vector panels loaded in the progress bar time estimate.
*/
public void vectorElementAdded()
{
if (progressBar != null)
{
progressBar.setValue(progressBar.getValue() + 1);
}
++vectorElementsAdded;
}
/**
* private helper method to instantiate a scalar string field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addStringField(string_field field, FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
objectList
list;
JstringField
sf;
boolean
mustChoose;
/* -- */
if (this.editable && fieldInfo.isEditable() && field.canChoose())
{
if (debug)
{
println("You can choose");
}
Vector choices = null;
Object key = field.choicesKey();
if (key == null)
{
if (debug)
{
println("key is null, getting new copy.");
}
choices = field.choices().getLabels();
}
else
{
if (debug)
{
println("key = " + key);
}
if (gc.cachedLists.containsList(key))
{
if (debug)
{
println("key in there, using cached list");
}
list = gc.cachedLists.getList(key);
}
else
{
if (debug)
{
println("Choice list for addStringField not cached, downloading a new one.");
}
gc.cachedLists.putList(key, field.choices());
list = gc.cachedLists.getList(key);
}
choices = list.getLabels(false);
}
String currentChoice = (String) fieldInfo.getValue();
final JComboBox combo = new JComboBox(choices);
combo.setKeySelectionManager(new TimedKeySelectionManager());
combo.setMaximumRowCount(8);
combo.setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
try
{
mustChoose = field.mustChoose();
}
catch (Exception rx)
{
gc.processException(rx);
throw new RuntimeException(rx);
}
combo.setEditable(!mustChoose);
if (currentChoice == null)
{
if (debug)
{
println("Setting current value to <none>, because the current choice is null. " + currentChoice);
}
combo.addItem("<none>");
combo.setSelectedItem("<none>");
}
else
{
if (debug)
{
println("Setting current value: " + currentChoice);
}
try
{
combo.setSelectedItem(currentChoice);
}
catch (IllegalArgumentException e)
{
println("IllegalArgumentException: current choice is not in the string selection combobox. Adding it now.");
combo.addItem(currentChoice);
combo.setSelectedItem(currentChoice);
}
}
if (editable && fieldInfo.isEditable())
{
combo.addItemListener(this); // register callback
}
combo.addFocusListener(new FocusListener()
{
public void focusLost(FocusEvent e)
{
}
public void focusGained(FocusEvent e)
{
scrollRectToVisible(combo.getBounds());
}
});
Component editor = combo.getEditor().getEditorComponent();
editor.addFocusListener(new FocusListener()
{
public void focusLost(FocusEvent e)
{
}
public void focusGained(FocusEvent e)
{
scrollRectToVisible(combo.getBounds());
}
});
registerComponent(combo, field, fieldTemplate);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
combo.setToolTipText(comment);
}
associateFieldId(fieldInfo, combo);
contentsPanel.addFillRow(fieldTemplate.getName(), combo, 1);
contentsPanel.setRowVisible(combo, fieldInfo.isVisible());
}
else if (fieldTemplate.isMultiLine())
{
JstringArea sa = new JstringArea(6, FIELDWIDTH);
registerComponent(sa, field, fieldTemplate);
sa.setAllowedChars(fieldTemplate.getOKChars());
sa.setDisallowedChars(fieldTemplate.getBadChars());
sa.setText((String)fieldInfo.getValue());
if (editable && fieldInfo.isEditable())
{
sa.setCallback(this);
}
else
{
// make sure we show the start of the non-editable string
sa.setCaretPosition(0);
}
sa.setEditable(editable && fieldInfo.isEditable());
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
sa.setToolTipText(comment);
}
associateFieldId(fieldInfo, sa);
contentsPanel.addFillRow(fieldTemplate.getName(), sa, 1);
contentsPanel.setRowVisible(sa, fieldInfo.isVisible());
}
else
{
// It's not a choice
int maxLength = fieldTemplate.getMaxLength();
sf = new JstringField(FIELDWIDTH > maxLength ? maxLength + 1 : FIELDWIDTH,
maxLength,
editable && fieldInfo.isEditable(),
false,
fieldTemplate.getOKChars(),
fieldTemplate.getBadChars(),
this);
registerComponent(sf, field, fieldTemplate);
sf.setText((String)fieldInfo.getValue());
if (editable && fieldInfo.isEditable())
{
sf.setCallback(this);
}
sf.setEditable(editable && fieldInfo.isEditable());
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
sf.setToolTipText(comment);
}
associateFieldId(fieldInfo, sf);
contentsPanel.addFillRow(fieldTemplate.getName(), sf, 1);
contentsPanel.setRowVisible(sf, fieldInfo.isVisible());
}
}
/**
* <p>Private helper method to instantiate a password field in this
* container panel.</p>
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addPasswordField(pass_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
JstringField sf;
/* -- */
if (editable && fieldInfo.isEditable())
{
JpassField pf = new JpassField(gc, 10, 8, editable && fieldInfo.isEditable());
registerComponent(pf, field, fieldTemplate);
if (editable && fieldInfo.isEditable())
{
pf.setCallback(this);
}
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
pf.setToolTipText(comment);
}
associateFieldId(fieldInfo, pf);
contentsPanel.addRow(fieldTemplate.getName(), pf);
contentsPanel.setRowVisible(pf, fieldInfo.isVisible());
}
else
{
int maxLength = fieldTemplate.getMaxLength();
sf = new JstringField(FIELDWIDTH > maxLength ? maxLength + 1 : FIELDWIDTH,
maxLength,
true,
false,
null,
null);
registerComponent(sf, field, fieldTemplate);
// the server won't give us an unencrypted password, we're clear here
sf.setText((String)fieldInfo.getValue());
sf.setEditable(false);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
sf.setToolTipText(comment);
}
associateFieldId(fieldInfo, sf);
contentsPanel.addFillRow(fieldTemplate.getName(), sf, 1);
contentsPanel.setRowVisible(sf, fieldInfo.isVisible());
}
}
/**
* private helper method to instantiate a numeric field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addNumericField(db_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
if (debug)
{
println("Adding numeric field");
}
JnumberField nf = new JnumberField();
registerComponent(nf, field, fieldTemplate);
Integer value = (Integer)fieldInfo.getValue();
if (value != null)
{
nf.setValue(value.intValue());
}
if (editable && fieldInfo.isEditable())
{
nf.setCallback(this);
}
nf.setEditable(editable && fieldInfo.isEditable());
nf.setColumns(FIELDWIDTH);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
nf.setToolTipText(comment);
}
associateFieldId(fieldInfo, nf);
contentsPanel.addFillRow(fieldTemplate.getName(), nf, 1);
contentsPanel.setRowVisible(nf, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a numeric field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addFloatField(db_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
if (debug)
{
println("Adding float field");
}
JfloatField nf = new JfloatField();
registerComponent(nf, field, fieldTemplate);
Double value = (Double)fieldInfo.getValue();
if (value != null)
{
nf.setValue(value.doubleValue());
}
if (editable && fieldInfo.isEditable())
{
nf.setCallback(this);
}
nf.setEditable(editable && fieldInfo.isEditable());
nf.setColumns(FIELDWIDTH);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
nf.setToolTipText(comment);
}
associateFieldId(fieldInfo, nf);
contentsPanel.addFillRow(fieldTemplate.getName(), nf, 1);
contentsPanel.setRowVisible(nf, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a date field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addDateField(db_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
boolean enabled = editable && fieldInfo.isEditable();
Date date = (Date) fieldInfo.getValue();
JsetValueCallback callback = null;
if (enabled)
{
callback = this;
}
date_field datef = (date_field) field;
JdateField df = null;
if (fieldInfo.isEditable())
{
df = new JdateField(date, enabled, datef.limited(), true, datef.minDate(), datef.maxDate(), callback);
}
else
{
df = new JdateField(date, enabled, false, true, null, null, callback);
}
registerComponent(df, field, fieldTemplate);
if (debug)
{
println("Editable: " + editable + " isEditable: " + fieldInfo.isEditable());
}
associateFieldId(fieldInfo, df);
contentsPanel.addRow(fieldTemplate.getName(), df);
contentsPanel.setRowVisible(df, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a boolean field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addBooleanField(db_field field, FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
final JCheckBox cb = new JCheckBox();
/* -- */
cb.addFocusListener(new FocusListener()
{
public void focusLost(FocusEvent e)
{
}
public void focusGained(FocusEvent e)
{
scrollRectToVisible(cb.getBounds());
}
});
registerComponent(cb, field, fieldTemplate);
cb.setEnabled(editable && fieldInfo.isEditable());
if (editable && fieldInfo.isEditable())
{
cb.addActionListener(this); // register callback
}
try
{
cb.setSelected(((Boolean)fieldInfo.getValue()).booleanValue());
}
catch (NullPointerException ex)
{
if (debug)
{
println("Null pointer setting selected choice: " + ex);
}
}
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
cb.setToolTipText(comment);
}
associateFieldId(fieldInfo, cb);
contentsPanel.addRow(fieldTemplate.getName(), cb);
contentsPanel.setRowVisible(cb, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a field options field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addFieldOptionsField(db_field field, FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
if (debug)
{
println("Adding field options matrix");
}
// note that the field options editor does its own callbacks to
// the server, albeit using our transaction / session.
fieldoption_button fob = new fieldoption_button((field_option_field) field,
this.object,
editable && fieldInfo.isEditable(),
gc,
fieldTemplate.getName());
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
fob.setToolTipText(comment);
}
associateFieldId(fieldInfo, fob);
contentsPanel.addRow(fieldTemplate.getName(), fob);
contentsPanel.setRowVisible(fob, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a permission matrix field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addPermissionField(db_field field, FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
if (debug)
{
println("Adding perm matrix");
}
// note that the permissions editor does its own callbacks to
// the server, albeit using our transaction / session.
perm_button pb = new perm_button((perm_field) field,
editable && fieldInfo.isEditable(),
gc,
false,
fieldTemplate.getName());
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
pb.setToolTipText(comment);
}
associateFieldId(fieldInfo, pb);
contentsPanel.addRow(fieldTemplate.getName(), pb);
contentsPanel.setRowVisible(pb, fieldInfo.isVisible());
}
/**
* private helper method to instantiate a scalar invid field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addInvidField(invid_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
objectList list;
/* -- */
if (debug)
{
println("addInvidField(" + fieldTemplate.getName() + ")");
}
if (fieldTemplate.isEditInPlace())
{
// this should never happen
if (debug)
{
println("Hey, " + fieldTemplate.getName() +
" is edit in place but not a vector, what gives?");
}
JLabel errorLabel = new JLabel("edit in place non-vector");
contentsPanel.addFillRow(fieldTemplate.getName(), errorLabel);
contentsPanel.setRowVisible(errorLabel, fieldInfo.isVisible());
return;
}
if (!editable || !fieldInfo.isEditable())
{
if (fieldInfo.getValue() == null)
{
// "Null Pointer"
JLabel errorLabel = new JLabel(ts.l("addInvidField.null_invid"));
contentsPanel.addFillRow(fieldTemplate.getName(), errorLabel);
contentsPanel.setRowVisible(errorLabel, fieldInfo.isVisible());
return;
}
final Invid thisInvid = (Invid) fieldInfo.getValue();
String label = (String) gc.getSession().viewObjectLabel(thisInvid);
if (label == null)
{
if (debug)
{
println("-you don't have permission to view this object.");
}
// "Can''t Show Target"
label = ts.l("addInvidField.no_view_perm");
}
JButton b = new JButton(label);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
getgclient().viewObject(thisInvid);
}
});
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
b.setToolTipText(comment);
}
// add the button to our objectHash so that we can change
// the label if the user edits the label field of the
// object we point to.
registerComponent(b, field, fieldTemplate);
contentsPanel.addRow(fieldTemplate.getName(), b);
contentsPanel.setRowVisible(b, fieldInfo.isVisible());
return;
}
if (!keepLoading())
{
return; // we were told to cancel
}
// okay, we've got an editable field, we need to construct and install
// a JInvidChooser.
// get the list of choices for this invid field
Object key = field.choicesKey();
if (key != null)
{
if (!gc.cachedLists.containsList(key))
{
gc.cachedLists.putList(key, field.choices());
}
list = gc.cachedLists.getList(key);
}
else
{
list = new objectList(field.choices());
}
// we have to include non-editables, because the server will
// include some that are non-editable, but for which
// DBEditObject.anonymousLinkOK() nonetheless give us rights
// to link.
Vector<listHandle> choices = list.getListHandles(false, true);
Invid currentChoice = (Invid) fieldInfo.getValue();
String currentChoiceLabel = null;
if (debug)
{
currentChoiceLabel = gc.getSession().viewObjectLabel(currentChoice);
println("Current choice is : " + currentChoice + ", " + currentChoiceLabel);
}
listHandle currentListHandle = null;
listHandle noneHandle = new listHandle(ts.l("global.none"), null); // "<none>"
boolean found = false;
JInvidChooser combo;
boolean mustChoose = false;
try
{
mustChoose = field.mustChoose();
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
// Find currentListHandle
// Make sure the current choice is in the chooser, if there is
// a current choice.
if (currentChoice != null)
{
for (listHandle thisChoice: choices)
{
if (thisChoice.getObject() == null)
{
println("Current object " + thisChoice + " is null.");
}
if (currentChoice.equals(thisChoice.getObject()))
{
if (debug)
{
println("Found the current object in the list!");
}
currentListHandle = thisChoice;
found = true;
break;
}
}
if (!found)
{
currentListHandle = new listHandle(gc.getSession().viewObjectLabel(currentChoice), currentChoice);
choices.add(currentListHandle);
}
}
if (!mustChoose || (currentChoice == null))
{
if (debug)
{
println("inserting null handle");
}
choices.insertElementAt(noneHandle, 0);
}
if (debug)
{
println("creating JInvidChooser");
}
combo = new JInvidChooser(choices, this, fieldTemplate.getTargetBase());
combo.setMaximumRowCount(12);
combo.setMaximumSize(new Dimension(Integer.MAX_VALUE, 20));
combo.setVisible(true);
if (currentChoice != null)
{
if (debug)
{
println("setting current choice: " + currentChoiceLabel);
}
try
{
combo.setSelectedItem(currentListHandle);
}
catch (IllegalArgumentException e)
{
println("IllegalArgumentException: current handle not in the list, adding it now.");
combo.addItem(currentListHandle);
combo.setSelectedItem(currentListHandle);
}
}
else
{
if (debug)
{
println("currentChoice is null");
}
combo.setSelectedItem(noneHandle);
}
if (editable && fieldInfo.isEditable())
{
combo.addItemListener(this); // register callback
}
combo.setAllowNone(!mustChoose);
// We get the itemStateChanged straight from the JComboBox in the
// JInvidChooser, so we need to save an association between the
// combobox and the field
invidChooserHash.put(combo.getCombo(), field);
// The update method still need to be able to find the field from
// the JInvidChooser, so we save it in objectHash, too.
registerComponent(combo, field, fieldTemplate);
if (debug)
{
println("Adding to panel");
}
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
combo.setToolTipText(comment);
}
associateFieldId(fieldInfo, combo);
contentsPanel.addRow(fieldTemplate.getName(), combo);
contentsPanel.setRowVisible(combo, fieldInfo.isVisible());
}
/**
* private helper method to instantiate an ip address field in this
* container panel
*
* @param field Remote reference to database field to be associated with a gui component
* @param fieldInfo Downloaded value and status information for this field
* @param fieldTemplate Downloaded static field type information for this field
*/
private void addIPField(ip_field field,
FieldInfo fieldInfo,
FieldTemplate fieldTemplate) throws RemoteException
{
JIPField
ipf;
IPAddress address;
/* -- */
if (debug)
{
println("Adding IP field");
}
try
{
ipf = new JIPField(editable && fieldInfo.isEditable(),
(editable && fieldInfo.isEditable()) ? field.v6Allowed() : field.isIPV6());
}
catch (Exception rx)
{
gc.processException(rx, "Could not determine if v6 Allowed for ip field: ");
throw new RuntimeException(rx);
}
registerComponent(ipf, field, fieldTemplate);
address = (IPAddress) fieldInfo.getValue();
if (address != null)
{
ipf.setValue(address);
}
ipf.setCallback(this);
String comment = fieldTemplate.getComment();
if (comment != null && !comment.equals(""))
{
ipf.setToolTipText(comment);
}
associateFieldId(fieldInfo, ipf);
contentsPanel.addFillRow(fieldTemplate.getName(), ipf, 1);
contentsPanel.setRowVisible(ipf, fieldInfo.isVisible());
}
private void associateFieldId(FieldInfo fieldInfo, Component comp)
{
idHash.put(fieldInfo.getIDObj(), comp);
}
/**
* <p>This method provides a handy way to null out data structures
* held in relationship to this containerPanel, particularly network
* reference resources.</p>
*
* <p>It is essential that this method be called from the client's
* GUI thread.</p>
*/
public synchronized final void cleanup()
{
if (debug)
{
printErr("containerPanel cleanUp()");
}
contentsPanel.cleanup();
gc = null;
invid = null;
winP = null;
frame = null;
if (updatesWhileLoading != null)
{
updatesWhileLoading.setSize(0);
updatesWhileLoading = null;
}
if (vectorPanelList != null)
{
vectorPanelList.setSize(0);
vectorPanelList = null;
}
currentlyChangingComponent = null;
/**
* The critical ones.. this will release our references to fields
* on the server
*/
object = null;
if (objectHash != null)
{
objectHash.clear();
objectHash = null;
}
if (objectTemplateHash != null)
{
objectTemplateHash.clear();
objectTemplateHash = null;
}
if (invidChooserHash != null)
{
invidChooserHash.clear();
invidChooserHash = null;
}
if (infoVector != null)
{
infoVector.setSize(0);
infoVector = null;
}
if (templates != null)
{
// it is critical that we don't setSize(0) on templates,
// as it is belongs to Loader. we can drop our reference
// to it, though.
templates = null;
}
progressBar = null;
}
/**
* This private helper method records associations between the
* guiComponent and the db_field and field id connected to that gui
* component, so that we can rapidly look up the information as
* needed during runtime.
*/
private void registerComponent(JComponent guiComponent, db_field field, FieldTemplate fieldTemplate)
{
objectHash.put(guiComponent, field);
objectTemplateHash.put(guiComponent, fieldTemplate);
}
}