001: /*
002: * @(#)TextComponent.java 1.51 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027: package java.awt;
028:
029: import sun.awt.peer.TextComponentPeer;
030: import java.awt.event.*;
031: import java.io.ObjectOutputStream;
032: import java.io.ObjectInputStream;
033: import java.io.IOException;
034: import sun.awt.SunToolkit;
035: import java.util.EventListener;
036: import java.awt.AWTEventMulticaster;
037:
038: /**
039: * The <code>TextComponent</code> class is the superclass of
040: * any component that allows the editing of some text.
041: * <p>
042: * A text component embodies a string of text. The
043: * <code>TextComponent</code> class defines a set of methods
044: * that determine whether or not this text is editable. If the
045: * component is editable, it defines another set of methods
046: * that supports a text insertion caret.
047: * <p>
048: * In addition, the class defines methods that are used
049: * to maintain a current <em>selection</em> from the text.
050: * The text selection, a substring of the component's text,
051: * is the target of editing operations. It is also referred
052: * to as the <em>selected text</em>.
053: *
054: * @version 1.43, 08/19/02
055: * @author Sami Shaio
056: * @author Arthur van Hoff
057: * @since JDK1.0
058: */
059: public class TextComponent extends Component {
060: /**
061: * The value of the text.
062: */
063: String text;
064: /**
065: * A boolean indicating whether or not this TextComponent is editable.
066: */
067: boolean editable = true;
068: /**
069: * The selection start.
070: */
071: int selectionStart;
072: /**
073: * The selection end.
074: */
075: int selectionEnd;
076:
077: // A flag used to tell whether the background has been set by
078: // developer code (as opposed to AWT code). Used to determine
079: // the background color of non-editable TextComponents.
080: boolean backgroundSetByClientCode = false;
081:
082: transient protected TextListener textListener;
083: /*
084: * JDK 1.1 serialVersionUID
085: */
086: private static final long serialVersionUID = -2214773872412987419L;
087:
088: /**
089: * Constructs a new text component initialized with the
090: * specified text. Sets the value of the cursor to
091: * <code>Cursor.TEXT_CURSOR</code>.
092: * The insertion caret is initially placed before the first
093: * character and the text is left justified.
094: * @param text the initial text that the component presents.
095: * @see java.awt.Cursor
096: * @since JDK1.0
097: */
098: TextComponent(String text) {
099: this .text = (text == null) ? "" : text;
100: setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
101: }
102:
103: boolean areInputMethodsEnabled() {
104: // this call will initialize the toolkit if not already initialized.
105: if (checkForEnableIM) {
106: enableInputMethodsIfNecessary();
107: }
108:
109: // TextComponent handles key events without touching the eventMask or
110: // having a key listener, so just check whether the flag is set
111: return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0;
112: }
113:
114: private void enableInputMethodsIfNecessary() {
115: if (checkForEnableIM) {
116: checkForEnableIM = false;
117: try {
118: Toolkit toolkit = Toolkit.getDefaultToolkit();
119: boolean shouldEnable = false;
120: shouldEnable = ((SunToolkit) toolkit)
121: .enableInputMethodsForTextComponent();
122: enableInputMethods(shouldEnable);
123: } catch (Exception e) {
124: // if something bad happens, just don't enable input methods
125: }
126: }
127: }
128:
129: public void enableInputMethods(boolean enable) {
130: checkForEnableIM = false;
131: super .enableInputMethods(enable);
132: }
133:
134: /**
135: * Makes this Component displayable by connecting it to a
136: * native screen resource.
137: * This method is called internally by the toolkit and should
138: * not be called directly by programs.
139: * @see java.awt.TextComponent#removeNotify
140: */
141: public void addNotify() {
142: super .addNotify();
143: enableInputMethodsIfNecessary();
144: }
145:
146: /**
147: * Removes the TextComponent's peer. The peer allows us to modify
148: * the appearance of the TextComponent without changing its
149: * functionality.
150: */
151: public void removeNotify() {
152: synchronized (getTreeLock()) {
153: TextComponentPeer peer = (TextComponentPeer) this .peer;
154: if (peer != null) {
155: text = peer.getText();
156: selectionStart = peer.getSelectionStart();
157: selectionEnd = peer.getSelectionEnd();
158: }
159: super .removeNotify();
160: }
161: }
162:
163: /**
164: * Sets the text that is presented by this
165: * text component to be the specified text.
166: * @param t the new text.
167: * @see java.awt.TextComponent#getText
168: * @since JDK1.0
169: */
170: public synchronized void setText(String t) {
171: if (t == null)
172: t = "";
173: text = t;
174: TextComponentPeer peer = (TextComponentPeer) this .peer;
175: if (peer != null) {
176: peer.setText(t);
177: }
178: }
179:
180: /**
181: * Gets the text that is presented by this text component.
182: * @see java.awt.TextComponent#setText
183: * @since JDK1.0
184: */
185: public synchronized String getText() {
186: TextComponentPeer peer = (TextComponentPeer) this .peer;
187: if (peer != null) {
188: text = peer.getText();
189: }
190: return text;
191: }
192:
193: /**
194: * Gets the selected text from the text that is
195: * presented by this text component.
196: * @return the selected text of this text component.
197: * @see java.awt.TextComponent#select
198: * @since JDK1.0
199: */
200: public synchronized String getSelectedText() {
201: return getText().substring(getSelectionStart(),
202: getSelectionEnd());
203: }
204:
205: /**
206: * Indicates whether or not this text component is editable.
207: * @return <code>true</code> if this text component is
208: * editable; <code>false</code> otherwise.
209: * @see java.awt.TextComponent#setEditable
210: * @since JDK1ble
211: */
212: public boolean isEditable() {
213: return editable;
214: }
215:
216: /**
217: * Sets the flag that determines whether or not this
218: * text component is editable.
219: * <p>
220: * If the flag is set to <code>true</code>, this text component
221: * becomes user editable. If the flag is set to <code>false</code>,
222: * the user cannot change the text of this text component.
223: * @param t a flag indicating whether this text component
224: * should be user editable.
225: * @see java.awt.TextComponent#isEditable
226: * @since JDK1.0
227: */
228: public synchronized void setEditable(boolean b) {
229: editable = b;
230: TextComponentPeer peer = (TextComponentPeer) this .peer;
231: if (peer != null) {
232: peer.setEditable(b);
233: }
234: }
235:
236: /**
237: * Gets the background color of this text component.
238: *
239: * By default, non-editable text components have a background color
240: * of SystemColor.control. This default can be overridden by
241: * calling setBackground.
242: *
243: * @return This text component's background color.
244: * If this text component does not have a background color,
245: * the background color of its parent is returned.
246: * @see setBackground
247: * @since JDK1.0
248: */
249: public Color getBackground() {
250: if (!editable && !backgroundSetByClientCode) {
251: return SystemColor.control;
252: }
253:
254: return super .getBackground();
255: }
256:
257: /**
258: * Sets the background color of this text component.
259: *
260: * @param c The color to become this text component's color.
261: * If this parameter is null then this text component
262: * will inherit the background color of its parent.
263: * @see #getBackground
264: * @since JDK1.0
265: */
266: public void setBackground(Color c) {
267: backgroundSetByClientCode = true;
268: super .setBackground(c);
269: }
270:
271: /**
272: * Gets the start position of the selected text in
273: * this text component.
274: * @return the start position of the selected text.
275: * @see java.awt.TextComponent#setSelectionStart
276: * @see java.awt.TextComponent#getSelectionEnd
277: * @since JDK1.0
278: */
279: public synchronized int getSelectionStart() {
280: TextComponentPeer peer = (TextComponentPeer) this .peer;
281: if (peer != null) {
282: selectionStart = peer.getSelectionStart();
283: }
284: return selectionStart;
285: }
286:
287: /**
288: * Sets the selection start for this text component to
289: * the specified position. The new start point is constrained
290: * to be at or before the current selection end. It also
291: * cannot be set to less than zero, the beginning of the
292: * component's text.
293: * If the caller supplies a value for <code>selectionStart</code>
294: * that is out of bounds, the method enforces these constraints
295: * silently, and without failure.
296: * @param selectionStart the start position of the
297: * selected text.
298: * @see java.awt.TextComponent#getSelectionStart
299: * @see java.awt.TextComponent#setSelectionEnd
300: * @since JDK1.1
301: */
302: public synchronized void setSelectionStart(int selectionStart) {
303: /* Route through select method to enforce consistent policy
304: * between selectionStart and selectionEnd.
305: */
306: select(selectionStart, getSelectionEnd());
307: }
308:
309: /**
310: * Gets the end position of the selected text in
311: * this text component.
312: * @return the end position of the selected text.
313: * @see java.awt.TextComponent#setSelectionEnd
314: * @see java.awt.TextComponent#getSelectionStart
315: * @since JDK1.0
316: */
317: public synchronized int getSelectionEnd() {
318: TextComponentPeer peer = (TextComponentPeer) this .peer;
319: if (peer != null) {
320: selectionEnd = peer.getSelectionEnd();
321: }
322: return selectionEnd;
323: }
324:
325: /**
326: * Sets the selection end for this text component to
327: * the specified position. The new end point is constrained
328: * to be at or after the current selection start. It also
329: * cannot be set beyond the end of the component's text.
330: * If the caller supplies a value for <code>selectionEnd</code>
331: * that is out of bounds, the method enforces these constraints
332: * silently, and without failure.
333: * @param selectionEnd the end position of the
334: * selected text.
335: * @see java.awt.TextComponent#getSelectionEnd
336: * @see java.awt.TextComponent#setSelectionStart
337: * @since JDK1.1
338: */
339: public synchronized void setSelectionEnd(int selectionEnd) {
340: /* Route through select method to enforce consistent policy
341: * between selectionStart and selectionEnd.
342: */
343: select(getSelectionStart(), selectionEnd);
344: }
345:
346: /**
347: * Selects the text between the specified start and end positions.
348: * <p>
349: * This method sets the start and end positions of the
350: * selected text, enforcing the restriction that the end
351: * position must be greater than or equal to the start position.
352: * The start position must be greater than zero, and the
353: * end position must be less that or equal to the length
354: * of the text component's text. If the caller supplies values
355: * that are inconsistent or out of bounds, the method enforces
356: * these constraints silently, and without failure.
357: * @param selectionStart the start position of the
358: * text to select.
359: * @param selectionEnd the end position of the
360: * text to select.
361: * @see java.awt.TextComponent#setSelectionStart
362: * @see java.awt.TextComponent#setSelectionEnd
363: * @see java.awt.TextComponent#selectAll
364: * @since JDK1.0
365: */
366: public synchronized void select(int selectionStart, int selectionEnd) {
367: String text = getText();
368: if (selectionStart < 0) {
369: selectionStart = 0;
370: }
371: if (selectionStart > text.length()) {
372: selectionStart = text.length();
373: }
374: if (selectionEnd > text.length()) {
375: selectionEnd = text.length();
376: }
377: if (selectionEnd < selectionStart) {
378: selectionEnd = selectionStart;
379: }
380: this .selectionStart = selectionStart;
381: this .selectionEnd = selectionEnd;
382: TextComponentPeer peer = (TextComponentPeer) this .peer;
383: if (peer != null) {
384: peer.select(selectionStart, selectionEnd);
385: }
386: }
387:
388: /**
389: * Selects all the text in this text component.
390: * @see java.awt.TextComponent#select
391: * @since JDK1.0
392: */
393: public synchronized void selectAll() {
394: String text = getText();
395: this .selectionStart = 0;
396: this .selectionEnd = getText().length();
397: TextComponentPeer peer = (TextComponentPeer) this .peer;
398: if (peer != null) {
399: peer.select(selectionStart, selectionEnd);
400: }
401: }
402:
403: /**
404: * Sets the position of the text insertion caret for
405: * this text component.
406: *
407: * @param position the position of the text insertion caret.
408: * @exception IllegalArgumentException if the value supplied
409: * for <code>position</code> is less than zero.
410: * @since JDK1.1
411: */
412: public synchronized void setCaretPosition(int position) {
413: if (position < 0) {
414: throw new IllegalArgumentException(
415: "position less than zero.");
416: }
417: int maxposition = getText().length();
418: if (position > maxposition) {
419: position = maxposition;
420: }
421: TextComponentPeer peer = (TextComponentPeer) this .peer;
422: if (peer != null) {
423: peer.setCaretPosition(position);
424: } else {
425: throw new IllegalComponentStateException(
426: "Cannot set caret position until after the peer has been created");
427: }
428: }
429:
430: /**
431: * Gets the position of the text insertion caret for
432: * this text component.
433: * @return the position of the text insertion caret.
434: * @since JDK1.1
435: */
436: public synchronized int getCaretPosition() {
437: TextComponentPeer peer = (TextComponentPeer) this .peer;
438: int position = 0;
439: if (peer != null) {
440: position = peer.getCaretPosition();
441: }
442: return position;
443: }
444:
445: /**
446: * Adds the specified text event listener to recieve text events
447: * from this textcomponent.
448: * @param l the text event listener
449: */
450: public synchronized void addTextListener(TextListener l) {
451: textListener = AWTEventMulticaster.add(textListener, l);
452: newEventsOnly = true;
453: }
454:
455: /**
456: * Removes the specified text event listener so that it no longer
457: * receives text events from this textcomponent
458: */
459: public synchronized void removeTextListener(TextListener l) {
460: textListener = AWTEventMulticaster.remove(textListener, l);
461: }
462:
463: /**
464: * Returns an array of all the text listeners
465: * registered on this text component.
466: *
467: * @return all of this text component's <code>TextListener</code>s
468: * or an empty array if no text
469: * listeners are currently registered
470: *
471: *
472: * @see #addTextListener
473: * @see #removeTextListener
474: * @since 1.4
475: */
476: public synchronized TextListener[] getTextListeners() {
477: return (TextListener[]) AWTEventMulticaster.getListeners(
478: (EventListener) textListener, TextListener.class);
479: }
480:
481: // NOTE: remove when filtering is done at lower level
482: boolean eventEnabled(AWTEvent e) {
483: if (e.id == TextEvent.TEXT_VALUE_CHANGED) {
484: if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0
485: || textListener != null) {
486: return true;
487: }
488: return false;
489: }
490: return super .eventEnabled(e);
491: }
492:
493: /**
494: * Processes events on this textcomponent. If the event is a
495: * TextEvent, it invokes the processTextEvent method,
496: * else it invokes its superclass's processEvent.
497: * @param e the event
498: */
499: protected void processEvent(AWTEvent e) {
500: if (e instanceof TextEvent) {
501: processTextEvent((TextEvent) e);
502: return;
503: }
504: super .processEvent(e);
505: }
506:
507: /**
508: * Processes text events occurring on this text component by
509: * dispatching them to any registered TextListener objects.
510: * NOTE: This method will not be called unless text events
511: * are enabled for this component; this happens when one of the
512: * following occurs:
513: * a) A TextListener object is registered via addTextListener()
514: * b) Text events are enabled via enableEvents()
515: * @see Component#enableEvents
516: * @param e the text event
517: */
518: protected void processTextEvent(TextEvent e) {
519: if (textListener != null) {
520: int id = e.getID();
521: switch (id) {
522: case TextEvent.TEXT_VALUE_CHANGED:
523: textListener.textValueChanged(e);
524: break;
525: }
526: }
527: }
528:
529: /**
530: * Returns the parameter string representing the state of this text
531: * component. This string is useful for debugging.
532: * @return the parameter string of this text component.
533: * @since JDK1.0
534: */
535: protected String paramString() {
536: String str = super .paramString() + ",text=" + getText();
537: if (editable) {
538: str += ",editable";
539: }
540: return str + ",selection=" + getSelectionStart() + "-"
541: + getSelectionEnd();
542: }
543:
544: /*
545: * Serialization support. Since the value of the fields
546: * selectionStart, and selectionEnd, and text aren't neccessarily
547: * up to date we sync them up with the peer before serializing.
548: */
549:
550: private int textComponentSerializedDataVersion = 1;
551:
552: private void writeObject(java.io.ObjectOutputStream s)
553: throws java.io.IOException {
554: TextComponentPeer peer = (TextComponentPeer) this .peer;
555: if (peer != null) {
556: text = peer.getText();
557: selectionStart = peer.getSelectionStart();
558: selectionEnd = peer.getSelectionEnd();
559: }
560: s.defaultWriteObject();
561: AWTEventMulticaster.save(s, textListenerK, textListener);
562: s.writeObject(null);
563: }
564:
565: private void readObject(ObjectInputStream s)
566: throws ClassNotFoundException, IOException {
567: s.defaultReadObject();
568: Object keyOrNull;
569: while (null != (keyOrNull = s.readObject())) {
570: String key = ((String) keyOrNull).intern();
571: if (textListenerK == key)
572: addTextListener((TextListener) (s.readObject()));
573: else
574: // skip value for unrecognized key
575: s.readObject();
576: }
577: }
578:
579: private boolean checkForEnableIM = true;
580: }
|