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