/*
GASH 2
vectorPanel.java
This module provides for a generic vector of objects, and can be
used to implement a collection of date fields, i.p. addresses,
or edit in place (composite) objects.
Created: 17 Oct 1996
Module By: Navin Manohar, Mike Mulvaney, Jonathan Abbey
-----------------------------------------------------------------------
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
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.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import arlut.csd.JDataComponent.JIPField;
import arlut.csd.JDataComponent.JErrorValueObject;
import arlut.csd.JDataComponent.JValueObject;
import arlut.csd.JDataComponent.JsetValueCallback;
import arlut.csd.ganymede.common.FieldInfo;
import arlut.csd.ganymede.common.FieldTemplate;
import arlut.csd.ganymede.common.Invid;
import arlut.csd.ganymede.common.IPAddress;
import arlut.csd.ganymede.common.ReturnVal;
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.Util.TranslationService;
/*------------------------------------------------------------------------------
class
vectorPanel
------------------------------------------------------------------------------*/
/**
* <p>A GUI vector panel that is designed to be placed inside a {@link
* arlut.csd.ganymede.client.containerPanel containerPanel} within the
* client. This vector panel is intended to support vectors of
* discrete GUI components that are mapped to elements in a
* server-side vector {@link arlut.csd.ganymede.rmi.db_field
* db_field}, but right now it only supports IP Address fields and
* edit-in-place containerPanels. Most of the code in this class is
* dedicated to handling vectors of embedded containerPanels.</p>
*
* <p>Whatever sort of GUI components/database elements this
* vectorPanel is managing, all GUI components will be wrapped by
* {@link arlut.csd.ganymede.client.elementWrapper elementWrapper}'s,
* which are responsible for hiding and revealing the GUI component on
* the user's command.</p>
*
* <p>vectorPanel handles communications with the server to manage the
* server-side db_field.</p>
*
* @see arlut.csd.ganymede.client.elementWrapper
* @see containerPanel
* @see arlut.csd.ganymede.rmi.invid_field
* @see arlut.csd.ganymede.rmi.ip_field
*
* @author Navin Manohar, Mike Mulvaney, and Jonathan Abbey
*/
public class vectorPanel extends JPanel implements JsetValueCallback, ActionListener, MouseListener, FocusListener, Runnable {
/**
* TranslationService object for handling string localization in the
* Ganymede client.
*/
static final TranslationService ts = TranslationService.getTranslationService("arlut.csd.ganymede.client.vectorPanel");
boolean debug = false;
/**
* Vector of GUI components held in this vectorPanel. This vector contains
* the actual GUI components added to this vector, not the elementWrappers.
*/
Vector<Component>
compVector;
/**
* Boolean to track editability status of the vector field we are connected
* to. This member is a Boolean instead of a boolean so that we can distinguish
* between false and unknown (null).
*/
private Boolean
myFieldIsEditable = null;
/**
* The name of the invid field we are managing
*/
String name = null;
/**
* Hash mapping GUI components added to this vectorPanel to the
* elementWrappers holding them. Used to update the elementWrappers'
* titles in response to changes in the fields connected to the
* components.
*/
Hashtable<Component, elementWrapper>
ewHash;
/**
* Button used to add a new element to the vector
*/
final JButton
addB;
/**
* centerPanel holds all of the elementWrappers in a BoxLayout
*/
JPanel
centerPanel;
boolean
editable,
isEditInPlace,
centerPanelAdded = false;
/**
* Reference to server-side vector {@link arlut.csd.ganymede.rmi.invid_field invid_field} or
* {@link arlut.csd.ganymede.rmi.ip_field ip_field}. This vectorPanel will communicate
* with the Ganymede server using this reference to make changes to the vector field.
*/
private db_field my_field;
/**
* Reference to the desktop pane containing the client's internal windows. Used to access
* some GUI resources and to provide to new containerPanels created for embedded objects.
*/
protected windowPanel
wp;
/**
* Reference to the containerPanel that we are embedded in.
*/
containerPanel
container;
/**
* Reference to the client's main class.
*/
gclient
gc;
/**
* Pop-up menu appears when you right-click anywhere inside the panel
*/
JPopupMenu
popupMenu;
JMenuItem
closeLevelMI,
expandLevelMI,
closeAllMI,
expandAllMI;
boolean
isCreating;
FieldTemplate
template;
/* -- */
/**
* Constructor
*
* @param field db_field for this vectorPanel
* @param parent windowPanel above this vectorPanel
* @param editable True if field is editable
* @param isEditInPlace True if object is editInPlace. If this is false, it will make a vector of IPFields.
* @param container containerPanel this vectorPanel is in
*
*/
public vectorPanel(db_field field, FieldTemplate template, windowPanel parent,
boolean editable, boolean isEditInPlace,
containerPanel container, boolean isCreating)
{
this.my_field = field;
this.template = template;
this.editable = editable;
this.isEditInPlace = isEditInPlace;
this.wp = parent;
this.container = container;
this.isCreating = isCreating;
this.gc = container.gc;
if (!debug)
{
debug = gc.debug;
}
if (debug)
{
System.out.println("Adding new vectorPanel");
}
centerPanel = new JPanel(false);
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
setLayout(new BorderLayout());
// "Add {0}" button.
name = template.getName();
addB = new JButton(ts.l("init.add_button", name));
addB.addFocusListener(this);
// Set up pop up menu
//
// The popup menu appears when the right mouse button is clicked inside the
// TitledBorder but outside of the elementWrappers. It basically presents a
// menu with either "expand" or "close" options.
// NOTE: This does not work in windows or certain version, works on Jon's but not James's computer!!!
popupMenu = new JPopupMenu();
// "Expand this level"
expandLevelMI = new JMenuItem(ts.l("init.expand_menu"));
expandLevelMI.setActionCommand("Expand this level");
expandLevelMI.addActionListener(this);
// "Expand all elements"
expandAllMI = new JMenuItem(ts.l("init.expand_all_menu"));
expandAllMI.addActionListener(this);
expandAllMI.setActionCommand("Expand all elements");
popupMenu.add(expandLevelMI);
popupMenu.add(expandAllMI);
popupMenu.addSeparator();
// "Close this level"
closeLevelMI = new JMenuItem(ts.l("init.close_menu"));
closeLevelMI.setActionCommand("Close this level");
closeLevelMI.addActionListener(this);
// "Close all elements"
closeAllMI = new JMenuItem(ts.l("init.close_all_menu"));
closeAllMI.addActionListener(this);
closeAllMI.setActionCommand("Close all elements");
popupMenu.add(closeLevelMI);
popupMenu.add(closeAllMI);
addMouseListener(this);
compVector = new Vector<Component>();
ewHash = new Hashtable<Component, elementWrapper>();
createVectorComponents();
}
private void showPopupMenu(int x, int y)
{
popupMenu.show(this, x, y);
}
/**
* Talks to the server and populates this vectorPanel with
* elements corresponding to values held in my_field. Only called
* on vectorPanel construction.
*/
private void createVectorComponents()
{
if (my_field instanceof ip_field)
{
if (debug)
{
System.out.println("Adding ip vector field");
}
try
{
ip_field ipfield = (ip_field) my_field;
int size = ipfield.size();
for (int i=0; (i < size) && container.keepLoading();i++)
{
JIPField ipf = new JIPField(editable,
ipfield.v6Allowed());
ipf.setValue((IPAddress) ipfield.getElement(i));
ipf.setCallback(this);
addElement(ipf, false);
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
else if (my_field instanceof invid_field)
{
if (debug)
{
System.out.println("Adding vector invid_field");
}
try
{
invid_field invidfield = (invid_field) my_field;
FieldInfo info = invidfield.getFieldInfo();
if (!(info.getValue() instanceof Vector))
{
throw new RuntimeException("Error, vectorPanel passed scalar invid_field");
}
Vector values = (Vector) info.getValue();
if (!isEditInPlace)
{
// The vectorPanel only handles edit-in-place fields.
// If it is not edit-in-place, then the field should
// just get a StringSelector.
throw new RuntimeException("Don't give me(the vectorPanel!) non edit-in-place invid_fields.");
}
if (debug)
{
System.out.println("Adding edit in place invid vector, size = " + invidfield.size());
}
int size = values.size();
for (int i=0; (i < size) && container.keepLoading(); i++)
{
if (debug)
{
System.out.println("Adding Invid to edit in place vector panel");
}
Invid inv = (Invid)(values.elementAt(i));
// We need to get a server-side db_object reference to pass to the
// containerPanel.
db_object object = null;
if (editable)
{
ReturnVal rv = gc.handleReturnVal(gc.getSession().edit_db_object(inv));
object = (db_object) rv.getObject();
}
else
{
ReturnVal rv = gc.handleReturnVal(gc.getSession().view_db_object(inv));
object = (db_object) rv.getObject();
}
// create a containerPanel, but don't load it yet.. the elementWrapper will
// load the containerPanel if the user actually opens it.
containerPanel cp = new containerPanel(object,
inv,
editable,
gc,
wp, container.frame,
null, false, null);
cp.setBorder(wp.lineEmptyBorder);
addElement(object.getEmbeddedObjectDisplayLabel(), cp, false, false);
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
else if (debug)
{
System.out.println("\n*** Error - inappropriate field type passed to vectorPanel constructor");
}
if (editable)
{
if (debug)
{
System.out.println("Adding add button");
}
JPanel addPanel = new JPanel();
addPanel.setLayout(new BorderLayout());
addB.addActionListener(this);
addPanel.add("East", addB);
add("South", addPanel);
}
else if (debug)
{
System.out.println("Field is not editable, no button added");
}
}
/**
* <p>Creates a new element in the vector {@link
* arlut.csd.ganymede.rmi.db_field db_field} this vectorPanel is
* connected to. Creates and adds the GUI component for the new
* element to the vectorPanel.</p>
*
* <p>This is called when the add button is clicked, but there is no
* reason why it couldn't be called from other places if you wanted
* to add a new element.</p>
*/
public void addNewElement()
{
ReturnVal retVal = null;
Invid invid = null;
boolean local_debug = false;
/* -- */
if (debug)
{
System.out.println("Adding new element");
}
if (my_field instanceof invid_field)
{
if (debug)
{
System.out.println("Adding new edit in place element");
}
try
{
retVal = ((invid_field)my_field).createNewEmbedded();
gc.handleReturnVal(retVal);
if (retVal != null && retVal.didSucceed())
{
if (local_debug)
{
System.err.println("XX** Hey.. got a result from the server to create a new embedded element");
}
invid = retVal.getInvid();
if (local_debug)
{
System.err.println("XX** Invid for new embedded object is " + invid);
}
db_object object = retVal.getObject();
// create and load the new containerPanel. Note that we are going to
// immediately show the new element open to the user, so we go ahead
// and use one of the default-valued containerPanel constructors that
// will immediately load the new containerPanel.
containerPanel cp = new containerPanel(object, invid,
isFieldEditable() && editable,
gc,
wp,
container.frame,
null);
if (local_debug)
{
System.err.println("XX** Created container panel");
}
if (local_debug)
{
System.err.println("XX** Recorded containerpanel in our client");
}
cp.setBorder(wp.lineEmptyBorder);
if (local_debug)
{
System.err.println("XX** Set border");
}
// display the new containerPanel pre-expanded
// "New Element"
addElement(ts.l("addNewElement.new_element"), cp, true);
if (local_debug)
{
System.err.println("XX** Added element");
}
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
else if (my_field instanceof ip_field)
{
if (debug)
{
System.out.println("Adding new ip vector field");
}
ip_field ipfield = (ip_field) my_field;
try
{
JIPField ipf = new JIPField(true,
ipfield.v6Allowed());
ipf.setCallback(this);
addElement(ipf);
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
else if (debug)
{
System.out.println("vectorPanel.addNewElement(): This type is not supported yet.");
}
gc.somethingChanged();
invalidate();
container.frame.validate();
}
/**
* <p>Adds a new element to the vectorPanel.</p>
*
* <p>This element gets the default title, and will not be opened in
* expanded form.</p>
*
* <p>The vectorPanel will be revalidated to trigger a GUI refresh
* after this element is added.</p>
*
* @param c Component to be added
*/
public void addElement(Component c)
{
addElement(null, c, false, true);
}
/**
* <p>Adds a new element to the vectorPanel.</p>
*
* <p>This element gets the default title, and will not be expanded
* immediately.</p>
*
* @param c Component to be added
* @param invalidateNow If true, invalidate()/validate() will be called.
* When adding several components all at once, set this to false.
*/
public void addElement(Component c, boolean invalidateNow)
{
addElement(null, c, false, invalidateNow);
}
/**
* <p>Adds a new element to the vectorPanel.</p>
*
* <p>The vectorPanel will be revalidated to trigger a GUI refresh
* after this element is added.</p>
*
* @param title String used in the "title" of the elementWrapper
* @param c Component to be added
*/
public void addElement(String title, Component c)
{
addElement(title,c,false, true);
}
/**
* <p>Adds a new element to the vectorPanel.</p>
*
* <p>This element will be immediately expanded if <expand> is
* true.</p>
*
* <p>The vectorPanel will be revalidated to trigger a GUI refresh
* after this element is added.</p>
*
* @param title String used in the "title" of the elementWrapper
* @param c Component to be added
* @param expand If true, the element will appear pre-opened
*/
public void addElement(String title, Component c, boolean expand)
{
addElement(title, c, expand, true);
}
/**
* <p>Add a new element to the vectorPanel.</p>
*
* @param title String used in the "title" of the elementWrapper
* @param c Component to be added
* @param expand If true, the elementWrapper will be expanded immediately after creation
* @param invalidateNow If true, invalidate()/validate() will be called.
* When adding several components all at once, set this to false.
*/
public void addElement(String title, Component c, boolean expand, boolean invalidateNow)
{
if (c == null)
{
throw new IllegalArgumentException("vectorPanel.addElement(): Component parameter is null");
}
compVector.add(c);
if (debug)
{
System.out.println("Index of element: " + compVector.indexOf(c));
}
// Make sure the centerPanel has been added.
if (!centerPanelAdded)
{
add("Center", centerPanel);
centerPanelAdded = true;
}
elementWrapper ew = new elementWrapper(title, c, this, editable && isFieldEditable());
ew.setIndex(compVector.indexOf(c));
// Keep track of the elementWrappers in the ewHash.
ewHash.put(c, ew);
// centerPanel uses a BoxLayout(Y_AXIS), so calling add() will
// just put the new component at the bottom(which is what we want)
centerPanel.add(ew);
// Only expand if it is a containerPanel. If it is something else, there
// isn't anything to expand (the GUI component will be shown inline with
// the elementWrapper's icons. We'll pre-expand the element if we are
// editing a newly created object. This check is done here in case the
// server responds to Session.create_db_object() with an object that
// already contains one or more embedded objects.
if ((c instanceof containerPanel) && (expand || isCreating))
{
ew.open();
}
// tell the containerPanel about us so it can update its progress
// bar, if it is displaying one
container.vectorElementAdded();
if (invalidateNow)
{
invalidate();
}
}
/**
* <p>Removes an element from the vector panel.</p>
*
* @param ew Component to be removed.
*/
public void deleteElement(elementWrapper ew)
{
if (debug)
{
System.out.println("Deleting element");
}
if (ew == null)
{
throw new IllegalArgumentException("Component parameter is null");
}
try
{
if (debug)
{
System.out.println("Deleting element number: " + ew.index + "(" + ew.getValue() + ")");
}
// we use ew.getValue() rather than ew.index here so that we
// don't risk screwing things up if we get our index numbers
// misaligned.
ReturnVal retVal = my_field.deleteElement(ew.getValue());
gc.handleReturnVal(retVal);
if (debug)
{
System.out.println("Deleting element (after handleReturnVal)");
}
if (ReturnVal.didSucceed(retVal))
{
// removeElement will return false if the element was
// already removed by handleReturnVal() above, due to a
// server-ordered field refresh of this vectorPanel.
if (removeElement(ew))
{
invalidate();
container.frame.validate();
}
gc.somethingChanged();
if (debug)
{
System.out.println("Deleting element (after revalidate)");
}
}
else
{
// "Server will not allow deletion of this element."
showErrorMessage(ts.l("deleteElement.error"));
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
/**
* <p>Refreshes the vectorPanel.</p>
*
* <p>This will refresh every containerPanel and IPField in this
* vectorPanel, and will add or remove entries as needed to bring
* this vectorPanel into synchronization with the field on the
* server that this vectorPanel is attached to.</p>
*
* <p>New containerPanel elements will always be added to the end of
* the vector.. we aren't worried about the order of the vector
* we're handling, just the values and the proper association
* between the GUI component and the server field.</p>
*/
public void refresh()
{
if (debug)
{
System.err.println("vectorPanel.refresh(" + name + ")");
}
boolean needFullRefresh = false;
/* -- */
try
{
if (my_field instanceof invid_field)
{
Invid invid = null;
Hashtable<Invid, Integer> serverInvids = new Hashtable<Invid, Integer>();
HashSet<Invid> localInvids = new HashSet<Invid>();
containerPanel cp = null;
// figure out what invids are currently in my_field
// on the server, save them so we can scratch them off
// as we sync this vector panel
Vector<Invid> serverValues = (Vector<Invid>) my_field.getValues();
if (debug)
{
System.err.println("vectorPanel.refresh(): serverValues.size = " + serverValues.size());
}
for (int i = 0; i < serverValues.size(); i++)
{
invid = serverValues.get(i);
serverInvids.put(invid, Integer.valueOf(i));
}
// what invid's are currently in this vector panel?
for (int i = 0; i < compVector.size(); i++)
{
cp = (containerPanel) compVector.get(i);
if (cp != null)
{
invid = cp.getObjectInvid();
localInvids.add(invid);
}
}
// now, iterate through the invids being displayed, scratching
// them out of serverInvids when we sync each containerPanel
int localIndex = 0;
while (localInvids.size() > 0)
{
cp = (containerPanel) compVector.get(localIndex);
invid = cp.getObjectInvid();
if (serverInvids.containsKey(invid))
{
// the server has this invid, go ahead and update
// this containerPanel's status
/*
gclient.handleReturnVal() will update the
containerPanel's contents if needed
cp.updateAll();
*/
elementWrapper ew = ewHash.get(cp);
ew.setIndex(localIndex);
ew.refreshTitle();
// we've updated this one.. scratch it off the server
// and client list
serverInvids.remove(invid);
localInvids.remove(invid);
localIndex++;
if (debug)
{
System.err.println("vectorPanel.refresh(): updated " + ew.titleText);
}
}
else
{
// the server doesn't have this invid anymore, so
// we need to take it out of this vector panel
elementWrapper ew = ewHash.get(cp);
removeElement(ew);
needFullRefresh = true;
localInvids.remove(invid);
if (debug)
{
System.err.println("vectorPanel.refresh(): removed " + ew.titleText);
}
}
}
// take care of any new embedded objects to be displayed, adding
// them to the end. Note that we don't care about the ordering
// of the invids in the client's vectorPanel, we just want to make
// sure that we have the right (unordered) set, and that everything
// is up-to-date.
Enumeration en = serverInvids.elements();
while (en.hasMoreElements())
{
invid = (Invid) en.nextElement();
localIndex = ((Integer) serverInvids.get(invid)).intValue();
if (debug)
{
System.out.println("VectorPanel.refresh(): adding new embedded object: " + invid);
}
ReturnVal rv = editable ? gc.getSession().edit_db_object(invid) :
gc.getSession().view_db_object(invid);
rv = gc.handleReturnVal(rv);
if (ReturnVal.didSucceed(rv))
{
// create the new containerPanel.. don't pre-load it..
containerPanel newcp = new containerPanel(((db_object) rv.getObject()), invid,
editable,
gc,
wp, container.frame,
null, false, null);
newcp.setBorder(wp.lineEmptyBorder);
// the addElement call will take care of most of the niceties of
// getting this containerPanel added.
addElement(newcp);
}
}
}
else if (my_field instanceof ip_field)
{
// this code branch hasn't been tested so well, since we
// don't use an IP address vector in ARL's schema..
int size = my_field.size();
for (int i = 0; i < size; i++)
{
if (i < compVector.size())
{
JIPField ipf = (JIPField) compVector.get(i);
ipf.setValue((IPAddress)my_field.getElement(i));
elementWrapper ew = ewHash.get(ipf);
ew.setIndex(i);
}
else
{
ip_field ipfield = (ip_field) my_field;
JIPField ipf = new JIPField(editable,
ipfield.v6Allowed());
ipf.setValue((IPAddress) ipfield.getElement(i));
ipf.setCallback(this);
addElement(ipf, false);
}
}
// Now get rid of extra ip field components
int fieldCount = compVector.size();
if (fieldCount > 0)
{
needFullRefresh = true;
}
// we count down so that we can remove extra fields off of the
// end
for (int i = fieldCount; i >= size; i--)
{
removeElement(ewHash.get(compVector.get(i)));
}
}
else
{
System.err.println("Unknown type in vectorPanel.refresh compVector: " + my_field.getClass());
}
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx, "vectorPanel.refresh(): ");
}
if (needFullRefresh)
{
invalidate();
container.frame.validate();
if (debug)
{
System.err.println("vectorPanel.refresh(): executed fullRefresh");
}
}
else if (debug)
{
System.err.println("vectorPanel.refresh(): skipped fullRefresh");
}
}
/**
* <p>Expand all the levels.</p>
*/
public void expandAllLevels()
{
for (elementWrapper ew: ewHash.values())
{
ew.open();
Component comp = ew.getComponent();
if (comp instanceof containerPanel)
{
if (debug)
{
System.out.println("Aye, it's a containerPanel");
}
containerPanel cp = (containerPanel)comp;
for (vectorPanel vp: cp.vectorPanelList)
{
vp.expandLevels(true);
}
}
else if (debug)
{
System.out.println("The likes of this I have never seen: " + comp);
}
}
invalidate();
container.frame.validate();
}
/**
* <p>Expands all closed elementWrappers in this vectorPanel</p>
*
* @param recursive If true, it will expand any vector panels inside
* the containerPanels in this vectorPanel as well. The current
* implementation opens up each level with a separate thread, which
* may well not be the best way of doing this from a performance/safety
* perspective.
*/
public void expandLevels(boolean recursive)
{
if (recursive)
{
// ok, mike did this with threaded recursion, strangely enough. Note
// that t.start() is equivalent to calling expandAllLevels() in a new
// thread, by way of this.run().
Thread t = new Thread(this);
t.setPriority(Thread.NORM_PRIORITY);
t.start();
}
else
{
setWaitCursor();
for (elementWrapper ew: ewHash.values())
{
ew.open();
}
setNormalCursor();
}
}
/**
* This just calls expandAllLevels.. for use in threading this out.
*/
public void run()
{
expandAllLevels();
}
/**
* Close all the levels
*
* @param recursive If true, close all vectorPanels inside this
* vectorPanel. This one never spawns another thread.
*/
public void closeLevels(boolean recursive)
{
setWaitCursor();
try
{
for (elementWrapper ew: ewHash.values())
{
ew.close();
if (recursive)
{
Component comp = ew.getComponent();
if (comp instanceof containerPanel)
{
containerPanel cp = (containerPanel)comp;
for (vectorPanel vp: cp.vectorPanelList)
{
vp.closeLevels(true);
}
}
}
}
// This is necessary for the closing, but the expanding doesn't
// need validate stuff. Pretty weird, but thus is swing.
invalidate();
container.frame.validate();
}
finally
{
setNormalCursor();
}
}
public void actionPerformed(ActionEvent e)
{
if ((e.getSource() == addB) && editable)
{
setWaitCursor();
addNewElement();
setNormalCursor();
}
else if (e.getSource() instanceof JMenuItem)
{
if (debug)
{
System.out.println("JMenuItem: " + e.getActionCommand());
}
if (e.getActionCommand().equals("Expand all elements"))
{
expandLevels(true);
}
else if (e.getActionCommand().equals("Expand this level"))
{
expandLevels(false);
}
else if (e.getActionCommand().equals("Close this level"))
{
closeLevels(false);
}
else if (e.getActionCommand().equals("Close all elements"))
{
closeLevels(true);
}
}
}
/**
* JsetValueCallback implementation method. Any elementWrappers we
* contain will talk to us using this method, to notify us of element
* deletion and to pass information to us about IP field value changes.
*/
public boolean setValuePerformed(JValueObject v)
{
if (v == null)
{
throw new IllegalArgumentException("ValueObject Argument is null");
}
elementWrapper ew = (elementWrapper)v.getSource();
boolean returnValue = false;
if (v.getValue() == null)
{
return false;
}
else if (v.getValue().equals("remove"))
{
if (debug)
{
System.out.println("You clicked on an element closebox");
}
if (editable)
{
deleteElement((elementWrapper)v.getSource());
}
else
{
// "You can''t delete elements in a view window."
setStatus(ts.l("setValuePerformed.nope_status"));
returnValue = false;
}
}
else if (ew.getComponent() instanceof JIPField)
{
if (debug)
{
System.out.println("IP field changed");
}
if (editable)
{
short index = (short)compVector.indexOf(ew.getComponent());
if (v instanceof JErrorValueObject)
{
setStatus((String)v.getValue());
returnValue = false;
}
else
{
try
{
returnValue = changeElement((IPAddress)v.getValue(), index);
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
}
else //editable == false, so can't make changes
{
returnValue = false;
}
}
else if (debug)
{
System.out.println("Value changed in field that is not yet supported");
}
if (returnValue)
{
gc.somethingChanged();
}
return returnValue;
}
/**
* <p>Server dispatch method for value change messages passed us by
* elementWrappers.</p>
*
* <p>Currently used only for IP fields, as embedded containerPanels
* communicate their field changes directly to the server.</p>
*/
public boolean changeElement(Object obj, short index) throws RemoteException
{
ReturnVal retVal;
boolean succeeded;
/* -- */
if (index >= my_field.size())
{
if (debug)
{
System.out.println("Adding new element");
}
retVal = my_field.addElement(obj);
succeeded = (retVal == null) ? true : retVal.didSucceed();
if (retVal != null)
{
gc.handleReturnVal(retVal);
}
if (succeeded)
{
if (debug)
{
System.out.println("Add Element returned true");
System.out.println("There are now " + my_field.size() + " elements in the field");
}
return true;
}
else
{
if (debug)
{
System.out.println("Add Element returned false");
}
return false;
}
}
else
{
if (debug)
{
System.out.println("Changing element " + index);
}
retVal = my_field.setElement(index, obj);
succeeded = (retVal == null) ? true : retVal.didSucceed();
if (retVal != null)
{
gc.handleReturnVal(retVal);
}
if (succeeded)
{
if (debug)
{
System.out.println("set Element returned true");
}
return true;
}
else
{
if (debug)
{
System.out.println("set Element returned false");
}
return false;
}
}
}
/**
* Pop-up menu dispatch.
*/
public void mousePressed(java.awt.event.MouseEvent e)
{
if (e.isPopupTrigger())
{
showPopupMenu(e.getX(), e.getY());
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
// FocusListener methods ------------------------------------------------------
public void focusLost(FocusEvent e)
{
}
public void focusGained(FocusEvent e)
{
addB.scrollRectToVisible(addB.getBounds());
}
// convenience stuff
private final void setStatus(String status)
{
gc.setStatus(status);
}
private final void setNormalCursor()
{
gc.setNormalCursor();
}
private final void setWaitCursor()
{
gc.setWaitCursor();
}
private final void showErrorMessage(String message)
{
gc.showErrorMessage(message);
}
/**
* <p>Returns true if the vector field we are connected to is
* editable.</p>
*
* <p>This method will call the server to query the field the first
* time it is called. It will return the cached result
* thereafter.</p>
*/
public boolean isFieldEditable()
{
if (myFieldIsEditable == null)
{
try
{
myFieldIsEditable = Boolean.valueOf(my_field.isEditable());
}
catch (Exception rx)
{
gc.processExceptionRethrow(rx);
}
}
return myFieldIsEditable.booleanValue();
}
/**
* This private helper method removes an element from this vectorPanel.
*/
private boolean removeElement(elementWrapper ew)
{
Component component = ew.getComponent();
// Check to see if the component is null so that we turn into a
// no-op if the element has already been removed.
//
// The component *might* be null if we are calling removeElement()
// after a server-ordered rescan of this vectorPanel is performed
// by gc.handleReturnVal().
if (component != null)
{
if (template.isEditInPlace())
{
container.frame.removeContainerPanel((containerPanel) component);
}
centerPanel.remove(ew);
compVector.removeElement(component);
ewHash.remove(component);
ew.clearElement();
return true;
}
return false;
}
}