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