1 /* TextComponent.java -- Widgets for entering text
2 Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.event.TextEvent;
42 import java.awt.event.TextListener;
43 import java.awt.peer.TextComponentPeer;
44 import java.io.Serializable;
45 import java.text.BreakIterator;
46 import java.util.EventListener;
48 import javax.accessibility.Accessible;
49 import javax.accessibility.AccessibleContext;
50 import javax.accessibility.AccessibleRole;
51 import javax.accessibility.AccessibleState;
52 import javax.accessibility.AccessibleStateSet;
53 import javax.accessibility.AccessibleText;
54 import javax.swing.text.AttributeSet;
57 * This class provides common functionality for widgets than
60 * @author Aaron M. Renn (arenn@urbanophile.com)
62 public class TextComponent extends Component
63 implements Serializable, Accessible
70 // Constant for serialization
71 private static final long serialVersionUID = -2214773872412987419L;
78 * @serial Indicates whether or not this component is editable.
79 * This is package-private to avoid an accessor method.
84 * @serial The starting position of the selected text region.
85 * This is package-private to avoid an accessor method.
90 * @serial The ending position of the selected text region.
91 * This is package-private to avoid an accessor method.
96 * @serial The text in the component
97 * This is package-private to avoid an accessor method.
102 * A list of listeners that will receive events from this object.
104 protected transient TextListener textListener;
106 protected class AccessibleAWTTextComponent
107 extends AccessibleAWTComponent
108 implements AccessibleText, TextListener
111 // Adds a listener for tracking caret changes
112 public AccessibleAWTTextComponent()
114 TextComponent.this.addTextListener(this);
117 public AccessibleRole getAccessibleRole()
119 return AccessibleRole.TEXT;
122 public AccessibleStateSet getAccessibleStateSet()
124 // TODO: Docs say PropertyChangeEvent will fire if this state changes.
125 // That means that the event has to fire when editable changes.
126 AccessibleStateSet ss = super.getAccessibleStateSet();
128 ss.add(AccessibleState.EDITABLE);
132 public AccessibleText getAccessibleText()
138 * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point)
140 public int getIndexAtPoint(Point point)
142 return TextComponent.this.getIndexAtPoint(point);
146 * @see javax.accessibility.AccessibleText#getCharacterBounds(int)
148 public Rectangle getCharacterBounds(int index)
150 return TextComponent.this.getCharacterBounds(index);
154 * @see javax.accessibility.AccessibleText#getCharCount()
156 public int getCharCount()
158 return text.length();
162 * @see javax.accessibility.AccessibleText#getCaretPosition()
164 public int getCaretPosition()
166 return TextComponent.this.getCaretPosition();
170 * @see javax.accessibility.AccessibleText#getAtIndex(int, int)
172 public String getAtIndex(int part, int index)
174 if (index < 0 || index >= text.length())
176 BreakIterator it = null;
180 return text.substring(index, index + 1);
182 it = BreakIterator.getWordInstance();
185 it = BreakIterator.getSentenceInstance();
192 if (!it.isBoundary(index))
193 start = it.preceding(index);
194 int end = it.following(index);
196 return text.substring(index);
198 return text.substring(index, end);
202 * @see javax.accessibility.AccessibleText#getAfterIndex(int, int)
204 public String getAfterIndex(int part, int index) {
205 if (index < 0 || index >= text.length())
207 BreakIterator it = null;
211 return text.substring(index, index + 1);
213 it = BreakIterator.getWordInstance();
216 it = BreakIterator.getSentenceInstance();
223 if (!it.isBoundary(index))
224 start = it.following(index);
225 // Make sure there was a complete unit. I.e. if index is in the middle
226 // of a word, return null if there is no word after the that one.
229 int end = it.following(start);
231 return text.substring(index);
233 return text.substring(index, end);
237 * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int)
239 public String getBeforeIndex(int part, int index)
241 if (index < 1 || index >= text.length())
243 BreakIterator it = null;
247 return text.substring(index - 1, index);
249 it = BreakIterator.getWordInstance();
252 it = BreakIterator.getSentenceInstance();
259 if (!it.isBoundary(index))
260 end = it.preceding(index);
261 // Make sure there was a complete unit. I.e. if index is in the middle
262 // of a word, return null if there is no word before that one.
265 int start = it.preceding(end);
267 return text.substring(0, end);
269 return text.substring(start, end);
273 * @see javax.accessibility.AccessibleText#getCharacterAttribute(int)
275 public AttributeSet getCharacterAttribute(int index)
277 // FIXME: I suspect this really gets filled in by subclasses.
282 * @see javax.accessibility.AccessibleText#getSelectionStart()
284 public int getSelectionStart() {
285 // TODO Auto-generated method stub
286 return selectionStart;
290 * @see javax.accessibility.AccessibleText#getSelectionEnd()
292 public int getSelectionEnd()
298 * @see javax.accessibility.AccessibleText#getSelectedText()
300 public String getSelectedText()
302 if (selectionEnd - selectionStart > 0)
303 return text.substring(selectionStart, selectionEnd);
309 * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent)
311 public void textValueChanged(TextEvent event)
313 // TODO Auto-generated method stub
319 /*************************************************************************/
325 TextComponent(String text)
328 this.editable = true;
331 /*************************************************************************/
338 * Returns the text in this component
340 * @return The text in this component.
342 public synchronized String
345 TextComponentPeer tcp = (TextComponentPeer)getPeer();
347 text = tcp.getText();
352 /*************************************************************************/
355 * Sets the text in this component to the specified string.
357 * @param text The new text for this component.
359 public synchronized void
367 TextComponentPeer tcp = (TextComponentPeer)getPeer();
373 /*************************************************************************/
376 * Returns a string that contains the text that is currently selected.
378 * @return The currently selected text region.
380 public synchronized String
383 String alltext = getText();
384 int start = getSelectionStart();
385 int end = getSelectionEnd();
387 return(alltext.substring(start, end));
390 /*************************************************************************/
393 * Returns the starting position of the selected text region.
394 * If the text is not selected then caret position is returned.
396 * @return The starting position of the selected text region.
398 public synchronized int
401 TextComponentPeer tcp = (TextComponentPeer)getPeer();
403 selectionStart = tcp.getSelectionStart();
405 return(selectionStart);
408 /*************************************************************************/
411 * Sets the starting position of the selected region to the
412 * specified value. If the specified value is out of range, then it
413 * will be silently changed to the nearest legal value.
415 * @param selectionStart The new start position for selected text.
417 public synchronized void
418 setSelectionStart(int selectionStart)
420 select(selectionStart, getSelectionEnd());
423 /*************************************************************************/
426 * Returns the ending position of the selected text region.
427 * If the text is not selected, then caret position is returned
429 * @return The ending position of the selected text region.
431 public synchronized int
434 TextComponentPeer tcp = (TextComponentPeer)getPeer();
436 selectionEnd = tcp.getSelectionEnd();
438 return(selectionEnd);
441 /*************************************************************************/
444 * Sets the ending position of the selected region to the
445 * specified value. If the specified value is out of range, then it
446 * will be silently changed to the nearest legal value.
448 * @param selectionEnd The new start position for selected text.
450 public synchronized void
451 setSelectionEnd(int selectionEnd)
453 select(getSelectionStart(), selectionEnd);
456 /*************************************************************************/
459 * This method sets the selected text range to the text between the
460 * specified start and end positions. Illegal values for these
461 * positions are silently fixed.
463 * @param selectionStart The new start position for the selected text.
464 * @param selectionEnd The new end position for the selected text.
466 public synchronized void
467 select(int selectionStart, int selectionEnd)
469 if (selectionStart < 0)
472 if (selectionStart > getText().length())
473 selectionStart = text.length();
475 if (selectionEnd > text.length())
476 selectionEnd = text.length();
478 if (selectionStart > selectionEnd)
479 selectionStart = selectionEnd;
481 this.selectionStart = selectionStart;
482 this.selectionEnd = selectionEnd;
484 TextComponentPeer tcp = (TextComponentPeer)getPeer();
486 tcp.select(selectionStart, selectionEnd);
489 /*************************************************************************/
492 * Selects all of the text in the component.
494 public synchronized void
497 select(0, getText().length());
500 /*************************************************************************/
503 * Returns the current caret position in the text.
505 * @return The caret position in the text.
507 public synchronized int
510 TextComponentPeer tcp = (TextComponentPeer)getPeer();
512 return(tcp.getCaretPosition());
517 /*************************************************************************/
520 * Sets the caret position to the specified value.
522 * @param caretPosition The new caret position.
524 * @exception IllegalArgumentException If the value supplied for position
529 public synchronized void
530 setCaretPosition(int caretPosition)
532 if (caretPosition < 0)
533 throw new IllegalArgumentException ();
535 TextComponentPeer tcp = (TextComponentPeer)getPeer();
537 tcp.setCaretPosition(caretPosition);
540 /*************************************************************************/
543 * Tests whether or not this component's text can be edited.
545 * @return <code>true</code> if the text can be edited, <code>false</code>
554 /*************************************************************************/
557 * Sets whether or not this component's text can be edited.
559 * @param editable <code>true</code> to enable editing of the text,
560 * <code>false</code> to disable it.
562 public synchronized void
563 setEditable(boolean editable)
565 this.editable = editable;
567 TextComponentPeer tcp = (TextComponentPeer)getPeer();
569 tcp.setEditable(editable);
572 /*************************************************************************/
575 * Notifies the component that it should destroy its native peer.
580 super.removeNotify();
583 /*************************************************************************/
586 * Adds a new listener to the list of text listeners for this
589 * @param listener The listener to be added.
591 public synchronized void
592 addTextListener(TextListener listener)
594 textListener = AWTEventMulticaster.add(textListener, listener);
596 enableEvents(AWTEvent.TEXT_EVENT_MASK);
599 /*************************************************************************/
602 * Removes the specified listener from the list of listeners
603 * for this component.
605 * @param listener The listener to remove.
607 public synchronized void
608 removeTextListener(TextListener listener)
610 textListener = AWTEventMulticaster.remove(textListener, listener);
613 /*************************************************************************/
616 * Processes the specified event for this component. Text events are
617 * processed by calling the <code>processTextEvent()</code> method.
618 * All other events are passed to the superclass method.
620 * @param event The event to process.
623 processEvent(AWTEvent event)
625 if (event instanceof TextEvent)
626 processTextEvent((TextEvent)event);
628 super.processEvent(event);
631 /*************************************************************************/
634 * Processes the specified text event by dispatching it to any listeners
635 * that are registered. Note that this method will only be called
636 * if text event's are enabled. This will be true if there are any
637 * registered listeners, or if the event has been specifically
638 * enabled using <code>enableEvents()</code>.
640 * @param event The text event to process.
643 processTextEvent(TextEvent event)
645 if (textListener != null)
646 textListener.textValueChanged(event);
650 dispatchEventImpl(AWTEvent e)
652 if (e.id <= TextEvent.TEXT_LAST
653 && e.id >= TextEvent.TEXT_FIRST
654 && (textListener != null
655 || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0))
658 super.dispatchEventImpl(e);
661 /*************************************************************************/
664 * Returns a debugging string.
666 * @return A debugging string.
671 return(getClass().getName() + "(text=" + getText() + ")");
675 * Returns an array of all the objects currently registered as FooListeners
676 * upon this <code>TextComponent</code>. FooListeners are registered using
677 * the addFooListener method.
679 * @exception ClassCastException If listenerType doesn't specify a class or
680 * interface that implements java.util.EventListener.
682 public EventListener[] getListeners (Class listenerType)
684 if (listenerType == TextListener.class)
685 return AWTEventMulticaster.getListeners (textListener, listenerType);
687 return super.getListeners (listenerType);
691 * Returns all text listeners registered to this object.
693 public TextListener[] getTextListeners ()
695 return (TextListener[]) getListeners (TextListener.class);
699 * Gets the AccessibleContext associated with this <code>TextComponent</code>.
700 * The context is created, if necessary.
702 * @return the associated context
704 public AccessibleContext getAccessibleContext()
706 /* Create the context if this is the first request */
707 if (accessibleContext == null)
708 accessibleContext = new AccessibleAWTTextComponent();
709 return accessibleContext;
713 /*******************************/
714 // Provide AccessibleAWTTextComponent access to several peer functions that
715 // aren't publicly exposed. This is package-private to avoid an accessor
718 getIndexAtPoint(Point p)
720 TextComponentPeer tcp = (TextComponentPeer)getPeer();
722 return tcp.getIndexAtPoint(p.x, p.y);
726 synchronized Rectangle
727 getCharacterBounds(int i)
729 TextComponentPeer tcp = (TextComponentPeer)getPeer();
731 return tcp.getCharacterBounds(i);
738 } // class TextComponent