1 /* TextComponent.java -- Widgets for entering text
2 Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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
19 02110-1301 USA.
20
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
24 combination.
25
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. */
37
38
39 package java.awt;
40
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;
47
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;
55
56 /**
57 * This class provides common functionality for widgets than
58 * contain text.
59 *
60 * @author Aaron M. Renn (arenn@urbanophile.com)
61 */
62 public class TextComponent extends Component
63 implements Serializable, Accessible
64 {
65
66 /*
67 * Static Variables
68 */
69
70 // Constant for serialization
71 private static final long serialVersionUID = -2214773872412987419L;
72
73 /*
74 * Instance Variables
75 */
76
77 /**
78 * @serial Indicates whether or not this component is editable.
79 * This is package-private to avoid an accessor method.
80 */
81 boolean editable;
82
83 /**
84 * @serial The starting position of the selected text region.
85 * This is package-private to avoid an accessor method.
86 */
87 int selectionStart;
88
89 /**
90 * @serial The ending position of the selected text region.
91 * This is package-private to avoid an accessor method.
92 */
93 int selectionEnd;
94
95 /**
96 * @serial The text in the component
97 * This is package-private to avoid an accessor method.
98 */
99 String text;
100
101 /**
102 * A list of listeners that will receive events from this object.
103 */
104 protected transient TextListener textListener;
105
106 protected class AccessibleAWTTextComponent
107 extends AccessibleAWTComponent
108 implements AccessibleText, TextListener
109 {
110 private static final long serialVersionUID = 3631432373506317811L;
111
112 // Constructor
113 // Adds a listener for tracking caret changes
AccessibleAWTTextComponent()114 public AccessibleAWTTextComponent()
115 {
116 TextComponent.this.addTextListener(this);
117 }
118
getAccessibleRole()119 public AccessibleRole getAccessibleRole()
120 {
121 return AccessibleRole.TEXT;
122 }
123
getAccessibleStateSet()124 public AccessibleStateSet getAccessibleStateSet()
125 {
126 // TODO: Docs say PropertyChangeEvent will fire if this state changes.
127 // That means that the event has to fire when editable changes.
128 AccessibleStateSet ss = super.getAccessibleStateSet();
129 if (editable)
130 ss.add(AccessibleState.EDITABLE);
131 return ss;
132 }
133
getAccessibleText()134 public AccessibleText getAccessibleText()
135 {
136 return this;
137 }
138
139 /* (non-Javadoc)
140 * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point)
141 */
getIndexAtPoint(Point point)142 public int getIndexAtPoint(Point point)
143 {
144 return TextComponent.this.getIndexAtPoint(point);
145 }
146
147 /* (non-Javadoc)
148 * @see javax.accessibility.AccessibleText#getCharacterBounds(int)
149 */
getCharacterBounds(int index)150 public Rectangle getCharacterBounds(int index)
151 {
152 return TextComponent.this.getCharacterBounds(index);
153 }
154
155 /* (non-Javadoc)
156 * @see javax.accessibility.AccessibleText#getCharCount()
157 */
getCharCount()158 public int getCharCount()
159 {
160 return text.length();
161 }
162
163 /* (non-Javadoc)
164 * @see javax.accessibility.AccessibleText#getCaretPosition()
165 */
getCaretPosition()166 public int getCaretPosition()
167 {
168 return TextComponent.this.getCaretPosition();
169 }
170
171 /* (non-Javadoc)
172 * @see javax.accessibility.AccessibleText#getAtIndex(int, int)
173 */
getAtIndex(int part, int index)174 public String getAtIndex(int part, int index)
175 {
176 if (index < 0 || index >= text.length())
177 return null;
178 BreakIterator it = null;
179 switch (part)
180 {
181 case CHARACTER:
182 return text.substring(index, index + 1);
183 case WORD:
184 it = BreakIterator.getWordInstance();
185 break;
186 case SENTENCE:
187 it = BreakIterator.getSentenceInstance();
188 break;
189 default:
190 return null;
191 }
192 it.setText(text);
193 int start = index;
194 if (!it.isBoundary(index))
195 start = it.preceding(index);
196 int end = it.following(index);
197 if (end == -1)
198 return text.substring(index);
199 else
200 return text.substring(index, end);
201 }
202
203 /* (non-Javadoc)
204 * @see javax.accessibility.AccessibleText#getAfterIndex(int, int)
205 */
getAfterIndex(int part, int index)206 public String getAfterIndex(int part, int index) {
207 if (index < 0 || index >= text.length())
208 return null;
209 BreakIterator it = null;
210 switch (part)
211 {
212 case CHARACTER:
213 return text.substring(index, index + 1);
214 case WORD:
215 it = BreakIterator.getWordInstance();
216 break;
217 case SENTENCE:
218 it = BreakIterator.getSentenceInstance();
219 break;
220 default:
221 return null;
222 }
223 it.setText(text);
224 int start = index;
225 if (!it.isBoundary(index))
226 start = it.following(index);
227 // Make sure there was a complete unit. I.e. if index is in the middle
228 // of a word, return null if there is no word after the that one.
229 if (start == -1)
230 return null;
231 int end = it.following(start);
232 if (end == -1)
233 return text.substring(index);
234 else
235 return text.substring(index, end);
236 }
237
238 /* (non-Javadoc)
239 * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int)
240 */
getBeforeIndex(int part, int index)241 public String getBeforeIndex(int part, int index)
242 {
243 if (index < 1 || index >= text.length())
244 return null;
245 BreakIterator it = null;
246 switch (part)
247 {
248 case CHARACTER:
249 return text.substring(index - 1, index);
250 case WORD:
251 it = BreakIterator.getWordInstance();
252 break;
253 case SENTENCE:
254 it = BreakIterator.getSentenceInstance();
255 break;
256 default:
257 return null;
258 }
259 it.setText(text);
260 int end = index;
261 if (!it.isBoundary(index))
262 end = it.preceding(index);
263 // Make sure there was a complete unit. I.e. if index is in the middle
264 // of a word, return null if there is no word before that one.
265 if (end == -1)
266 return null;
267 int start = it.preceding(end);
268 if (start == -1)
269 return text.substring(0, end);
270 else
271 return text.substring(start, end);
272 }
273
274 /* (non-Javadoc)
275 * @see javax.accessibility.AccessibleText#getCharacterAttribute(int)
276 */
getCharacterAttribute(int index)277 public AttributeSet getCharacterAttribute(int index)
278 {
279 // FIXME: I suspect this really gets filled in by subclasses.
280 return null;
281 }
282
283 /* (non-Javadoc)
284 * @see javax.accessibility.AccessibleText#getSelectionStart()
285 */
getSelectionStart()286 public int getSelectionStart() {
287 // TODO Auto-generated method stub
288 return selectionStart;
289 }
290
291 /* (non-Javadoc)
292 * @see javax.accessibility.AccessibleText#getSelectionEnd()
293 */
getSelectionEnd()294 public int getSelectionEnd()
295 {
296 return selectionEnd;
297 }
298
299 /* (non-Javadoc)
300 * @see javax.accessibility.AccessibleText#getSelectedText()
301 */
getSelectedText()302 public String getSelectedText()
303 {
304 if (selectionEnd - selectionStart > 0)
305 return text.substring(selectionStart, selectionEnd);
306 else
307 return null;
308 }
309
310 /* (non-Javadoc)
311 * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent)
312 */
textValueChanged(TextEvent event)313 public void textValueChanged(TextEvent event)
314 {
315 // TODO Auto-generated method stub
316
317 }
318
319 }
320
321 /*************************************************************************/
322
323 /*
324 * Constructors
325 */
326
TextComponent(String text)327 TextComponent(String text)
328 {
329 this.text = text;
330 this.editable = true;
331 }
332
333 /*************************************************************************/
334
335 /*
336 * Instance Methods
337 */
338
339 /**
340 * Returns the text in this component
341 *
342 * @return The text in this component.
343 */
344 public synchronized String
getText()345 getText()
346 {
347 TextComponentPeer tcp = (TextComponentPeer)getPeer();
348 if (tcp != null)
349 text = tcp.getText();
350
351 return(text);
352 }
353
354 /*************************************************************************/
355
356 /**
357 * Sets the text in this component to the specified string.
358 *
359 * @param text The new text for this component.
360 */
361 public synchronized void
setText(String text)362 setText(String text)
363 {
364 if (text == null)
365 text = "";
366
367 this.text = text;
368
369 TextComponentPeer tcp = (TextComponentPeer)getPeer();
370 if (tcp != null)
371 tcp.setText(text);
372 setCaretPosition(0);
373 }
374
375 /*************************************************************************/
376
377 /**
378 * Returns a string that contains the text that is currently selected.
379 *
380 * @return The currently selected text region.
381 */
382 public synchronized String
getSelectedText()383 getSelectedText()
384 {
385 String alltext = getText();
386 int start = getSelectionStart();
387 int end = getSelectionEnd();
388
389 return(alltext.substring(start, end));
390 }
391
392 /*************************************************************************/
393
394 /**
395 * Returns the starting position of the selected text region.
396 * If the text is not selected then caret position is returned.
397 *
398 * @return The starting position of the selected text region.
399 */
400 public synchronized int
getSelectionStart()401 getSelectionStart()
402 {
403 TextComponentPeer tcp = (TextComponentPeer)getPeer();
404 if (tcp != null)
405 selectionStart = tcp.getSelectionStart();
406
407 return(selectionStart);
408 }
409
410 /*************************************************************************/
411
412 /**
413 * Sets the starting position of the selected region to the
414 * specified value. If the specified value is out of range, then it
415 * will be silently changed to the nearest legal value.
416 *
417 * @param selectionStart The new start position for selected text.
418 */
419 public synchronized void
setSelectionStart(int selectionStart)420 setSelectionStart(int selectionStart)
421 {
422 select(selectionStart, getSelectionEnd());
423 }
424
425 /*************************************************************************/
426
427 /**
428 * Returns the ending position of the selected text region.
429 * If the text is not selected, then caret position is returned
430 *
431 * @return The ending position of the selected text region.
432 */
433 public synchronized int
getSelectionEnd()434 getSelectionEnd()
435 {
436 TextComponentPeer tcp = (TextComponentPeer)getPeer();
437 if (tcp != null)
438 selectionEnd = tcp.getSelectionEnd();
439
440 return(selectionEnd);
441 }
442
443 /*************************************************************************/
444
445 /**
446 * Sets the ending position of the selected region to the
447 * specified value. If the specified value is out of range, then it
448 * will be silently changed to the nearest legal value.
449 *
450 * @param selectionEnd The new start position for selected text.
451 */
452 public synchronized void
setSelectionEnd(int selectionEnd)453 setSelectionEnd(int selectionEnd)
454 {
455 select(getSelectionStart(), selectionEnd);
456 }
457
458 /*************************************************************************/
459
460 /**
461 * This method sets the selected text range to the text between the
462 * specified start and end positions. Illegal values for these
463 * positions are silently fixed.
464 *
465 * @param selectionStart The new start position for the selected text.
466 * @param selectionEnd The new end position for the selected text.
467 */
468 public synchronized void
select(int selectionStart, int selectionEnd)469 select(int selectionStart, int selectionEnd)
470 {
471 if (selectionStart < 0)
472 selectionStart = 0;
473
474 if (selectionStart > getText().length())
475 selectionStart = text.length();
476
477 if (selectionEnd > text.length())
478 selectionEnd = text.length();
479
480 if (selectionStart > selectionEnd)
481 selectionStart = selectionEnd;
482
483 this.selectionStart = selectionStart;
484 this.selectionEnd = selectionEnd;
485
486 TextComponentPeer tcp = (TextComponentPeer)getPeer();
487 if (tcp != null)
488 tcp.select(selectionStart, selectionEnd);
489 }
490
491 /*************************************************************************/
492
493 /**
494 * Selects all of the text in the component.
495 */
496 public synchronized void
selectAll()497 selectAll()
498 {
499 select(0, getText().length());
500 }
501
502 /*************************************************************************/
503
504 /**
505 * Returns the current caret position in the text.
506 *
507 * @return The caret position in the text.
508 */
509 public synchronized int
getCaretPosition()510 getCaretPosition()
511 {
512 TextComponentPeer tcp = (TextComponentPeer)getPeer();
513 if (tcp != null)
514 return(tcp.getCaretPosition());
515 else
516 return(0);
517 }
518
519 /*************************************************************************/
520
521 /**
522 * Sets the caret position to the specified value.
523 *
524 * @param caretPosition The new caret position.
525 *
526 * @exception IllegalArgumentException If the value supplied for position
527 * is less than zero.
528 *
529 * @since 1.1
530 */
531 public synchronized void
setCaretPosition(int caretPosition)532 setCaretPosition(int caretPosition)
533 {
534 if (caretPosition < 0)
535 throw new IllegalArgumentException ();
536
537 TextComponentPeer tcp = (TextComponentPeer)getPeer();
538 if (tcp != null)
539 tcp.setCaretPosition(caretPosition);
540 }
541
542 /*************************************************************************/
543
544 /**
545 * Tests whether or not this component's text can be edited.
546 *
547 * @return <code>true</code> if the text can be edited, <code>false</code>
548 * otherwise.
549 */
550 public boolean
isEditable()551 isEditable()
552 {
553 return(editable);
554 }
555
556 /*************************************************************************/
557
558 /**
559 * Sets whether or not this component's text can be edited.
560 *
561 * @param editable <code>true</code> to enable editing of the text,
562 * <code>false</code> to disable it.
563 */
564 public synchronized void
setEditable(boolean editable)565 setEditable(boolean editable)
566 {
567 this.editable = editable;
568
569 TextComponentPeer tcp = (TextComponentPeer)getPeer();
570 if (tcp != null)
571 tcp.setEditable(editable);
572 }
573
574 /*************************************************************************/
575
576 /**
577 * Notifies the component that it should destroy its native peer.
578 */
579 public void
removeNotify()580 removeNotify()
581 {
582 super.removeNotify();
583 }
584
585 /*************************************************************************/
586
587 /**
588 * Adds a new listener to the list of text listeners for this
589 * component.
590 *
591 * @param listener The listener to be added.
592 */
593 public synchronized void
addTextListener(TextListener listener)594 addTextListener(TextListener listener)
595 {
596 textListener = AWTEventMulticaster.add(textListener, listener);
597
598 enableEvents(AWTEvent.TEXT_EVENT_MASK);
599 }
600
601 /*************************************************************************/
602
603 /**
604 * Removes the specified listener from the list of listeners
605 * for this component.
606 *
607 * @param listener The listener to remove.
608 */
609 public synchronized void
removeTextListener(TextListener listener)610 removeTextListener(TextListener listener)
611 {
612 textListener = AWTEventMulticaster.remove(textListener, listener);
613 }
614
615 /*************************************************************************/
616
617 /**
618 * Processes the specified event for this component. Text events are
619 * processed by calling the <code>processTextEvent()</code> method.
620 * All other events are passed to the superclass method.
621 *
622 * @param event The event to process.
623 */
624 protected void
processEvent(AWTEvent event)625 processEvent(AWTEvent event)
626 {
627 if (event instanceof TextEvent)
628 processTextEvent((TextEvent)event);
629 else
630 super.processEvent(event);
631 }
632
633 /*************************************************************************/
634
635 /**
636 * Processes the specified text event by dispatching it to any listeners
637 * that are registered. Note that this method will only be called
638 * if text event's are enabled. This will be true if there are any
639 * registered listeners, or if the event has been specifically
640 * enabled using <code>enableEvents()</code>.
641 *
642 * @param event The text event to process.
643 */
644 protected void
processTextEvent(TextEvent event)645 processTextEvent(TextEvent event)
646 {
647 if (textListener != null)
648 textListener.textValueChanged(event);
649 }
650
651 void
dispatchEventImpl(AWTEvent e)652 dispatchEventImpl(AWTEvent e)
653 {
654 if (e.id <= TextEvent.TEXT_LAST
655 && e.id >= TextEvent.TEXT_FIRST
656 && (textListener != null
657 || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0))
658 processEvent(e);
659 else
660 super.dispatchEventImpl(e);
661 }
662
663 /*************************************************************************/
664
665 /**
666 * Returns a debugging string.
667 *
668 * @return A debugging string.
669 */
670 protected String
paramString()671 paramString()
672 {
673 return(getClass().getName() + "(text=" + getText() + ")");
674 }
675
676 /**
677 * Returns an array of all the objects currently registered as FooListeners
678 * upon this <code>TextComponent</code>. FooListeners are registered using
679 * the addFooListener method.
680 *
681 * @exception ClassCastException If listenerType doesn't specify a class or
682 * interface that implements java.util.EventListener.
683 */
getListeners(Class listenerType)684 public EventListener[] getListeners (Class listenerType)
685 {
686 if (listenerType == TextListener.class)
687 return AWTEventMulticaster.getListeners (textListener, listenerType);
688
689 return super.getListeners (listenerType);
690 }
691
692 /**
693 * Returns all text listeners registered to this object.
694 */
getTextListeners()695 public TextListener[] getTextListeners ()
696 {
697 return (TextListener[]) getListeners (TextListener.class);
698 }
699
700 /**
701 * Gets the AccessibleContext associated with this <code>TextComponent</code>.
702 * The context is created, if necessary.
703 *
704 * @return the associated context
705 */
getAccessibleContext()706 public AccessibleContext getAccessibleContext()
707 {
708 /* Create the context if this is the first request */
709 if (accessibleContext == null)
710 accessibleContext = new AccessibleAWTTextComponent();
711 return accessibleContext;
712 }
713
714
715 /*******************************/
716 // Provide AccessibleAWTTextComponent access to several peer functions that
717 // aren't publicly exposed. This is package-private to avoid an accessor
718 // method.
719 synchronized int
getIndexAtPoint(Point p)720 getIndexAtPoint(Point p)
721 {
722 TextComponentPeer tcp = (TextComponentPeer)getPeer();
723 if (tcp != null)
724 return tcp.getIndexAtPoint(p.x, p.y);
725 return -1;
726 }
727
728 synchronized Rectangle
getCharacterBounds(int i)729 getCharacterBounds(int i)
730 {
731 TextComponentPeer tcp = (TextComponentPeer)getPeer();
732 if (tcp != null)
733 return tcp.getCharacterBounds(i);
734 return null;
735 }
736
737
738
739
740 } // class TextComponent
741
742