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 import com.sun.star.awt.XWindow; 23 import com.sun.star.beans.XPropertySet; 24 import com.sun.star.beans.XPropertyChangeListener; 25 import com.sun.star.beans.PropertyChangeEvent; 26 import com.sun.star.container.XEnumerationAccess; 27 import com.sun.star.container.XEnumeration; 28 import com.sun.star.document.XEventListener; 29 import com.sun.star.drawing.XDrawPage; 30 import com.sun.star.drawing.XDrawView; 31 import com.sun.star.frame.XController; 32 import com.sun.star.frame.XFrame; 33 import com.sun.star.frame.XFrameActionListener; 34 import com.sun.star.frame.FrameActionEvent; 35 import com.sun.star.frame.FrameAction; 36 import com.sun.star.lang.XComponent; 37 import com.sun.star.lang.XMultiServiceFactory; 38 import com.sun.star.lang.XServiceInfo; 39 import com.sun.star.frame.XDesktop; 40 import com.sun.star.frame.XModel; 41 import com.sun.star.frame.XTerminateListener; 42 import com.sun.star.uno.UnoRuntime; 43 44 import com.sun.star.accessibility.XAccessible; 45 import com.sun.star.accessibility.XAccessibleContext; 46 import com.sun.star.accessibility.XAccessibleComponent; 47 import com.sun.star.accessibility.XAccessibleExtendedComponent; 48 import com.sun.star.accessibility.XAccessibleRelationSet; 49 import com.sun.star.accessibility.XAccessibleStateSet; 50 51 import com.sun.star.awt.XExtendedToolkit; 52 53 import java.awt.*; 54 import java.awt.event.*; 55 import javax.swing.*; 56 import javax.swing.tree.*; 57 import javax.swing.event.TreeSelectionListener; 58 import javax.swing.event.TreeSelectionEvent; 59 import java.io.*; 60 61 import ov.ObjectViewContainer; 62 63 /** This class manages the GUI of the work bench. 64 @see AccessibilityTreeModel 65 for the implementation of the tree view on the left side which also 66 manages the registration of accessibility listeners. 67 @see Canvas 68 for the graphical view of the accessible objects. 69 */ 70 public class AccessibilityWorkBench 71 extends JFrame 72 implements ActionListener, XTerminateListener, TreeSelectionListener 73 74 { 75 public static final String msVersion = "v1.7.2"; 76 public String msOptionsFileName = ".AWBrc"; 77 78 public static void main (String args[]) 79 { 80 int nPortNumber = 5678; 81 82 for (int i=0; i<args.length; i++) 83 { 84 if (args[i].equals ("-h") || args[i].equals ("--help") || args[i].equals ("-?")) 85 { 86 System.out.println ("usage: AccessibilityWorkBench <option>*"); 87 System.out.println ("options:"); 88 System.out.println (" -p <port-number> Port on which to connect to StarOffice."); 89 System.out.println (" Defaults to 5678."); 90 System.exit (0); 91 } 92 else if (args[i].equals ("-p")) 93 { 94 nPortNumber = Integer.parseInt (args[++i]); 95 } 96 } 97 98 saWorkBench = new AccessibilityWorkBench (nPortNumber); 99 } 100 101 102 103 104 /** Return the one instance of the AccessibilityWorkBench 105 @return 106 Returns null when the AccessibilityWorkBench could not be 107 created successfully. 108 */ 109 public static AccessibilityWorkBench Instance () 110 { 111 return saWorkBench; 112 } 113 114 115 116 /** Create an accessibility work bench that listens at the specified 117 port to Office applications. 118 */ 119 private AccessibilityWorkBench (int nPortNumber) 120 { 121 mbInitialized = false; 122 123 Layout (); 124 125 MessageArea.println (System.getProperty ("os.name") + " / " 126 + System.getProperty ("os.arch") + " / " 127 + System.getProperty ("os.version")); 128 MessageArea.println ("Using port " + nPortNumber); 129 office = new SimpleOffice (nPortNumber); 130 info = new InformationWriter (); 131 132 maAccessibilityTree.getComponent().addTreeSelectionListener (this); 133 134 addWindowListener (new WindowAdapter () 135 { public void windowClosing (WindowEvent e) 136 { System.exit(0); } 137 }); 138 139 initialize (); 140 } 141 142 143 144 145 /** Create and arrange the widgets of the GUI. 146 */ 147 public void Layout () 148 { 149 setSize (new Dimension (8000,600)); 150 151 JScrollPane aScrollPane; 152 GridBagConstraints constraints; 153 154 // Create new layout. 155 GridBagLayout aLayout = new GridBagLayout (); 156 getContentPane().setLayout (aLayout); 157 158 // Accessible Tree. 159 maAccessibilityTree = new AccessibilityTree (); 160 // maAccessibilityTree.getComponent().setMinimumSize (new Dimension (250,300)); 161 JScrollPane aTreeScrollPane = new JScrollPane( 162 maAccessibilityTree.getComponent(), 163 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 164 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 165 aTreeScrollPane.setPreferredSize (new Dimension (400,300)); 166 167 // Object view shows details about the currently selected accessible 168 // object. 169 maObjectViewContainer = new ObjectViewContainer (); 170 // maObjectViewContainer.setPreferredSize (new Dimension (300,100)); 171 JScrollPane aObjectViewContainerScrollPane = new JScrollPane( 172 maObjectViewContainer, 173 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 174 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 175 aObjectViewContainerScrollPane.setPreferredSize (new Dimension (400,300)); 176 177 // Split pane for tree view and object view. 178 JSplitPane aLeftViewSplitPane = new JSplitPane ( 179 JSplitPane.VERTICAL_SPLIT, 180 aTreeScrollPane, 181 aObjectViewContainerScrollPane 182 ); 183 aLeftViewSplitPane.setDividerLocation (300); 184 185 // Canvas. 186 maCanvas = new Canvas (); 187 maCanvas.setTree (maAccessibilityTree.getComponent()); 188 maAccessibilityTree.SetCanvas (maCanvas); 189 JScrollPane aScrolledCanvas = new JScrollPane(maCanvas, 190 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 191 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 192 aScrolledCanvas.getViewport().setBackground (Color.RED); 193 aScrolledCanvas.setPreferredSize (new Dimension(600,400)); 194 195 // Split pane for tree view and canvas. 196 JSplitPane aViewSplitPane = new JSplitPane ( 197 JSplitPane.HORIZONTAL_SPLIT, 198 aLeftViewSplitPane, 199 aScrolledCanvas 200 ); 201 aViewSplitPane.setOneTouchExpandable(true); 202 aViewSplitPane.setDividerLocation (400); 203 204 // Text output area. 205 maMessageArea = MessageArea.Instance (); 206 // maMessageArea.setPreferredSize (new Dimension (300,50)); 207 208 // Split pane for the two views and the message area. 209 JSplitPane aSplitPane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, 210 aViewSplitPane, maMessageArea); 211 aSplitPane.setOneTouchExpandable(true); 212 addGridElement (aViewSplitPane, 0,0, 2,1, 3,3, 213 GridBagConstraints.CENTER, GridBagConstraints.BOTH); 214 215 // Button bar. 216 maButtonBar = new JPanel(); 217 GridBagLayout aButtonLayout = new GridBagLayout (); 218 maButtonBar.setLayout (new FlowLayout()); 219 addGridElement (maButtonBar, 0,3, 2,1, 1,0, 220 GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL); 221 222 // Buttons. 223 aConnectButton = createButton ("Connect", "connect"); 224 aUpdateButton = createButton ("Update", "update"); 225 aShapesButton = createButton ("Expand Shapes", "shapes"); 226 aExpandButton = createButton ("Expand All", "expand"); 227 aQuitButton = createButton ("Quit", "quit"); 228 UpdateButtonStates (); 229 230 Options.Instance().Load (msOptionsFileName); 231 232 setJMenuBar (CreateMenuBar ()); 233 234 setTitle("Accessibility Workbench " + msVersion); 235 236 pack (); 237 setVisible (true); 238 validate (); 239 repaint(); 240 } 241 242 243 244 245 /** Shortcut method for adding an object to a GridBagLayout. 246 */ 247 void addGridElement (JComponent object, 248 int x, int y, int width, int height, int weightx, int weighty, 249 int anchor, int fill) 250 { 251 GridBagConstraints constraints = new GridBagConstraints (); 252 constraints.gridx = x; 253 constraints.gridy = y; 254 constraints.gridwidth = width; 255 constraints.gridheight = height; 256 constraints.weightx = weightx; 257 constraints.weighty = weighty; 258 constraints.anchor = anchor; 259 constraints.fill = fill; 260 getContentPane().add (object, constraints); 261 } 262 263 264 265 266 /** Create a new button and place at the right most position into the 267 button bar. 268 */ 269 public JButton createButton (String title, String command) 270 { 271 JButton aButton = new JButton (title); 272 aButton.setEnabled (false); 273 aButton.setActionCommand (command); 274 aButton.addActionListener (this); 275 276 maButtonBar.add (aButton); 277 return aButton; 278 } 279 280 281 282 283 /** Create a menu bar for the application. 284 @return 285 Returns the new menu bar. The returned reference is also 286 remembered in the data member <member>maMenuBar</member>. 287 */ 288 JMenuBar CreateMenuBar () 289 { 290 // Menu bar. 291 maMenuBar = new JMenuBar (); 292 293 // File menu. 294 JMenu aFileMenu = new JMenu ("File"); 295 maMenuBar.add (aFileMenu); 296 JMenuItem aItem; 297 aItem = new JMenuItem ("Quit"); 298 aFileMenu.add (aItem); 299 aItem.addActionListener (this); 300 301 // View menu. 302 JMenu aViewMenu = new JMenu ("View"); 303 maMenuBar.add (aViewMenu); 304 ButtonGroup aGroup = new ButtonGroup (); 305 JRadioButtonMenuItem aRadioButton = new JRadioButtonMenuItem ("Whole Screen"); 306 aGroup.add (aRadioButton); 307 aViewMenu.add (aRadioButton); 308 aRadioButton.addActionListener (this); 309 aRadioButton = new JRadioButtonMenuItem ("200%"); 310 aGroup.add (aRadioButton); 311 aViewMenu.add (aRadioButton); 312 aRadioButton.addActionListener (this); 313 aRadioButton = new JRadioButtonMenuItem ("100%"); 314 aGroup.add (aRadioButton); 315 aViewMenu.add (aRadioButton); 316 aRadioButton.addActionListener (this); 317 aRadioButton = new JRadioButtonMenuItem ("50%"); 318 aGroup.add (aRadioButton); 319 aViewMenu.add (aRadioButton); 320 aRadioButton.addActionListener (this); 321 aRadioButton = new JRadioButtonMenuItem ("25%"); 322 aGroup.add (aRadioButton); 323 aViewMenu.add (aRadioButton); 324 aRadioButton.addActionListener (this); 325 aRadioButton = new JRadioButtonMenuItem ("10%"); 326 aGroup.add (aRadioButton); 327 aViewMenu.add (aRadioButton); 328 aRadioButton.addActionListener (this); 329 330 // Options menu. 331 JMenu aOptionsMenu = new JMenu ("Options"); 332 maMenuBar.add (aOptionsMenu); 333 JCheckBoxMenuItem aCBItem; 334 aCBItem = new JCheckBoxMenuItem ("Show Descriptions", maCanvas.getShowDescriptions()); 335 aOptionsMenu.add (aCBItem); 336 aCBItem.addActionListener (this); 337 338 aCBItem = new JCheckBoxMenuItem ("Show Names", maCanvas.getShowNames()); 339 aOptionsMenu.add (aCBItem); 340 aCBItem.addActionListener (this); 341 342 aCBItem = new JCheckBoxMenuItem ("Show Text", maCanvas.getShowText()); 343 aOptionsMenu.add (aCBItem); 344 aCBItem.addActionListener (this); 345 346 aCBItem = new JCheckBoxMenuItem ("Antialiased Rendering", maCanvas.getAntialiasing()); 347 aOptionsMenu.add (aCBItem); 348 aCBItem.addActionListener (this); 349 350 // Help menu. 351 JMenu aHelpMenu = new JMenu ("Help"); 352 maMenuBar.add (aHelpMenu); 353 354 aItem = new JMenuItem ("Help"); 355 aHelpMenu.add (aItem); 356 aItem.addActionListener (this); 357 358 aItem = new JMenuItem ("News"); 359 aHelpMenu.add (aItem); 360 aItem.addActionListener (this); 361 362 aItem = new JMenuItem ("About"); 363 aHelpMenu.add (aItem); 364 aItem.addActionListener (this); 365 366 return maMenuBar; 367 } 368 369 370 371 372 /** Initialize the AWB. This includes clearing the canvas, add 373 listeners, creation of a new tree model for the tree list box and 374 the update of the button states. 375 376 This method may be called any number of times. Note that all 377 actions will be carried out every time. The main purpose of a 378 second call is that of a re-initialization after a reconnect. 379 */ 380 protected void initialize () 381 { 382 maCanvas.clear(); 383 384 AccessibilityTreeModel aModel = null; 385 aModel = new AccessibilityTreeModel (createTreeModelRoot()); 386 387 aModel.setCanvas (maCanvas); 388 maAccessibilityTree.getComponent().setModel (aModel); 389 390 if (office != null) 391 { 392 // Add terminate listener. 393 if (office.getDesktop() != null) 394 office.getDesktop().addTerminateListener (this); 395 396 XExtendedToolkit xToolkit = office.getExtendedToolkit(); 397 // Remove old top window listener. 398 if (maTopWindowListener != null) 399 xToolkit.removeTopWindowListener (maQueuedTopWindowListener); 400 // Add top window listener. 401 if (xToolkit != null) 402 { 403 MessageArea.println ("registering at extended toolkit"); 404 maTopWindowListener = new TopWindowListener (aModel, office); 405 maQueuedTopWindowListener = new QueuedTopWindowListener (maTopWindowListener); 406 xToolkit.addTopWindowListener (maQueuedTopWindowListener); 407 maTopWindowListener.Initialize (); 408 } 409 else 410 maTopWindowListener = null; 411 } 412 413 mbInitialized = true; 414 UpdateButtonStates (); 415 } 416 417 418 419 420 /** Update the states of the buttons according to the internal state of 421 the AWB. 422 */ 423 protected void UpdateButtonStates () 424 { 425 aConnectButton.setEnabled (mbInitialized); 426 aQuitButton.setEnabled (mbInitialized); 427 aUpdateButton.setEnabled (mbInitialized); 428 aExpandButton.setEnabled (mbInitialized); 429 aShapesButton.setEnabled (mbInitialized); 430 } 431 432 433 434 /** Callback for GUI actions from the buttons. 435 */ 436 public void actionPerformed (java.awt.event.ActionEvent e) 437 { 438 if (e.getActionCommand().equals("connect")) 439 { 440 office.connect(); 441 initialize (); 442 } 443 else if (e.getActionCommand().equals("quit")) 444 { 445 AccessibilityTreeModel aModel = (AccessibilityTreeModel)maAccessibilityTree.getComponent().getModel(); 446 aModel.clear(); 447 System.exit (0); 448 } 449 else if (e.getActionCommand().equals("update")) 450 { 451 initialize (); 452 } 453 else if (e.getActionCommand().equals("shapes")) 454 { 455 Cursor aCursor = getCursor(); 456 setCursor (new Cursor (Cursor.WAIT_CURSOR)); 457 maAccessibilityTree.expandShapes(); 458 setCursor (aCursor); 459 } 460 else if (e.getActionCommand().equals("expand")) 461 { 462 Cursor aCursor = getCursor(); 463 setCursor (new Cursor (Cursor.WAIT_CURSOR)); 464 maAccessibilityTree.expandAll(); 465 setCursor (aCursor); 466 } 467 else if (e.getActionCommand().equals ("Quit")) 468 { 469 System.out.println ("exiting"); 470 System.exit (0); 471 } 472 else if (e.getActionCommand().equals ("Show Descriptions")) 473 { 474 maCanvas.setShowDescriptions ( ! maCanvas.getShowDescriptions()); 475 Options.Instance().Save (msOptionsFileName); 476 } 477 else if (e.getActionCommand().equals ("Show Names")) 478 { 479 maCanvas.setShowNames ( ! maCanvas.getShowNames()); 480 Options.Instance().Save (msOptionsFileName); 481 } 482 else if (e.getActionCommand().equals ("Antialiased Rendering")) 483 { 484 maCanvas.setAntialiasing ( ! maCanvas.getAntialiasing()); 485 Options.Instance().Save (msOptionsFileName); 486 } 487 else if (e.getActionCommand().equals ("Help")) 488 { 489 HelpWindow.Instance().loadFile ("help.html"); 490 } 491 else if (e.getActionCommand().equals ("News")) 492 { 493 try{ 494 HelpWindow.Instance().loadFile ("news.html"); 495 } catch (Exception ex) {} 496 } 497 else if (e.getActionCommand().equals ("About")) 498 { 499 HelpWindow.Instance().loadFile ("about.html"); 500 } 501 else if (e.getActionCommand().equals ("Whole Screen")) 502 { 503 maCanvas.setZoomMode (Canvas.WHOLE_SCREEN); 504 Options.Instance().Save (msOptionsFileName); 505 } 506 else if (e.getActionCommand().equals ("200%")) 507 { 508 maCanvas.setZoomMode (200); 509 Options.Instance().Save (msOptionsFileName); 510 } 511 else if (e.getActionCommand().equals ("100%")) 512 { 513 maCanvas.setZoomMode (100); 514 Options.Instance().Save (msOptionsFileName); 515 } 516 else if (e.getActionCommand().equals ("50%")) 517 { 518 maCanvas.setZoomMode (50); 519 Options.Instance().Save (msOptionsFileName); 520 } 521 else if (e.getActionCommand().equals ("25%")) 522 { 523 maCanvas.setZoomMode (25); 524 Options.Instance().Save (msOptionsFileName); 525 } 526 else if (e.getActionCommand().equals ("10%")) 527 { 528 maCanvas.setZoomMode (10); 529 Options.Instance().Save (msOptionsFileName); 530 } 531 else 532 { 533 System.err.println("unknown command " + e.getActionCommand()); 534 } 535 } 536 537 538 539 540 /** Create an AccessibilityTreeModel root which contains the documents 541 (top windows) that are present at the moment. 542 */ 543 private AccessibleTreeNode createTreeModelRoot() 544 { 545 // create root node 546 VectorNode aRoot = new VectorNode ("Accessibility Tree", null); 547 if (maTopWindowListener != null) 548 maTopWindowListener.Initialize (); 549 return aRoot; 550 } 551 552 553 // TreeSelectionListener 554 public void valueChanged (TreeSelectionEvent aEvent) 555 { 556 TreePath aPath = aEvent.getPath(); 557 Object aObject = aPath.getLastPathComponent(); 558 if (aObject instanceof AccTreeNode) 559 { 560 AccTreeNode aNode = (AccTreeNode) aObject; 561 XAccessibleContext xContext = aNode.getContext(); 562 maObjectViewContainer.SetObject (xContext); 563 } 564 } 565 566 567 568 569 // XEventListener 570 public void disposing( com.sun.star.lang.EventObject aSourceObj ) 571 { 572 XFrame xFrame = UnoRuntime.queryInterface(XFrame.class, aSourceObj.Source); 573 574 if( xFrame != null ) 575 System.out.println("frame disposed"); 576 else 577 System.out.println("controller disposed"); 578 } 579 580 581 582 583 // XTerminateListener 584 public void queryTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException 585 { 586 System.out.println ("Terminate Event : " + aEvent); 587 } 588 589 590 591 592 // XTerminateListener 593 public void notifyTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException 594 { 595 System.out.println ("Notify Termination Event : " + aEvent); 596 } 597 598 599 600 /// The Singleton Workbench object. 601 private static AccessibilityWorkBench 602 saWorkBench = null; 603 604 protected SimpleOffice 605 office; 606 protected InformationWriter 607 info; 608 609 private XModel 610 mxModel; 611 private JPanel 612 maMainPanel, 613 maButtonBar; 614 private Canvas 615 maCanvas; 616 private AccessibilityTree 617 maAccessibilityTree; 618 private ObjectViewContainer 619 maObjectViewContainer; 620 private JScrollPane 621 maScrollPane; 622 private MessageArea 623 maMessageArea; 624 private JButton 625 aConnectButton, 626 aQuitButton, 627 aUpdateButton, 628 aExpandButton, 629 aShapesButton; 630 private JMenuBar 631 maMenuBar; 632 private String 633 msMessage; 634 private boolean 635 mbInitialized; 636 private TopWindowListener 637 maTopWindowListener; 638 private QueuedTopWindowListener 639 maQueuedTopWindowListener; 640 } 641