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 24 package org.openoffice.accessibility.awb.view; 25 26 import java.awt.Color; 27 import java.awt.Dimension; 28 import java.awt.GridBagConstraints; 29 import java.awt.GridBagLayout; 30 import java.awt.event.ActionListener; 31 import java.awt.event.ActionEvent; 32 33 import javax.swing.JButton; 34 import javax.swing.JComponent; 35 import javax.swing.JLabel; 36 import javax.swing.JPanel; 37 import javax.swing.JScrollPane; 38 import javax.swing.JSpinner; 39 import javax.swing.JTree; 40 import javax.swing.tree.TreeNode; 41 import javax.swing.tree.DefaultMutableTreeNode; 42 import javax.swing.tree.DefaultTreeModel; 43 import javax.swing.tree.MutableTreeNode; 44 45 import com.sun.star.accessibility.AccessibleEventId; 46 import com.sun.star.accessibility.AccessibleEventObject; 47 import com.sun.star.accessibility.AccessibleTextType; 48 import com.sun.star.accessibility.AccessibleStateType; 49 import com.sun.star.accessibility.TextSegment; 50 import com.sun.star.accessibility.XAccessibleText; 51 import com.sun.star.accessibility.XAccessibleContext; 52 import com.sun.star.accessibility.XAccessibleMultiLineText; 53 import com.sun.star.accessibility.XAccessibleStateSet; 54 import com.sun.star.awt.Point; 55 import com.sun.star.awt.Rectangle; 56 import com.sun.star.beans.PropertyValue; 57 import com.sun.star.lang.IndexOutOfBoundsException; 58 import com.sun.star.lang.IllegalArgumentException; 59 import com.sun.star.uno.UnoRuntime; 60 61 import org.openoffice.accessibility.awb.view.text.CaretSpinnerModel; 62 import org.openoffice.accessibility.awb.view.text.TextDialogFactory; 63 64 65 public class TextView 66 extends ObjectView 67 implements ActionListener 68 { 69 70 /** Create a TextView when the given object supports the 71 XAccessibleText interface. 72 */ Create( ObjectViewContainer aContainer, XAccessibleContext xContext)73 static public ObjectView Create ( 74 ObjectViewContainer aContainer, 75 XAccessibleContext xContext) 76 { 77 XAccessibleText xText = (XAccessibleText)UnoRuntime.queryInterface( 78 XAccessibleText.class, xContext); 79 if (xText != null) 80 return new TextView (aContainer); 81 else 82 return null; 83 } 84 85 TextView(ObjectViewContainer aContainer)86 public TextView (ObjectViewContainer aContainer) 87 { 88 super (aContainer); 89 90 ViewGridLayout aLayout = new ViewGridLayout (this); 91 92 maTextLabel = aLayout.AddLabeledString ("Text: "); 93 maCharacterArrayLabel = aLayout.AddLabeledEntry ("Characters: "); 94 maCharacterCountLabel = aLayout.AddLabeledEntry ("Character Count: "); 95 maSelectionLabel = aLayout.AddLabeledEntry ("Selection: "); 96 maBoundsLabel = aLayout.AddLabeledEntry ("Bounds Test: "); 97 maCaretPositionSpinner = (JSpinner)aLayout.AddLabeledComponent ( 98 "Caret position:", new JSpinner()); 99 Dimension aSize = maCaretPositionSpinner.getSize(); 100 maCaretPositionSpinner.setPreferredSize (new Dimension (100,20)); 101 maCaretLineNoLabel = aLayout.AddLabeledEntry ("Line number at caret: "); 102 maCaretLineTextLabel = aLayout.AddLabeledEntry ("Text of line at caret: "); 103 maLineNoFromCaretPosLabel = aLayout.AddLabeledEntry ("Line number at index of caret: "); 104 maLineTextFromCaretPosLabel = aLayout.AddLabeledEntry ("Text of line at index of caret: "); 105 106 JPanel aButtonPanel = new JPanel (); 107 aLayout.AddComponent (aButtonPanel); 108 109 JButton aButton = new JButton ("select..."); 110 aButton.setFont (aLayout.GetFont()); 111 aButton.addActionListener (this); 112 aButtonPanel.add (aButton); 113 114 aButton = new JButton ("copy..."); 115 aButton.setFont (aLayout.GetFont()); 116 aButton.addActionListener (this); 117 aButtonPanel.add (aButton); 118 119 // A tree that holds the text broken down into various segments. 120 maTree = new JTree (); 121 aLayout.AddComponent (new JScrollPane ( 122 maTree, 123 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 124 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); 125 } 126 127 128 /** Additionally to the context store a reference to the 129 XAccessibleText interface. 130 */ SetObject(XAccessibleContext xObject)131 public void SetObject (XAccessibleContext xObject) 132 { 133 mxText = (XAccessibleText)UnoRuntime.queryInterface( 134 XAccessibleText.class, xObject); 135 maCaretSpinnerModel = new CaretSpinnerModel(mxText); 136 maCaretPositionSpinner.setModel (maCaretSpinnerModel); 137 super.SetObject (xObject); 138 } 139 Destroy()140 synchronized public void Destroy () 141 { 142 mxText = null; 143 super.Destroy(); 144 } 145 Update()146 synchronized public void Update () 147 { 148 maCaretPositionSpinner.setEnabled (mxText != null); 149 DefaultMutableTreeNode aRoot = new DefaultMutableTreeNode ("Text Segments"); 150 if (mxText == null) 151 { 152 maTextLabel.setText ("<null object>"); 153 maCharacterArrayLabel.setText ("<null object>"); 154 maCharacterCountLabel.setText ("<null object>"); 155 maSelectionLabel.setText ("<null object>"); 156 maBoundsLabel.setText ("<null object>"); 157 maCaretLineNoLabel.setText ("<null object>"); 158 maCaretLineTextLabel.setText ("<null object>"); 159 maLineNoFromCaretPosLabel.setText ("<null object>"); 160 maLineTextFromCaretPosLabel.setText ("<null object>"); 161 } 162 else 163 { 164 maTextLabel.setText (mxText.getText()); 165 maCharacterArrayLabel.setText (GetCharacterArray()); 166 maCharacterCountLabel.setText ( 167 Integer.toString(mxText.getCharacterCount())); 168 // Selection. 169 maSelectionLabel.setText ( 170 "[" + mxText.getSelectionStart() 171 + "," + mxText.getSelectionEnd() 172 + "] \"" + mxText.getSelectedText() + "\""); 173 174 // Character bounds. 175 maBoundsLabel.setText (GetTextBoundsString()); 176 177 // Caret position. 178 maCaretPositionSpinner.setValue (new Integer (mxText.getCaretPosition())); 179 180 // Multi line methods. 181 XAccessibleMultiLineText xMultiText = (XAccessibleMultiLineText) 182 UnoRuntime.queryInterface( XAccessibleMultiLineText.class, mxText ); 183 184 if( null != xMultiText ) { 185 try { 186 maCaretLineNoLabel.setText ( Integer.toString( xMultiText.getNumberOfLineWithCaret() ) ); 187 TextSegment ts = xMultiText.getTextAtLineWithCaret(); 188 maCaretLineTextLabel.setText ( "[" + ts.SegmentStart 189 + "," + ts.SegmentEnd 190 + "] \"" + ts.SegmentText + "\""); 191 maLineNoFromCaretPosLabel.setText ( Integer.toString( xMultiText.getLineNumberAtIndex( mxText.getCaretPosition() ) ) ); 192 ts = xMultiText.getTextAtLineNumber(xMultiText.getLineNumberAtIndex( mxText.getCaretPosition() ) ); 193 maLineTextFromCaretPosLabel.setText ( "[" + ts.SegmentStart 194 + "," + ts.SegmentEnd 195 + "] \"" + ts.SegmentText + "\""); 196 } catch( IndexOutOfBoundsException e) { 197 } 198 } 199 200 // Text segments. 201 aRoot.add (CreateNode ("Character", AccessibleTextType.CHARACTER)); 202 aRoot.add (CreateNode ("Word", AccessibleTextType.WORD)); 203 aRoot.add (CreateNode ("Sentence", AccessibleTextType.SENTENCE)); 204 aRoot.add (CreateNode ("Paragraph", AccessibleTextType.PARAGRAPH)); 205 aRoot.add (CreateNode ("Line", AccessibleTextType.LINE)); 206 aRoot.add (CreateNode ("Attribute", AccessibleTextType.ATTRIBUTE_RUN)); 207 aRoot.add (CreateNode ("Glyph", AccessibleTextType.GLYPH)); 208 } 209 ((DefaultTreeModel)maTree.getModel()).setRoot (aRoot); 210 } 211 GetTitle()212 public String GetTitle () 213 { 214 return ("Text"); 215 } 216 notifyEvent(AccessibleEventObject aEvent)217 public void notifyEvent (AccessibleEventObject aEvent) 218 { 219 System.out.println (aEvent); 220 switch (aEvent.EventId) 221 { 222 case AccessibleEventId.CARET_CHANGED : 223 maCaretSpinnerModel.Update(); 224 Update (); 225 break; 226 227 case AccessibleEventId.TEXT_CHANGED : 228 case AccessibleEventId.TEXT_SELECTION_CHANGED: 229 Update (); 230 break; 231 } 232 } 233 actionPerformed(ActionEvent aEvent)234 public void actionPerformed (ActionEvent aEvent) 235 { 236 String sCommand = aEvent.getActionCommand(); 237 if (sCommand.equals ("select...")) 238 TextDialogFactory.CreateSelectionDialog (mxContext); 239 else if (sCommand.equals ("copy...")) 240 TextDialogFactory.CreateCopyDialog (mxContext); 241 } 242 243 244 245 /** Create a string that is a list of all characters returned by the 246 getCharacter() method. 247 */ GetCharacterArray()248 private String GetCharacterArray () 249 { 250 // Do not show more than 30 characters. 251 int nCharacterCount = mxText.getCharacterCount(); 252 int nMaxDisplayCount = 30; 253 254 // build up string 255 StringBuffer aCharacterArray = new StringBuffer(); 256 int nIndex = 0; 257 try 258 { 259 while (nIndex<nCharacterCount && nIndex<nMaxDisplayCount) 260 { 261 aCharacterArray.append (mxText.getCharacter (nIndex)); 262 if (nIndex < nCharacterCount-1) 263 aCharacterArray.append (","); 264 nIndex ++; 265 } 266 if (nMaxDisplayCount < nCharacterCount) 267 aCharacterArray.append (", ..."); 268 } 269 catch (IndexOutOfBoundsException e) 270 { 271 aCharacterArray.append ("; Index Out Of Bounds at index " + nIndex); 272 } 273 274 return aCharacterArray.toString(); 275 } 276 277 278 279 /** Iterate over all characters and translate their positions 280 back and forth. 281 */ GetTextBoundsString()282 private String GetTextBoundsString () 283 { 284 StringBuffer aBuffer = new StringBuffer (); 285 try 286 { 287 // Iterate over all characters in the text. 288 int nCount = mxText.getCharacterCount(); 289 for (int i=0; i<nCount; i++) 290 { 291 // Get bounds for this character. 292 Rectangle aBBox = mxText.getCharacterBounds (i); 293 294 // get the character by 'clicking' into the middle of 295 // the bounds 296 Point aMiddle = new Point(); 297 aMiddle.X = aBBox.X + (aBBox.Width / 2) - 1; 298 aMiddle.Y = aBBox.Y + (aBBox.Height / 2) - 1; 299 int nIndex = mxText.getIndexAtPoint (aMiddle); 300 301 // get the character, or a '#' for an illegal index 302 if ((nIndex >= 0) && (nIndex < mxText.getCharacter(i))) 303 aBuffer.append (mxText.getCharacter(nIndex)); 304 else 305 aBuffer.append ('#'); 306 } 307 } 308 catch (IndexOutOfBoundsException aEvent) 309 { 310 // Ignore errors. 311 } 312 313 return aBuffer.toString(); 314 } 315 316 317 318 319 private final static int BEFORE = -1; 320 private final static int AT = 0; 321 private final static int BEHIND = +1; 322 CreateNode(String sTitle, short nTextType)323 private MutableTreeNode CreateNode (String sTitle, short nTextType) 324 { 325 DefaultMutableTreeNode aNode = new DefaultMutableTreeNode (sTitle); 326 327 aNode.add (CreateSegmentNode ("Before", nTextType, BEFORE)); 328 aNode.add (CreateSegmentNode ("At", nTextType, AT)); 329 aNode.add (CreateSegmentNode ("Behind", nTextType, BEHIND)); 330 331 return aNode; 332 } 333 CreateSegmentNode(String sTitle, short nTextType, int nWhere)334 private MutableTreeNode CreateSegmentNode (String sTitle, short nTextType, int nWhere) 335 { 336 TextSegment aSegment; 337 int nTextLength = mxText.getCharacterCount(); 338 DefaultMutableTreeNode aNode = new DefaultMutableTreeNode (sTitle); 339 for (int nIndex=0; nIndex<=nTextLength; /* empty */) 340 { 341 aSegment = GetTextSegment (nIndex, nTextType, nWhere); 342 DefaultMutableTreeNode aSegmentNode = new DefaultMutableTreeNode ( 343 new StringBuffer ( 344 Integer.toString (nIndex) + " -> " 345 + Integer.toString (aSegment.SegmentStart) + " - " 346 + Integer.toString (aSegment.SegmentEnd) + " : " 347 + aSegment.SegmentText.toString())); 348 aNode.add (aSegmentNode); 349 if (nTextType == AccessibleTextType.ATTRIBUTE_RUN) 350 AddAttributeNodes (aSegmentNode, aSegment); 351 if (aSegment.SegmentEnd > nIndex) 352 nIndex = aSegment.SegmentEnd; 353 else 354 nIndex ++; 355 } 356 357 return aNode; 358 } 359 360 GetTextSegment(int nIndex, short nTextType, int nWhere)361 private TextSegment GetTextSegment (int nIndex, short nTextType, int nWhere) 362 { 363 TextSegment aSegment; 364 365 try 366 { 367 switch (nWhere) 368 { 369 case BEFORE: 370 aSegment = mxText.getTextBeforeIndex (nIndex, nTextType); 371 break; 372 373 case AT: 374 aSegment = mxText.getTextAtIndex (nIndex, nTextType); 375 break; 376 377 case BEHIND: 378 aSegment = mxText.getTextBehindIndex (nIndex, nTextType); 379 break; 380 381 default: 382 aSegment = new TextSegment(); 383 aSegment.SegmentText = new String ("unknown position " + nWhere); 384 aSegment.SegmentStart = nIndex; 385 aSegment.SegmentStart = nIndex+1; 386 break; 387 } 388 } 389 catch (IndexOutOfBoundsException aException) 390 { 391 aSegment = new TextSegment (); 392 aSegment.SegmentText = new String ("Invalid index at ") + nIndex + " : " 393 + aException.toString(); 394 aSegment.SegmentStart = nIndex; 395 aSegment.SegmentEnd = nIndex+1; 396 } 397 catch (IllegalArgumentException aException) 398 { 399 aSegment = new TextSegment (); 400 aSegment.SegmentText = new String ("Illegal argument at ") + nIndex + " : " 401 + aException.toString(); 402 aSegment.SegmentStart = nIndex; 403 aSegment.SegmentEnd = nIndex+1; 404 } 405 406 return aSegment; 407 } 408 409 410 /** Add to the given node one node for every attribute of the given segment. 411 */ AddAttributeNodes( DefaultMutableTreeNode aNode, TextSegment aSegment)412 private void AddAttributeNodes ( 413 DefaultMutableTreeNode aNode, 414 TextSegment aSegment) 415 { 416 try 417 { 418 PropertyValue[] aValues = mxText.getCharacterAttributes ( 419 aSegment.SegmentStart, aAttributeList); 420 for (int i=0; i<aValues.length; i++) 421 aNode.add (new DefaultMutableTreeNode ( 422 aValues[i].Name + ": " + aValues[i].Value)); 423 } 424 catch (IndexOutOfBoundsException aException) 425 { 426 aNode.add (new DefaultMutableTreeNode ( 427 "caught IndexOutOfBoundsException while retrieveing attributes")); 428 } 429 } 430 431 private XAccessibleText mxText; 432 private JLabel 433 maTextLabel, 434 maCharacterArrayLabel, 435 maCharacterCountLabel, 436 maSelectionLabel, 437 maBoundsLabel, 438 maCaretLineNoLabel, 439 maCaretLineTextLabel, 440 maLineNoFromCaretPosLabel, 441 maLineTextFromCaretPosLabel; 442 443 private JSpinner maCaretPositionSpinner; 444 private JTree maTree; 445 private CaretSpinnerModel maCaretSpinnerModel; 446 447 private static String[] aAttributeList = new String[] { 448 "CharBackColor", 449 "CharColor", 450 "CharEscapement", 451 "CharHeight", 452 "CharPosture", 453 "CharStrikeout", 454 "CharUnderline", 455 "CharWeight", 456 "ParaAdjust", 457 "ParaBottomMargin", 458 "ParaFirstLineIndent", 459 "ParaLeftMargin", 460 "ParaLineSpacing", 461 "ParaRightMargin", 462 "ParaTabStops"}; 463 } 464