/*
JentryField.java
Created: 12 Jul 1996
Module By: Navin Manohar
-----------------------------------------------------------------------
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.JDataComponent;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.rmi.RemoteException;
import javax.swing.BorderFactory;
import javax.swing.JComponent;;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/*------------------------------------------------------------------------------
class
JstringArea
------------------------------------------------------------------------------*/
/**
* This is a multi-line, scrollable text component with support for
* the {@link arlut.csd.JDataComponent.JsetValueCallback} listener
* interface used for the arlut.csd.JDataComponent graphical
* components.
*
* This component consists of a JScrollPane with an embedded {@link
* arlut.csd.JDataComponent.myTextArea}. The myTextArea object
* handles keystroke filtering to prevent input of characters that do
* not meet the acceptable characters constraints.
*
* You can control how arlut.csd.JDataComponent.JstringArea handles
* scrolling by calling the {@link
* javax.swing.JScrollPane#setHorizontalScrollBarPolicy(int)} and
* {@link javax.swing.JScrollPane#setVerticalScrollBarPolicy(int)}
* methods on JScrollPane, which we inherit from.
*/
public class JstringArea extends JScrollPane implements FocusListener {
final static boolean debug = false;
// ---
public boolean allowCallback = false;
protected boolean changed = false;
protected JsetValueCallback my_parent = null;
private boolean processingCallback = false;
private myTextArea textArea = null;
String
value = null, // last known value, used in comparisons to see if we need to do full callback
allowedChars = null,
disallowedChars = null;
/* -- */
public JstringArea()
{
this(0,0); // Call to main contructor function
}
public JstringArea(int rows, int columns)
{
// create myTextArea to put inside the JScrollPane, JScrollPane
// fits to area size
textArea = new myTextArea(this,rows,columns);
if (debug)
{
System.out.println("Constructing pane with textarea, adding to JScrollPane: ");
}
// Add textArea to scrollPane viewport
setViewportView(textArea);
textArea.setVisible(true);
addFocusListener(this);
} // JstringArea
///////////////////
// Class Methods //
///////////////////
/**
* returns true if the value in the JentryField has
* been modified.
*/
public boolean getChanged()
{
return changed;
}
public void setText(String s)
{
value = s;
textArea.setText(s);
}
public String getText()
{
return textArea.getText();
}
public void setCaretPosition(int pos)
{
textArea.setCaretPosition(pos);
}
public void setAllowedChars(String s)
{
allowedChars = s;
}
public void setDisallowedChars(String s)
{
disallowedChars = s;
}
public void setEditable(boolean val)
{
if (my_parent != null)
{
allowCallback = val;
}
textArea.setEditable(val);
}
/**
* sets the parent of this component for callback purposes
*
*/
public void setCallback(JsetValueCallback parent)
{
if (parent == null)
{
throw new IllegalArgumentException("Invalid Parameter: parent cannot be null");
}
my_parent = parent;
allowCallback = textArea.isEditable();
}
/**
* sendCallback is called when focus is lost.
*/
public void sendCallback()
{
String str;
/* -- */
synchronized (this)
{
if (processingCallback)
{
return;
}
processingCallback = true;
}
try
{
// if nothing in the JstringArea has changed,
// we don't need to worry about this event.
str = getText();
if (value != null)
{
if (debug)
{
System.err.println("JstringArea.sendCallback: old value != null");
}
changed = !value.equals(str);
}
else
{
if (debug)
{
System.err.println("JstringArea.sendCallback: old value == null");
}
if (str == null || str.equals(""))
{
changed = false;
}
else
{
changed = true;
}
}
if (debug)
{
System.err.println("JstringArea.sendCallback(): str == '" + str + "', value == '" + value + "'");
}
if (!changed)
{
if (debug)
{
System.err.println("JstringArea.sendCallback: no change, ignoring");
}
return;
}
// if we don't need to handle callbacks, just accept the new
// string value from the user and return
if (!allowCallback)
{
value = str;
return;
}
boolean b = false;
try
{
if (debug)
{
System.err.println("JstringArea.sendCallback: making callback");
}
if (str.equals(""))
{
b = my_parent.setValuePerformed(new JSetValueObject(this, null));
}
else
{
b = my_parent.setValuePerformed(new JSetValueObject(this, str));
}
}
catch (RemoteException re)
{
}
// If the setValuePerformed callback failed, we'll revert the value to our last
// approved value
if (!b)
{
if (debug)
{
System.err.println("JstringArea.sendCallback: setValue rejected");
if (value == null)
{
System.err.println("JstringArea.sendCallback: resetting to empty string");
}
else
{
System.err.println("JstringArea.sendCallback: resetting to " + value);
}
}
if (value == null)
{
textArea.setText("");
}
else
{
textArea.setText(value);
}
changed = false;
}
else
{
if (debug)
{
System.err.println("JstringArea.sendCallback: setValue('" + str + "') accepted");
}
if (str.equals(""))
{
value = null;
}
else
{
value = str;
}
changed = false;
}
}
finally
{
processingCallback = false;
}
}
/**
* Stub function that is overriden in subclasses of JentryField
*
*/
boolean isAllowed(char ch)
{
if (disallowedChars != null)
{
if (disallowedChars.indexOf(ch) != -1)
{
if (debug)
{
System.err.println("JstringArea.isAllowed() rejecting char " + ch + " as disallowed");
}
return false;
}
}
if (allowedChars != null)
{
if (allowedChars.indexOf(ch) == -1)
{
if (debug)
{
System.err.println("JstringArea.isAllowed() rejecting char " + ch + " as not allowed");
}
return false;
}
}
return true;
}
public void focusLost(FocusEvent e)
{
if (debug)
{
System.out.println("JstringArea: focusLost");
}
sendCallback();
}
public void focusGained(FocusEvent e)
{
if (debug)
{
System.out.println("focusGained");
}
JComponent parent = (JComponent) this.getParent();
if (parent != null)
{
parent.scrollRectToVisible(this.getBounds());
}
}
/**
*
* Debug rig
*
*/
public static final void main(String argv[])
{
JFrame frame = new JFrame();
JstringArea area = new JstringArea(5, 10);
area.setText("Hello world");
area.setDisallowedChars("asdf");
frame.getContentPane().add(area);
area.setCallback(new JsetValueCallback() {
public boolean setValuePerformed(JValueObject o) {
System.out.println("I got an o: " + o.getValue());
return true;
}
});
frame.pack();
frame.setSize(200,200);
frame.setVisible(true);
}
}
/*------------------------------------------------------------------------------
class
myTextArea
------------------------------------------------------------------------------*/
/**
* This is a custom subclass of {@link javax.swing.JTextArea} that
* provides the inner gui component substrate for the optionally
* scrollable {@link arlut.csd.JDataComponent.JstringArea}.
*
* By embedding this subclass in JstringArea, we can intercept event
* processing from our superclass while still allowing JstringArea to
* be an all-in-one component suitable for use in the Ganymede {@link
* arlut.csd.ganymede.client.containerPanel}.
*/
class myTextArea extends JTextArea
{
private JstringArea my_parent;
/* -- */
public myTextArea(JstringArea x, int rows, int cols)
{
super(rows,cols);
this.my_parent = x;
enableEvents(AWTEvent.KEY_EVENT_MASK);
addFocusListener(x);
}
/**
* We only want certain keystrokes to be registered by the field.
*
* This method overrides the processKeyEvent() method in JComponent and
* gives us a way of intercepting characters that we don't want in our
* string field.
*/
protected void processKeyEvent(KeyEvent e)
{
// always pass through useful editing keystrokes.. this seems to be
// necessary because backspace and delete apparently have defined
// Unicode representations, so they don't match CHAR_UNDEFINED below
switch (e.getKeyCode())
{
case KeyEvent.VK_BACK_SPACE:
case KeyEvent.VK_DELETE:
case KeyEvent.VK_END:
case KeyEvent.VK_HOME:
super.processKeyEvent(e);
return;
}
// We check against KeyEvent.CHAR_UNDEFINED so that we pass
// through things like arrow keys, etc. that don't result in
// character insertion
if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED)
{
super.processKeyEvent(e);
return;
}
// now check with the JstringArea to adjudicate this character
// insertion
if (my_parent.isAllowed(e.getKeyChar()))
{
super.processKeyEvent(e);
return;
}
// otherwise, we ignore it
if (JstringArea.debug)
{
System.err.println("JstringArea: skipping key event " + e);
}
} // processKeyEvent
} // mytextarea