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