OSDN Git Service

Imported Classpath 0.18.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / awt / TextComponent.java
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     // Constructor
111     // Adds a listener for tracking caret changes
112     public AccessibleAWTTextComponent()
113     {
114       TextComponent.this.addTextListener(this);
115     }
116     
117     public AccessibleRole getAccessibleRole()
118     {
119       return AccessibleRole.TEXT;
120     }
121     
122     public AccessibleStateSet getAccessibleStateSet()
123     {
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();
127       if (editable)
128         ss.add(AccessibleState.EDITABLE);
129       return ss;
130     }
131     
132     public AccessibleText getAccessibleText()
133     {
134       return this;
135     }
136     
137     /* (non-Javadoc)
138      * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point)
139      */
140     public int getIndexAtPoint(Point point)
141     {
142       return TextComponent.this.getIndexAtPoint(point);
143     }
144
145     /* (non-Javadoc)
146      * @see javax.accessibility.AccessibleText#getCharacterBounds(int)
147      */
148     public Rectangle getCharacterBounds(int index)
149     {
150       return TextComponent.this.getCharacterBounds(index);
151     }
152
153     /* (non-Javadoc)
154      * @see javax.accessibility.AccessibleText#getCharCount()
155      */
156     public int getCharCount()
157     {
158       return text.length();
159     }
160
161     /* (non-Javadoc)
162      * @see javax.accessibility.AccessibleText#getCaretPosition()
163      */
164     public int getCaretPosition()
165     {
166       return TextComponent.this.getCaretPosition();
167     }
168
169     /* (non-Javadoc)
170      * @see javax.accessibility.AccessibleText#getAtIndex(int, int)
171      */
172     public String getAtIndex(int part, int index)
173     {
174       if (index < 0 || index >= text.length())
175         return null;
176       BreakIterator it = null;
177       switch (part)
178       {
179         case CHARACTER:
180           return text.substring(index, index + 1);
181         case WORD:
182           it = BreakIterator.getWordInstance();
183           break;
184         case SENTENCE:
185           it = BreakIterator.getSentenceInstance();
186           break;
187         default:
188           return null;
189       }
190           it.setText(text);
191           int start = index;
192           if (!it.isBoundary(index))
193             start = it.preceding(index); 
194           int end = it.following(index);
195           if (end == -1)
196             return text.substring(index);
197           else
198             return text.substring(index, end);
199     }
200
201     /* (non-Javadoc)
202      * @see javax.accessibility.AccessibleText#getAfterIndex(int, int)
203      */
204     public String getAfterIndex(int part, int index) {
205       if (index < 0 || index >= text.length())
206         return null;
207       BreakIterator it = null;
208       switch (part)
209       {
210         case CHARACTER:
211           return text.substring(index, index + 1);
212         case WORD:
213           it = BreakIterator.getWordInstance();
214           break;
215         case SENTENCE:
216           it = BreakIterator.getSentenceInstance();
217           break;
218         default:
219           return null;
220       }
221           it.setText(text);
222           int start = index;
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.
227           if (start == -1)
228             return null;
229           int end = it.following(start);
230           if (end == -1)
231             return text.substring(index);
232           else
233             return text.substring(index, end);
234     }
235
236     /* (non-Javadoc)
237      * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int)
238      */
239     public String getBeforeIndex(int part, int index)
240     {
241       if (index < 1 || index >= text.length())
242         return null;
243       BreakIterator it = null;
244       switch (part)
245       {
246         case CHARACTER:
247           return text.substring(index - 1, index);
248         case WORD:
249           it = BreakIterator.getWordInstance();
250           break;
251         case SENTENCE:
252           it = BreakIterator.getSentenceInstance();
253           break;
254         default:
255           return null;
256       }
257           it.setText(text);
258           int end = index;
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.
263           if (end == -1)
264             return null;
265           int start = it.preceding(end);
266           if (start == -1)
267             return text.substring(0, end);
268           else
269             return text.substring(start, end);
270     }
271
272     /* (non-Javadoc)
273      * @see javax.accessibility.AccessibleText#getCharacterAttribute(int)
274      */
275     public AttributeSet getCharacterAttribute(int index)
276     {
277       // FIXME: I suspect this really gets filled in by subclasses.
278       return null;
279     }
280
281     /* (non-Javadoc)
282      * @see javax.accessibility.AccessibleText#getSelectionStart()
283      */
284     public int getSelectionStart() {
285       // TODO Auto-generated method stub
286       return selectionStart;
287     }
288
289     /* (non-Javadoc)
290      * @see javax.accessibility.AccessibleText#getSelectionEnd()
291      */
292     public int getSelectionEnd()
293     {
294       return selectionEnd;
295     }
296
297     /* (non-Javadoc)
298      * @see javax.accessibility.AccessibleText#getSelectedText()
299      */
300     public String getSelectedText()
301     {
302       if (selectionEnd - selectionStart > 0)
303         return text.substring(selectionStart, selectionEnd);
304       else
305         return null;
306     }
307
308     /* (non-Javadoc)
309      * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent)
310      */
311     public void textValueChanged(TextEvent event)
312     {
313       // TODO Auto-generated method stub
314       
315     }
316     
317   }
318
319 /*************************************************************************/
320
321 /*
322  * Constructors
323  */
324
325 TextComponent(String text)
326 {
327   this.text = text;
328   this.editable = true;
329 }
330
331 /*************************************************************************/
332
333 /*
334  * Instance Methods
335  */
336
337 /**
338   * Returns the text in this component
339   *
340   * @return The text in this component.
341   */
342 public synchronized String
343 getText()
344 {
345   TextComponentPeer tcp = (TextComponentPeer)getPeer();
346   if (tcp != null)
347     text = tcp.getText();
348
349   return(text);
350 }
351
352 /*************************************************************************/
353
354 /**
355   * Sets the text in this component to the specified string.
356   *
357   * @param text The new text for this component.
358   */
359 public synchronized void
360 setText(String text)
361 {
362   if (text == null)
363     text = "";
364
365   this.text = text;
366
367   TextComponentPeer tcp = (TextComponentPeer)getPeer();
368   if (tcp != null)
369     tcp.setText(text);
370   setCaretPosition(0);
371 }
372
373 /*************************************************************************/
374
375 /**
376   * Returns a string that contains the text that is currently selected.
377   *
378   * @return The currently selected text region.
379   */
380 public synchronized String
381 getSelectedText()
382 {
383   String alltext = getText();
384   int start = getSelectionStart();
385   int end = getSelectionEnd();
386   
387   return(alltext.substring(start, end));
388 }
389
390 /*************************************************************************/
391
392 /**
393   * Returns the starting position of the selected text region.
394   * If the text is not selected then caret position is returned. 
395   *
396   * @return The starting position of the selected text region.
397   */
398 public synchronized int
399 getSelectionStart()
400 {
401   TextComponentPeer tcp = (TextComponentPeer)getPeer();
402   if (tcp != null)
403     selectionStart = tcp.getSelectionStart();
404
405   return(selectionStart);
406 }
407
408 /*************************************************************************/
409
410 /**
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.
414   *
415   * @param selectionStart The new start position for selected text.
416   */
417 public synchronized void
418 setSelectionStart(int selectionStart)
419 {
420   select(selectionStart, getSelectionEnd());
421 }
422
423 /*************************************************************************/
424
425 /**
426   * Returns the ending position of the selected text region.
427   * If the text is not selected, then caret position is returned 
428   *
429   * @return The ending position of the selected text region.
430   */
431 public synchronized int
432 getSelectionEnd()
433 {
434   TextComponentPeer tcp = (TextComponentPeer)getPeer();
435   if (tcp != null)
436     selectionEnd = tcp.getSelectionEnd();
437
438   return(selectionEnd);
439 }
440
441 /*************************************************************************/
442
443 /**
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.
447   *
448   * @param selectionEnd The new start position for selected text.
449   */
450 public synchronized void
451 setSelectionEnd(int selectionEnd)
452 {
453   select(getSelectionStart(), selectionEnd);
454 }
455
456 /*************************************************************************/
457
458 /**
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.
462   *
463   * @param selectionStart The new start position for the selected text.
464   * @param selectionEnd The new end position for the selected text.
465   */
466 public synchronized void
467 select(int selectionStart, int selectionEnd)
468 {
469   if (selectionStart < 0)
470     selectionStart = 0;
471
472   if (selectionStart > getText().length())
473     selectionStart = text.length();
474
475   if (selectionEnd > text.length())
476     selectionEnd = text.length();
477
478   if (selectionStart > selectionEnd)
479     selectionStart = selectionEnd;
480
481   this.selectionStart = selectionStart;
482   this.selectionEnd = selectionEnd;
483
484   TextComponentPeer tcp = (TextComponentPeer)getPeer();
485   if (tcp != null)
486     tcp.select(selectionStart, selectionEnd);
487 }
488
489 /*************************************************************************/
490
491 /**
492   * Selects all of the text in the component.
493   */
494 public synchronized void
495 selectAll()
496 {
497   select(0, getText().length());
498 }
499
500 /*************************************************************************/
501
502 /**
503   * Returns the current caret position in the text.
504   *
505   * @return The caret position in the text.
506   */
507 public synchronized int
508 getCaretPosition()
509 {
510   TextComponentPeer tcp = (TextComponentPeer)getPeer();
511   if (tcp != null)
512     return(tcp.getCaretPosition());
513   else
514     return(0);
515 }
516
517 /*************************************************************************/
518
519 /**
520   * Sets the caret position to the specified value.
521   *
522   * @param caretPosition The new caret position.
523   *
524   * @exception IllegalArgumentException If the value supplied for position
525   * is less than zero.
526   *
527   * @since 1.1
528   */
529 public synchronized void
530 setCaretPosition(int caretPosition)
531 {
532   if (caretPosition < 0)
533     throw new IllegalArgumentException ();
534   
535   TextComponentPeer tcp = (TextComponentPeer)getPeer();
536   if (tcp != null)
537     tcp.setCaretPosition(caretPosition);
538 }
539
540 /*************************************************************************/
541
542 /**
543   * Tests whether or not this component's text can be edited.
544   *
545   * @return <code>true</code> if the text can be edited, <code>false</code>
546   * otherwise.
547   */
548 public boolean
549 isEditable()
550 {
551   return(editable);
552 }
553
554 /*************************************************************************/
555
556 /**
557   * Sets whether or not this component's text can be edited.
558   *
559   * @param editable <code>true</code> to enable editing of the text,
560   * <code>false</code> to disable it.
561   */
562 public synchronized void
563 setEditable(boolean editable)
564 {
565   this.editable = editable;
566
567   TextComponentPeer tcp = (TextComponentPeer)getPeer();
568   if (tcp != null)
569     tcp.setEditable(editable);
570 }
571
572 /*************************************************************************/
573
574 /**
575   * Notifies the component that it should destroy its native peer.
576   */
577 public void
578 removeNotify()
579 {
580   super.removeNotify();
581 }
582
583 /*************************************************************************/
584
585 /**
586   * Adds a new listener to the list of text listeners for this
587   * component.
588   *
589   * @param listener The listener to be added.
590   */
591 public synchronized void
592 addTextListener(TextListener listener)
593 {
594   textListener = AWTEventMulticaster.add(textListener, listener);
595
596   enableEvents(AWTEvent.TEXT_EVENT_MASK);  
597 }
598
599 /*************************************************************************/
600
601 /**
602   * Removes the specified listener from the list of listeners
603   * for this component.
604   *
605   * @param listener The listener to remove.
606   */
607 public synchronized void
608 removeTextListener(TextListener listener)
609 {
610   textListener = AWTEventMulticaster.remove(textListener, listener);
611 }
612
613 /*************************************************************************/
614
615 /**
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.
619   * 
620   * @param event The event to process.
621   */
622 protected void
623 processEvent(AWTEvent event)
624 {
625   if (event instanceof TextEvent)
626     processTextEvent((TextEvent)event);
627   else
628     super.processEvent(event);
629 }
630
631 /*************************************************************************/
632
633 /**
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>.
639   *
640   * @param event The text event to process.
641   */
642 protected void
643 processTextEvent(TextEvent event)
644 {
645   if (textListener != null)
646     textListener.textValueChanged(event);
647 }
648
649 void
650 dispatchEventImpl(AWTEvent e)
651 {
652   if (e.id <= TextEvent.TEXT_LAST 
653       && e.id >= TextEvent.TEXT_FIRST
654       && (textListener != null 
655           || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0))
656     processEvent(e);
657   else
658     super.dispatchEventImpl(e);
659 }
660
661 /*************************************************************************/
662
663 /**
664   * Returns a debugging string.
665   *
666   * @return A debugging string.
667   */
668 protected String
669 paramString()
670 {
671   return(getClass().getName() + "(text=" + getText() + ")");
672 }
673
674   /**
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.
678    *
679    * @exception ClassCastException If listenerType doesn't specify a class or
680    * interface that implements java.util.EventListener.
681    */
682   public EventListener[] getListeners (Class listenerType)
683   {
684     if (listenerType == TextListener.class)
685       return AWTEventMulticaster.getListeners (textListener, listenerType);
686
687     return super.getListeners (listenerType);
688   }
689
690   /**
691    * Returns all text listeners registered to this object.
692    */
693   public TextListener[] getTextListeners ()
694   {
695     return (TextListener[]) getListeners (TextListener.class);
696   }
697
698   /**
699    * Gets the AccessibleContext associated with this <code>TextComponent</code>.
700    * The context is created, if necessary.
701    *
702    * @return the associated context
703    */
704   public AccessibleContext getAccessibleContext()
705   {
706     /* Create the context if this is the first request */
707     if (accessibleContext == null)
708       accessibleContext = new AccessibleAWTTextComponent();
709     return accessibleContext;
710   }
711
712   
713   /*******************************/
714   // Provide AccessibleAWTTextComponent access to several peer functions that
715   // aren't publicly exposed.  This is package-private to avoid an accessor
716   // method.
717   synchronized int
718   getIndexAtPoint(Point p)
719   {
720     TextComponentPeer tcp = (TextComponentPeer)getPeer();
721     if (tcp != null)
722       return tcp.getIndexAtPoint(p.x, p.y);
723     return -1;
724   }
725   
726   synchronized Rectangle
727   getCharacterBounds(int i)
728   {
729     TextComponentPeer tcp = (TextComponentPeer)getPeer();
730     if (tcp != null)
731       return tcp.getCharacterBounds(i);
732     return null;
733   }
734   
735   
736
737
738 } // class TextComponent
739