/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.util.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.FocusManager;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import com.servoy.j2db.util.Debug;
/**
* <class description> creation <date> changes <date1>, <description1> ... <daten>, <descriptionn>
*
* @author <author name(s)>
* @see <string>
*/
public class SizableTextField extends JLabel implements MouseListener
{
/*
* _____________________________________________________________ Declaration of attributes
*/
private final SpecialTextArea textArea;
private boolean select = false;
public JRootPane rootPane;
private Document doc = new PlainDocument();
private List _focusListeners;
private List _cellEditorListeners;
private boolean havingFocus = false; //can't use isManaging() focus becouse that may influence the focus manager
private Character eventChar;
/*
* _____________________________________________________________ Declaration and definition of constructors
*/
public SizableTextField()
{
this.setBorder(BorderFactory.createEtchedBorder());
setBackground(Color.white);
setOpaque(true);
addMouseListener(this);
textArea = new SpecialTextArea();
textArea.setFont(getFont());
textArea.setLineWrap(true);
}
/*
* _____________________________________________________________ The methods below override methods from superclass <classname>
*/
/*
* _____________________________________________________________ The methods below belong to interface <interfacename>
*/
/*
* _____________________________________________________________ The methods below belong to this class
*/
public Object getValue()
{
return this.getText();
}
public void setValue(Object o)
{
if (o != null)
{
this.setText(o.toString());
}
else
{
this.setText(""); //$NON-NLS-1$
}
}
public void setDocument(Document doc)
{
this.doc = doc;
}
public Document getDocument()
{
return doc;
}
@Override
public void setText(String s)
{
if (s == null) s = ""; //$NON-NLS-1$
// Debug.trace("renderer.setText "+s);
// if (havingFocus)
super.setText(s);
if (textArea != null && havingFocus) textArea.setText(s);
}
@Override
public void setForeground(Color c)
{
// if (havingFocus) textArea.setForeground(c);
super.setForeground(c);
if (textArea != null) textArea.setForeground(c);
}
@Override
public void setBackground(Color c)
{
// if (havingFocus) textArea.setBackground(c);
super.setBackground(c);
if (textArea != null) textArea.setBackground(c);
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
if (isEnabled()) requestFocus();
}
@Override
public void grabFocus()
{
super.grabFocus();
requestFocus();
}
public void selectAll()
{
select = true;
}
@Override
public void requestFocus()
{
if (rootPane == null)
{
rootPane = SwingUtilities.getRootPane(this);
if (rootPane == null)
{
throw new IllegalStateException("SizableTextField works only with a parent wich is a JRootPane"); //$NON-NLS-1$
}
JLayeredPane lay = rootPane.getLayeredPane();
rootPane.getGlassPane().addMouseListener(textArea);
rootPane.getGlassPane().addMouseMotionListener(textArea);
lay.add(textArea, JLayeredPane.MODAL_LAYER, 5);
}
textArea.setDocument(doc);
textArea.setFont(this.getFont());
textArea.setBackground(this.getBackground());
textArea.setForeground(this.getForeground());
textArea.setVisible(true);
if (/* getBorder() == null && */getParent() instanceof JComponent)
{
Border b = ((JComponent)getParent()).getBorder();
if (b != null)
{
textArea.setBorder(b);
}
else
{
textArea.setBorder(BorderFactory.createLineBorder(Color.black));
}
}
else
{
textArea.setBorder(this.getBorder());
}
// textArea.setMargin(new Insets(0,2,0,0));
textArea.setLocation(computeLocation(this));
textArea.setSize(getSize());
if (eventChar != null)
{
textArea.setText(getText() + eventChar.toString());
eventChar = null;
}
else
{
textArea.setText(getText());
}
textArea.doSize();
fireFocusGained();
rootPane.getGlassPane().setVisible(true);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
textArea.requestFocus();
if (select) textArea.selectAll();
}
});
}
@Override
public boolean isFocusTraversable()
{
return true;
}
//new focus listener becouse focusin is changed for this componenet
public void addCellEditorListener(CellEditorListener l)
{
if (_cellEditorListeners == null) _cellEditorListeners = new ArrayList();
_cellEditorListeners.add(l);
}
public void removeCellEditorListener(CellEditorListener l)
{
_cellEditorListeners.remove(l);
}
//new focus listener becouse focusin is changed for this componenet
@Override
public void addFocusListener(FocusListener l)
{
if (_focusListeners == null) _focusListeners = new ArrayList();
_focusListeners.add(l);
}
@Override
public void removeFocusListener(FocusListener l)
{
_focusListeners.remove(l);
}
void fireFocusGained()
{
havingFocus = true;
if (_focusListeners != null)
{
for (int i = 0; i < _focusListeners.size(); i++)
{
((FocusListener)_focusListeners.get(i)).focusGained(new FocusEvent(this, 0, false));
}
}
}
void fireFocusLost()
{
havingFocus = false;
if (_focusListeners != null) for (int i = 0; i < _focusListeners.size(); i++)
{
((FocusListener)_focusListeners.get(i)).focusLost(new FocusEvent(this, 0, false));
}
if (_cellEditorListeners != null) for (int i = 0; i < _cellEditorListeners.size(); i++)
{
((CellEditorListener)_cellEditorListeners.get(i)).editingStopped(new ChangeEvent(this));
}
}
private Point computeLocation(Component w)
{
Point retval = new Point();
while ((w != null) && !(w instanceof JRootPane))
{
retval.translate(w.getLocation().x, w.getLocation().y);
// Debug.trace(w);
w = w.getParent();
}
return retval;
}
/**
* @see java.awt.Component#setFont(Font)
*/
@Override
public void setFont(Font f)
{
super.setFont(f);
if (textArea != null) textArea.setFont(f);
}
/**
* @see javax.swing.JComponent#processKeyBinding(KeyStroke, KeyEvent, int, boolean)
*/
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed)
{
if (Character.isLetterOrDigit(e.getKeyChar()))
{
eventChar = new Character(e.getKeyChar());
}
return super.processKeyBinding(ks, e, condition, pressed);
}
class SpecialTextArea extends JTextArea implements CaretListener, FocusListener, MouseListener, MouseMotionListener
{
SpecialTextArea()
{
super();
this.addCaretListener(this);
this.addFocusListener(this);
// this.addMouseListener(this);
// this.addMouseMotionListener(this);
// this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter");
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape"); //$NON-NLS-1$
/*
* this.getActionMap().put("enter", new AbstractAction() { public void actionPerformed(ActionEvent event) { System.out.println(event); } });
*/this.getActionMap().put("escape", new AbstractAction() //$NON-NLS-1$
{
public void actionPerformed(ActionEvent event)
{
// System.out.println(event);
}
});
}
@Override
protected void processKeyEvent(KeyEvent e)
{
super.processKeyEvent(e);
}
@Override
protected void processComponentKeyEvent(KeyEvent e)
{
// Debug.trace("processComponentKeyEvent "+e.getID());
if ((e.getKeyCode() == KeyEvent.VK_TAB || e.getKeyCode() == KeyEvent.VK_ESCAPE))// || (e.getKeyCode() == e.VK_ENTER && e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD)))
{
if (e.getID() == KeyEvent.KEY_PRESSED)
{
this.setVisible(false);
if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
{
super.setText(SizableTextField.this.getText());
}
else
{
SizableTextField.this.setText(getText());
}
SizableTextField.this.fireFocusLost();
if ((e.getModifiers() & InputEvent.SHIFT_MASK) == InputEvent.SHIFT_MASK)
{
FocusManager.getCurrentManager().focusPreviousComponent(SizableTextField.this);
}
else
{
FocusManager.getCurrentManager().focusNextComponent(SizableTextField.this);
}
}
e.consume();
}
else
{
super.processComponentKeyEvent(e);
}
}
public void caretUpdate(CaretEvent e)
{
doSize();
}
public void doSize()
{
FontMetrics fm = this.getFontMetrics(this.getFont());
StringTokenizer st = new StringTokenizer(getText(), "\n", true); //$NON-NLS-1$
int rows = getText().endsWith("\n") ? 0 : 1; //$NON-NLS-1$
while (st.hasMoreTokens())
{
// rows++;
String sToken = st.nextToken();
if (sToken.equals("\n")) //$NON-NLS-1$
{
rows++;
}
else
{
rows += fm.stringWidth(sToken) / SizableTextField.this.getWidth();
}
}
if (getText().endsWith("\n")) rows++; //$NON-NLS-1$
int height = rows * fm.getHeight();
if (height > SizableTextField.this.getHeight())
{
Border b = this.getBorder();
Insets i;
if (b == null)
{
i = new Insets(0, 0, 0, 0);
}
else
{
i = b.getBorderInsets(this);
}
setSize(getWidth(), rows * fm.getHeight() + i.top + i.bottom);
}
else
{
setSize(SizableTextField.this.getSize());
}
}
public void focusGained(FocusEvent e)
{
doSize();
setCaretPosition(getText().length());
}
public void focusLost(FocusEvent e)
{
SizableTextField.this.setText(getText());
SizableTextField.this.fireFocusLost();
this.setVisible(false);
}
// -------------------- code for correct hiding of glasspane
public void mouseMoved(MouseEvent e)
{
// redispatchMouseEvent(e);
}
private boolean dragMode = false;
public void mouseDragged(MouseEvent e)
{
dragMode = true;
redispatchMouseEvent(e);
}
public void mouseClicked(MouseEvent e)
{
redispatchMouseEvent(e);
dragMode = false;
}
public void mouseEntered(MouseEvent e)
{
// redispatchMouseEvent(e);
}
public void mouseExited(MouseEvent e)
{
// redispatchMouseEvent(e);
}
public void mousePressed(MouseEvent e)
{
redispatchMouseEvent(e);
}
public void mouseReleased(MouseEvent e)
{
redispatchMouseEvent(e);
dragMode = false;
}
private void redispatchMouseEvent(MouseEvent e)
{
Component glassPane = rootPane.getGlassPane();
if (e.getSource() != glassPane) return;
JLayeredPane container = rootPane.getLayeredPane();
Point glassPanePoint = e.getPoint();
//Container container = rootPane.getContentPane();
Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, container);
Component component = SwingUtilities.getDeepestComponentAt(container, containerPoint.x, containerPoint.y);
if (component == null || component == e.getSource()) return;
Point componentPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, component);
MouseEvent ne = new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(), componentPoint.x, componentPoint.y, e.getClickCount(),
e.isPopupTrigger());
if (component == this)
{
try
{
this.dispatchEvent(ne);
}
catch (Exception ex)
{
// ignore
Debug.trace(ex);
}
}
else if (!dragMode)
{
rootPane.getGlassPane().setVisible(false);
SizableTextField.this.setText(getText());
SizableTextField.this.fireFocusLost();
this.setVisible(false);
component.dispatchEvent(ne);
}
}
}
}