OSDN Git Service

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