OSDN Git Service

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