xref: /trunk/main/toolkit/test/accessibility/AccessibleTextHandler.java (revision 5226f0b8dea4ef117ddd2bc4948a8601d0ac403b)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 import com.sun.star.accessibility.AccessibleTextType;
24 import com.sun.star.accessibility.TextSegment;
25 import com.sun.star.accessibility.XAccessibleContext;
26 import com.sun.star.accessibility.XAccessibleText;
27 import com.sun.star.accessibility.XAccessibleEditableText;
28 
29 import com.sun.star.awt.Rectangle;
30 import com.sun.star.awt.Point;
31 import com.sun.star.uno.UnoRuntime;
32 import com.sun.star.lang.IndexOutOfBoundsException;
33 import com.sun.star.beans.PropertyValue;
34 
35 import java.awt.Container;
36 import java.awt.FlowLayout;
37 import java.awt.BorderLayout;
38 import java.awt.Color;
39 import java.awt.Component;
40 import java.awt.Graphics;
41 import java.awt.event.ActionListener;
42 import java.awt.event.ActionEvent;
43 import javax.swing.JDialog;
44 import javax.swing.JButton;
45 import javax.swing.JPanel;
46 import javax.swing.JLabel;
47 import javax.swing.Icon;
48 import javax.swing.JTextArea;
49 import javax.swing.JOptionPane;
50 import javax.swing.JCheckBox;
51 import javax.swing.JColorChooser;
52 import javax.swing.BoxLayout;
53 import javax.swing.text.JTextComponent;
54 
55 
56 class AccessibleTextHandler extends NodeHandler
57 {
createHandler(XAccessibleContext xContext)58     public NodeHandler createHandler (XAccessibleContext xContext)
59     {
60         XAccessibleText xText = UnoRuntime.queryInterface (
61             XAccessibleText.class, xContext);
62         if (xText != null)
63             return new AccessibleTextHandler (xText);
64         else
65             return null;
66     }
67 
AccessibleTextHandler()68     public AccessibleTextHandler ()
69     {
70     }
71 
AccessibleTextHandler(XAccessibleText xText)72     public AccessibleTextHandler (XAccessibleText xText)
73     {
74         if (xText != null)
75             maChildList.setSize (8);
76     }
77 
createChild(AccessibleTreeNode aParent, int nIndex)78     public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex)
79     {
80         AccessibleTreeNode aChild = null;
81         XAccessibleText xText = null;
82         if (aParent instanceof AccTreeNode)
83             xText = ((AccTreeNode)aParent).getText();
84 
85         try
86         {
87             if( xText != null )
88             {
89                 switch( nIndex )
90                 {
91                     case 0:
92                         aChild = new StringNode (xText.getText(), aParent);
93                         break;
94                     case 1:
95                         aChild = new StringNode ("# chars: " + xText.getCharacterCount(), aParent);
96                         break;
97                     case 2:
98                         aChild = new StringNode (characters( xText ), aParent);
99                         break;
100                     case 3:
101                         aChild = new StringNode ("selection: "
102                             + "[" + xText.getSelectionStart()
103                             + "," + xText.getSelectionEnd()
104                             + "] \"" + xText.getSelectedText() + "\"",
105                             aParent);
106                         break;
107                     case 4:
108                         aChild = new StringNode ("getCaretPosition: " + xText.getCaretPosition(), aParent);
109                         break;
110                     case 5:
111                     {
112                         VectorNode aVec = new VectorNode("portions", aParent);
113                         aChild = aVec;
114                         aVec.addChild(
115                              textAtIndexNode( xText, "Character",
116                                               AccessibleTextType.CHARACTER,
117                                               aParent ) );
118                         aVec.addChild(
119                             textAtIndexNode( xText, "Word",
120                                              AccessibleTextType.WORD,
121                                              aParent ) );
122                         aVec.addChild(
123                             textAtIndexNode( xText, "Sentence",
124                                              AccessibleTextType.SENTENCE,
125                                              aParent ) );
126                         aVec.addChild(
127                             textAtIndexNode( xText, "Paragraph",
128                                              AccessibleTextType.PARAGRAPH,
129                                              aParent ) );
130                         aVec.addChild(
131                             textAtIndexNode( xText, "Line",
132                                              AccessibleTextType.LINE,
133                                              aParent ) );
134                         aVec.addChild(
135                             textAtIndexNode( xText, "Attribute",
136                                              AccessibleTextType.ATTRIBUTE_RUN,
137                                              aParent ) );
138                         aVec.addChild(
139                             textAtIndexNode( xText, "Glyph",
140                                              AccessibleTextType.GLYPH,
141                                              aParent ) );
142                     }
143                     break;
144                     case 6:
145                         aChild = new StringNode (bounds( xText ), aParent);
146                         break;
147                     case 7:
148                         aChild = getAttributes( xText, aParent );
149                         break;
150                     default:
151                         aChild = new StringNode ("unknown child index " + nIndex, aParent);
152                 }
153             }
154         }
155         catch (Exception e)
156         {
157             // Return empty child.
158         }
159 
160         return aChild;
161     }
162 
163 
textAtIndexNodeString( int nStart, int nEnd, String sWord, String sBefore, String sBehind)164     private String textAtIndexNodeString(
165         int nStart, int nEnd,
166         String sWord, String sBefore, String sBehind)
167     {
168         return "[" + nStart + "," + nEnd + "] "
169             + "\"" + sWord + "\"     \t"
170             + "(" + sBefore + ","
171             + "" + sBehind + ")";
172     }
173 
174     /** Create a text node that lists all strings of a particular text type
175      */
textAtIndexNode( XAccessibleText xText, String sName, short nTextType, AccessibleTreeNode aParent)176     private AccessibleTreeNode textAtIndexNode(
177         XAccessibleText xText,
178         String sName,
179         short nTextType,
180         AccessibleTreeNode aParent)
181     {
182         VectorNode aNode = new VectorNode (sName, aParent);
183 
184         // get word at all positions;
185         // for nicer display, compare current word to previous one and
186         // make a new node for every interval, not for every word
187         int nLength = xText.getCharacterCount();
188         if( nLength > 0 )
189         {
190             try
191             {
192                 // sWord + nStart mark the current word
193                 // make a node as soon as a new one is found; close the last
194                 // one at the end
195                 TextSegment sWord = xText.getTextAtIndex(0, nTextType);
196                 TextSegment sBefore = xText.getTextBeforeIndex(0, nTextType);
197                 TextSegment sBehind = xText.getTextBehindIndex(0, nTextType);
198                 int nStart = 0;
199                 for(int i = 1; i < nLength; i++)
200                 {
201                     TextSegment sTmp = xText.getTextAtIndex(i, nTextType);
202                     TextSegment sTBef = xText.getTextBeforeIndex(i, nTextType);
203                     TextSegment sTBeh = xText.getTextBehindIndex(i, nTextType);
204                     if( ! ( sTmp.equals( sWord ) && sTBef.equals( sBefore ) &&
205                             sTBeh.equals( sBehind ) ) )
206                     {
207                         aNode.addChild (new StringNode (textAtIndexNodeString(
208                             nStart, i,
209                             sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode));
210                         sWord = sTmp;
211                         sBefore = sTBef;
212                         sBehind = sTBeh;
213                         nStart = i;
214                     }
215 
216                     // don't generate more than 50 children.
217                     if (aNode.getChildCount() > 50)
218                     {
219                         sWord.SegmentText = "...";
220                         break;
221                     }
222                 }
223                 aNode.addChild (new StringNode (textAtIndexNodeString(
224                     nStart, nLength,
225                     sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode));
226             }
227             catch( IndexOutOfBoundsException e )
228             {
229                 aNode.addChild (new StringNode (e.toString(), aNode));
230             }
231             catch (com.sun.star.lang.IllegalArgumentException e)
232             {
233                 aNode.addChild (new StringNode (e.toString(), aNode));
234             }
235         }
236 
237         return aNode;
238     }
239 
240 
241 
242     /** getCharacter (display as array string) */
characters(XAccessibleText xText)243     private String characters(XAccessibleText xText)
244     {
245         // get count (max. 30)
246         int nChars = xText.getCharacterCount();
247         if( nChars > 30 )
248             nChars = 30;
249 
250         // build up string
251         StringBuilder aChars = new StringBuilder();
252         try
253         {
254             aChars.append( "[" );
255             for( int i = 0; i < nChars; i++)
256             {
257                 aChars.append( xText.getCharacter(i) );
258                 aChars.append( "," );
259             }
260             if( nChars > 0)
261             {
262                 if( nChars == xText.getCharacterCount() )
263                     aChars.deleteCharAt( aChars.length() - 1 );
264                 else
265                     aChars.append( "..." );
266             }
267             aChars.append( "]" );
268         }
269         catch( IndexOutOfBoundsException e )
270         {
271             aChars.append( "   ERROR   " );
272         }
273 
274         // return result
275         return "getCharacters: " + aChars;
276     }
277 
278 
279     /** iterate over characters, and translate their positions
280      * back and forth */
bounds( XAccessibleText xText )281     private String bounds( XAccessibleText xText )
282     {
283         StringBuilder aBuffer = new StringBuilder( "bounds: " );
284         try
285         {
286             // iterate over characters
287             int nCount = xText.getCharacterCount();
288             for(int i = 0; i < nCount; i++ )
289             {
290                 // get bounds for this character
291                 Rectangle aRect = xText.getCharacterBounds( i );
292 
293                 // get the character by 'clicking' into the middle of
294                 // the bounds
295                 Point aMiddle = new Point();
296                 aMiddle.X = aRect.X + (aRect.Width / 2) - 1;
297                 aMiddle.Y = aRect.Y + (aRect.Height / 2 ) - 1;
298                 int nIndex = xText.getIndexAtPoint( aMiddle );
299 
300                 // get the character, or a '#' for an illegal index
301                 if( (nIndex >= 0) && (nIndex < xText.getCharacter(i)) )
302                     aBuffer.append( xText.getCharacter(nIndex) );
303                 else
304                     aBuffer.append( '#' );
305             }
306         }
307         catch( IndexOutOfBoundsException e )
308             { ; } // ignore errors
309 
310         return aBuffer.toString();
311     }
312 
313 
getAttributes( XAccessibleText xText, AccessibleTreeNode aParent)314     private AccessibleTreeNode getAttributes( XAccessibleText xText,
315                                   AccessibleTreeNode aParent)
316     {
317         String[] aAttributeList = new String[] {
318             "CharBackColor",
319             "CharColor",
320             "CharEscapement",
321             "CharHeight",
322             "CharPosture",
323             "CharStrikeout",
324             "CharUnderline",
325             "CharWeight",
326             "ParaAdjust",
327             "ParaBottomMargin",
328             "ParaFirstLineIndent",
329             "ParaLeftMargin",
330             "ParaLineSpacing",
331             "ParaRightMargin",
332             "ParaTabStops"};
333 
334         AccessibleTreeNode aRet;
335 
336         try
337         {
338             VectorNode aPortions = new VectorNode ("getAttributes", aParent);
339 
340             int nIndex = 0;
341             int nLength = xText.getCharacterCount();
342             while( nIndex < nLength )
343             {
344                 // get attribute run
345                 String aPortion = null;
346                 try
347                 {
348                     aPortion = xText.getTextAtIndex(
349                         nIndex, AccessibleTextType.ATTRIBUTE_RUN).SegmentText;
350                 }
351                 catch(com.sun.star.lang.IllegalArgumentException e)
352                 {
353                     aPortion = new String ("");
354                 }
355 
356                 // get attributes and make node with attribute children
357                 PropertyValue[] aValues = xText.getCharacterAttributes(nIndex, aAttributeList);
358                 VectorNode aAttrs = new VectorNode (aPortion, aPortions);
359                 for( int i = 0; i < aValues.length; i++ )
360                 {
361                     new StringNode( aValues[i].Name + ": " + aValues[i].Value,
362                                     aAttrs );
363                 }
364 
365                 // get next portion, but advance at least one
366                 nIndex += (aPortion.length() > 0) ? aPortion.length() : 1;
367             }
368 
369             aRet = aPortions;
370         }
371         catch( IndexOutOfBoundsException e )
372         {
373             aRet = new StringNode( "Exception caught:" + e, aParent );
374         }
375 
376         return aRet;
377     }
378 
379 
380     static String[] aTextActions =
381         new String[] { "select...", "copy..." };
382     static String[] aEditableTextActions =
383         new String[] { "select...", "copy...",
384                        "cut...", "paste...", "edit...", "format..." };
385 
getActions(AccessibleTreeNode aNode)386     public String[] getActions (AccessibleTreeNode aNode)
387     {
388         XAccessibleEditableText xEText = null;
389         if (aNode instanceof AccTreeNode)
390             xEText = ((AccTreeNode)aNode).getEditText ();
391 
392         return (xEText == null) ? aTextActions : aEditableTextActions;
393     }
394 
performAction(AccessibleTreeNode aNode, int nIndex)395     public void performAction (AccessibleTreeNode aNode, int nIndex)
396     {
397         if ( ! (aNode instanceof AccTreeNode))
398             return;
399 
400         AccTreeNode aATNode = (AccTreeNode)aNode;
401         TextActionDialog aDialog = null;
402 
403         // create proper dialog
404         switch( nIndex )
405         {
406             case 0:
407                 aDialog = new TextActionDialog( aATNode,
408                                                 "Select range:",
409                                                 "select" )
410                     {
411                         boolean action(
412                             JTextComponent aText, AccTreeNode aNode )
413                             throws IndexOutOfBoundsException
414                         {
415                             return aNode.getText().setSelection(
416                                 getSelectionStart(),
417                                 getSelectionEnd() );
418                         }
419                     };
420                 break;
421             case 1:
422                 aDialog = new TextActionDialog( aATNode,
423                                                 "Select range and copy:",
424                                                 "copy" )
425                     {
426                         boolean action(
427                             JTextComponent aText, AccTreeNode aNode )
428                             throws IndexOutOfBoundsException
429                         {
430                             return aNode.getText().copyText(
431                                 getSelectionStart(),
432                                 getSelectionEnd() );
433                         }
434                     };
435                 break;
436             case 2:
437                 aDialog = new TextActionDialog( aATNode,
438                                                 "Select range and cut:",
439                                                 "cut" )
440                     {
441                         boolean action(
442                             JTextComponent aText, AccTreeNode aNode )
443                             throws IndexOutOfBoundsException
444                         {
445                             return aNode.getEditText().cutText(
446                                 getSelectionStart(),
447                                 getSelectionEnd() );
448                         }
449                     };
450                 break;
451             case 3:
452                 aDialog = new TextActionDialog( aATNode,
453                                                 "Place Caret and paste:",
454                                                 "paste" )
455                     {
456                         boolean action(
457                             JTextComponent aText, AccTreeNode aNode )
458                             throws IndexOutOfBoundsException
459                         {
460                             return aNode.getEditText().pasteText(
461                                 aText.getCaretPosition() );
462                         }
463                     };
464                 break;
465             case 4:
466                 aDialog = new TextEditDialog( aATNode, "Edit text:",
467                                               "edit" );
468                 break;
469             case 5:
470                 aDialog = new TextAttributeDialog( aATNode );
471                 break;
472         }
473 
474         if( aDialog != null )
475             aDialog.show();
476     }
477 
478 }
479 
480 /**
481  * Display a dialog with a text field and a pair of cancel/do-it buttons
482  */
483 class TextActionDialog extends JDialog
484     implements ActionListener
485 {
486     AccTreeNode aNode;
487     JTextArea aText;
488     String sName;
489     JCheckBox aIndexToggle;
490 
TextActionDialog( AccTreeNode aNd, String sExplanation, String sButtonText )491     public TextActionDialog( AccTreeNode aNd,
492                              String sExplanation,
493                              String sButtonText )
494     {
495         super( AccessibilityWorkBench.Instance() );
496 
497         aNode = aNd;
498         sName = sButtonText;
499         init( sExplanation, aNode.getText().getText(), sButtonText );
500 //        setSize( getPreferredSize() );
501         setSize( 350, 225 );
502     }
503 
504     /** build dialog */
init( String sExplanation, String sText, String sButtonText )505     protected void init( String sExplanation,
506                          String sText,
507                          String sButtonText )
508     {
509         setTitle( sName );
510 
511         // vertical stacking of the elements
512         Container aContent = getContentPane();
513         //        aContent.setLayout( new BorderLayout() );
514 
515         // label with explanation
516         if( sExplanation.length() > 0 )
517             aContent.add( new JLabel( sExplanation ), BorderLayout.NORTH );
518 
519         // the text field
520         aText = new JTextArea();
521         aText.setText( sText );
522         aText.setColumns( Math.min( Math.max( 40, sText.length() ), 20 ) );
523         aText.setRows( sText.length() / 40 + 1 );
524         aText.setLineWrap( true );
525         aText.setEditable( false );
526         aContent.add( aText, BorderLayout.CENTER );
527 
528         JPanel aButtons = new JPanel();
529         aButtons.setLayout( new FlowLayout() );
530         aIndexToggle = new JCheckBox( "reverse selection" );
531         aButtons.add( aIndexToggle );
532         JButton aActionButton = new JButton( sButtonText );
533         aActionButton.setActionCommand( "Action" );
534         aActionButton.addActionListener( this );
535         aButtons.add( aActionButton );
536         JButton aCancelButton = new JButton( "cancel" );
537         aCancelButton.setActionCommand( "Cancel" );
538         aCancelButton.addActionListener( this );
539         aButtons.add( aCancelButton );
540 
541         // add Panel with buttons
542         aContent.add( aButtons, BorderLayout.SOUTH );
543     }
544 
cancel()545     void cancel()
546     {
547         hide();
548         dispose();
549     }
550 
action()551     void action()
552     {
553         String sError = null;
554         try
555         {
556             boolean bSuccess = action( aText, aNode );
557             if( !bSuccess )
558                 sError = "Can't execute";
559         }
560         catch( IndexOutOfBoundsException e )
561         {
562             sError = "Index out of bounds";
563         }
564 
565         if( sError != null )
566             JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(),
567                                            sError, sName,
568                                            JOptionPane.ERROR_MESSAGE);
569 
570         cancel();
571     }
572 
actionPerformed(ActionEvent e)573     public void actionPerformed(ActionEvent e)
574     {
575         String sCommand = e.getActionCommand();
576 
577         if( "Cancel".equals( sCommand ) )
578             cancel();
579         else if( "Action".equals( sCommand ) )
580             action();
581     }
582 
583 
getSelectionStart()584     int getSelectionStart()     { return getSelection(true); }
getSelectionEnd()585     int getSelectionEnd()       { return getSelection(false); }
getSelection(boolean bStart)586     int getSelection(boolean bStart)
587     {
588         return ( bStart ^ aIndexToggle.isSelected() )
589             ? aText.getSelectionStart() : aText.getSelectionEnd();
590     }
591 
592 
593 
594     /** override this for dialog-specific action */
action( JTextComponent aText, AccTreeNode aNode )595     boolean action( JTextComponent aText, AccTreeNode aNode )
596         throws IndexOutOfBoundsException
597     {
598         return false;
599     }
600 }
601 
602 
603 class TextEditDialog extends TextActionDialog
604 {
TextEditDialog( AccTreeNode aNode, String sExplanation, String sButtonText )605     public TextEditDialog( AccTreeNode aNode,
606                            String sExplanation,
607                            String sButtonText )
608     {
609         super( aNode, sExplanation, sButtonText );
610     }
611 
init( String sExplanation, String sText, String sButtonText )612     protected void init( String sExplanation,
613                          String sText,
614                          String sButtonText )
615     {
616         super.init( sExplanation, sText, sButtonText );
617         aText.setEditable( true );
618     }
619 
620 
621     /** edit the text */
action( JTextComponent aText, AccTreeNode aNode )622     boolean action( JTextComponent aText, AccTreeNode aNode )
623     {
624         // is this text editable? if not, fudge you and return
625         XAccessibleEditableText xEdit = aNode.getEditText();
626         return ( xEdit == null ) ? false :
627             updateText( xEdit, aText.getText() );
628     }
629 
630 
631     /** update the text */
updateText( XAccessibleEditableText xEdit, String sNew )632     boolean updateText( XAccessibleEditableText xEdit, String sNew )
633     {
634         String sOld = xEdit.getText();
635 
636         // false alarm? Early out if no change was done!
637         if( sOld.equals( sNew ) )
638             return false;
639 
640         // get the minimum length of both strings
641         int nMinLength = sOld.length();
642         if( sNew.length() < nMinLength )
643             nMinLength = sNew.length();
644 
645         // count equal characters from front and end
646         int nFront = 0;
647         while( (nFront < nMinLength) &&
648                (sNew.charAt(nFront) == sOld.charAt(nFront)) )
649             nFront++;
650         int nBack = 0;
651         while( (nBack < nMinLength) &&
652                ( sNew.charAt(sNew.length()-nBack-1) ==
653                  sOld.charAt(sOld.length()-nBack-1)    ) )
654             nBack++;
655         if( nFront + nBack > nMinLength )
656             nBack = nMinLength - nFront;
657 
658         // so... the first nFront and the last nBack characters
659         // are the same. Change the others!
660         String sDel = sOld.substring( nFront, sOld.length() - nBack );
661         String sIns = sNew.substring( nFront, sNew.length() - nBack );
662 
663         System.out.println("edit text: " +
664                            sOld.substring(0, nFront) +
665                            " [ " + sDel + " -> " + sIns + " ] " +
666                            sOld.substring(sOld.length() - nBack) );
667 
668         boolean bRet = false;
669         try
670         {
671             // edit the text, and use
672             // (set|insert|delete|replace)Text as needed
673             if( nFront+nBack == 0 )
674                 bRet = xEdit.setText( sIns );
675             else if( sDel.length() == 0 )
676                 bRet = xEdit.insertText( sIns, nFront );
677             else if( sIns.length() == 0 )
678                 bRet = xEdit.deleteText( nFront, sOld.length()-nBack );
679             else
680                 bRet = xEdit.replaceText(nFront, sOld.length()-nBack,sIns);
681         }
682         catch( IndexOutOfBoundsException e )
683         {
684             bRet = false;
685         }
686 
687         return bRet;
688     }
689 }
690 
691 
692 class TextAttributeDialog extends TextActionDialog
693 {
TextAttributeDialog( AccTreeNode aNode )694     public TextAttributeDialog(
695         AccTreeNode aNode )
696     {
697         super( aNode, "Choose attributes, select text, and press 'Set':",
698                "set" );
699     }
700 
701     private JCheckBox aBold, aUnderline, aItalics;
702     private Color aForeground, aBackground;
703 
init( String sExplanation, String sText, String sButtonText )704     protected void init( String sExplanation,
705                          String sText,
706                          String sButtonText )
707     {
708         super.init( sExplanation, sText, sButtonText );
709 
710         aForeground = Color.black;
711         aBackground = Color.white;
712 
713         JPanel aAttr = new JPanel();
714         aAttr.setLayout( new BoxLayout( aAttr, BoxLayout.Y_AXIS ) );
715 
716         aBold = new JCheckBox( "bold" );
717         aUnderline = new JCheckBox( "underline" );
718         aItalics = new JCheckBox( "italics" );
719 
720         JButton aForeButton = new JButton("Foreground", new ColorIcon(true));
721         aForeButton.addActionListener( new ActionListener() {
722                 public void actionPerformed(ActionEvent e)
723                 {
724                     aForeground = JColorChooser.showDialog(
725                         TextAttributeDialog.this,
726                         "Select Foreground Color",
727                         aForeground);
728                 }
729             } );
730 
731         JButton aBackButton = new JButton("Background", new ColorIcon(false));
732         aBackButton.addActionListener( new ActionListener() {
733                 public void actionPerformed(ActionEvent e)
734                 {
735                     aBackground = JColorChooser.showDialog(
736                         TextAttributeDialog.this,
737                         "Select Background Color",
738                         aBackground);
739                 }
740             } );
741 
742         aAttr.add( aBold );
743         aAttr.add( aUnderline );
744         aAttr.add( aItalics );
745         aAttr.add( aForeButton );
746         aAttr.add( aBackButton );
747 
748         getContentPane().add( aAttr, BorderLayout.WEST );
749     }
750 
751 
752     class ColorIcon implements Icon
753     {
754         boolean bForeground;
755         static final int nHeight = 16;
756         static final int nWidth = 16;
757 
ColorIcon(boolean bWhich)758         public ColorIcon(boolean bWhich) { bForeground = bWhich; }
getIconHeight()759         public int getIconHeight()  { return nHeight; }
getIconWidth()760         public int getIconWidth() { return nWidth; }
paintIcon(Component c, Graphics g, int x, int y)761         public void paintIcon(Component c, Graphics g, int x, int y)
762         {
763             g.setColor( getColor() );
764             g.fillRect( x, y, nHeight, nWidth );
765             g.setColor( c.getForeground() );
766             g.drawRect( x, y, nHeight, nWidth );
767         }
getColor()768         Color getColor()
769         {
770             return bForeground ? aForeground : aBackground;
771         }
772     }
773 
774 
775 
776     /** edit the text */
action( JTextComponent aText, AccTreeNode aNode )777     boolean action( JTextComponent aText, AccTreeNode aNode )
778         throws IndexOutOfBoundsException
779     {
780         // is this text editable? if not, fudge you and return
781         XAccessibleEditableText xEdit = aNode.getEditText();
782         boolean bSuccess = false;
783         if( xEdit != null )
784         {
785             PropertyValue[] aSequence = new PropertyValue[6];
786             aSequence[0] = new PropertyValue();
787             aSequence[0].Name = "CharWeight";
788             aSequence[0].Value = new Integer( aBold.isSelected() ? 150 : 100 );
789             aSequence[1] = new PropertyValue();
790             aSequence[1].Name = "CharUnderline";
791             aSequence[1].Value = new Integer( aUnderline.isSelected() ? 1 : 0 );
792             aSequence[2] = new PropertyValue();
793             aSequence[2].Name = "CharBackColor";
794             aSequence[2].Value = new Integer( aBackground.getRGB() );
795             aSequence[3] = new PropertyValue();
796             aSequence[3].Name = "CharColor";
797             aSequence[3].Value = new Integer( aForeground.getRGB() );
798             aSequence[4] = new PropertyValue();
799             aSequence[4].Name = "CharPosture";
800             aSequence[4].Value = new Integer( aItalics.isSelected() ? 1 : 0 );
801             aSequence[5] = new PropertyValue();
802             aSequence[5].Name = "CharBackTransparent";
803             aSequence[5].Value = new Boolean( false );
804 
805             bSuccess = xEdit.setAttributes( getSelectionStart(),
806                                             getSelectionEnd(),
807                                             aSequence );
808         }
809         return bSuccess;
810     }
811 
812 }
813