001: package jimm.datavision.gui;
002:
003: import jimm.datavision.field.Field;
004: import jimm.datavision.gui.cmd.TypingCommand;
005: import jimm.util.I18N;
006: import java.awt.Cursor;
007: import java.awt.Font;
008: import java.awt.FontMetrics;
009: import java.awt.event.MouseEvent;
010: import java.awt.event.KeyEvent;
011: import java.awt.event.ActionEvent;
012: import javax.swing.JTextPane;
013: import javax.swing.text.BadLocationException;
014:
015: /**
016: *
017: * A text field widget is a field widget that is editable.
018: *
019: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
020: */
021: public class TextFieldWidget extends FieldWidget {
022:
023: protected int origHeight;
024: protected int lineHeight;
025: protected boolean changingEditState;
026: protected TypingCommand typingCommand;
027:
028: /**
029: * Constructor.
030: *
031: * @param sw section widget in which the field's new widget will reside
032: * @param field a report field
033: */
034: public TextFieldWidget(SectionWidget sw, Field field) {
035: super (sw, field);
036:
037: // Calculate line height.
038: Font f = field.getFormat().getFont();
039: FontMetrics fm = swingField.getComponent().getFontMetrics(f);
040: lineHeight = fm.getHeight();
041: }
042:
043: protected String getPopupNameText() {
044: // This string is now found in the menu properties file.
045: return I18N.get(I18N.MENU_FILE_PREFIX,
046: "TextFieldWidget.popup_name");
047: }
048:
049: protected void addCustomPopupItems() {
050: MenuUtils.addToMenu(this , popup, "TextFieldWidget.popup_edit",
051: POPUP_FONT);
052: popup.addSeparator();
053: }
054:
055: /**
056: * Performs some action based on the action command string (the menu
057: * item text).
058: */
059: public void actionPerformed(ActionEvent e) {
060: String command = e.getActionCommand();
061: if (command == null)
062: return;
063:
064: if ("edit".equals(command))
065: startEditing();
066: else
067: super .actionPerformed(e);
068: }
069:
070: /**
071: * if this is a double-click, start editing; else handle the mouse event
072: * like a normal field widget.
073: *
074: * @param e mouse event
075: */
076: public void mouseClicked(MouseEvent e) {
077: if (e.getClickCount() == 2)
078: startEditing();
079: else
080: super .mouseClicked(e);
081: }
082:
083: /**
084: * Makes our text editable and starts editing.
085: */
086: public void startEditing() {
087: // Bug fix: endEditing is called while startEditing is running
088: synchronized (this ) {
089: if (changingEditState)
090: return;
091: changingEditState = true;
092: }
093:
094: if (!selected)
095: select(true);
096:
097: JTextPane textPane = (JTextPane) getComponent();
098: textPane.setEditable(true);
099: textPane.getCaret().setVisible(true);
100: textPane.requestFocus();
101: origHeight = textPane.getBounds().height;
102:
103: sectionWidget.designer.setIgnoreKeys(true);
104:
105: textPane.removeMouseListener(this );
106: textPane.removeMouseMotionListener(this );
107:
108: changingEditState = false;
109:
110: typingCommand = new TypingCommand(this , origHeight);
111: }
112:
113: /**
114: * Stores new value and bounds from self into field and makes text
115: * non-editable.
116: */
117: protected void endEditing() {
118: // Bug fix: endEditing is called while startEditing is running
119: synchronized (this ) {
120: if (changingEditState)
121: return;
122: changingEditState = true;
123: }
124:
125: if (typingCommand != null) {
126: getSectionWidget().performCommand(typingCommand);
127: typingCommand = null;
128: }
129:
130: changingEditState = false;
131: }
132:
133: /**
134: * Perform selection; if becoming deselected, ends editing.
135: *
136: * @param makeSelected new selection state
137: */
138: protected void doSelect(boolean makeSelected) {
139: if (makeSelected == false)
140: endEditing();
141: super .doSelect(makeSelected);
142: }
143:
144: /**
145: * If this field is being edited, show text cursor. Else call superclass
146: * method.
147: *
148: * @param e a mouse event
149: */
150: protected void cursorForPosition(MouseEvent e) {
151: JTextPane textPane = (JTextPane) getComponent();
152: if (textPane.isEditable())
153: textPane.setCursor(Cursor
154: .getPredefinedCursor(Cursor.TEXT_CURSOR));
155: else
156: super .cursorForPosition(e);
157: }
158:
159: /**
160: * Handles return key by expanding height of editor and backspace and
161: * delete by shrinking height if newline is deleted.
162: *
163: * @param e key event
164: */
165: public void keyTyped(KeyEvent e) {
166: char c = e.getKeyChar();
167: int pos;
168: java.awt.Rectangle bounds;
169: JTextPane textPane = (JTextPane) getComponent();
170:
171: switch (c) {
172: case KeyEvent.VK_ENTER:
173: // We've just grown. Make sure we still fit.
174: bounds = getComponent().getBounds();
175: bounds.height += lineHeight;
176: getComponent().setBounds(bounds);
177:
178: // Make sure we still fit in section
179: sectionWidget.growToFit();
180: break;
181: case KeyEvent.VK_BACK_SPACE:
182: try {
183: pos = textPane.getCaretPosition();
184: if (pos > 0
185: && textPane.getText(pos - 1, 1).equals("\n")) {
186: bounds = textPane.getBounds();
187: bounds.height -= lineHeight;
188: textPane.setBounds(bounds);
189: }
190: } catch (BadLocationException ex) {
191: }
192: break;
193: case KeyEvent.VK_DELETE:
194: try {
195: pos = textPane.getCaretPosition();
196: if (pos < textPane.getText().length()
197: && textPane.getText(pos, 1).equals("\n")) {
198: bounds = textPane.getBounds();
199: bounds.height -= lineHeight;
200: textPane.setBounds(bounds);
201: }
202: } catch (BadLocationException ex2) {
203: }
204: break;
205: }
206: }
207:
208: }
|