001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.visualweb.gravy.actions;
043:
044: import java.awt.Component;
045: import java.awt.Container;
046: import java.awt.EventQueue;
047: import java.awt.event.ActionEvent;
048: import java.awt.event.KeyEvent;
049: import javax.swing.tree.TreePath;
050:
051: import org.netbeans.jellytools.JellyVersion;
052: import org.netbeans.modules.visualweb.gravy.MainWindowOperator;
053: import org.netbeans.modules.visualweb.gravy.Util; // mdk import org.netbeans.jellytools.nodes.Node;
054: import org.netbeans.modules.visualweb.gravy.nodes.Node;
055: import org.netbeans.jemmy.EventTool;
056: import org.netbeans.jemmy.JemmyException;
057: import org.netbeans.jemmy.JemmyProperties;
058: import org.netbeans.jemmy.Timeouts;
059: import org.netbeans.jemmy.drivers.input.KeyRobotDriver;
060: import org.netbeans.jemmy.operators.ComponentOperator;
061: import org.netbeans.jemmy.operators.JPopupMenuOperator;
062: import org.netbeans.jemmy.operators.Operator;
063: import org.netbeans.jemmy.operators.Operator.ComponentVisualizer;
064: import org.netbeans.jemmy.operators.Operator.DefaultStringComparator;
065: import org.netbeans.jemmy.operators.Operator.StringComparator;
066: import org.netbeans.jemmy.util.EmptyVisualizer;
067: import org.openide.util.actions.SystemAction;
068:
069: /** Ancestor class for all blocking actions.<p>
070: * It handles performing action through main menu (MENU_MODE), popup menu
071: * (POPUP_MODE), IDE SystemAction API call (API_MODE) or through keyboard shortcut
072: * (SHORTCUT_MODE).<p>
073: * Action can be performed in exact mode by calling performMenu(...),
074: * performPopup(...), performAPI(...) or performShortcut(...).<p>
075: * If exact mode is not supported by the action it throws
076: * UnsupportedOperationException.<p>
077: * Current implementation supports MENU_MODE when menuPath is defined, POPUP_MODE
078: * when popupPath is defined, API_MODE when systemActionClass is defined and
079: * SHORTCUT_MODE when shorcut is defined (see Action constructors).<p>
080: * Action also can be performed using runtime default mode by calling perform(...).<p>
081: * When default mode is not support by the action other modes are tried till
082: * supported mode found and action is performed.
083: *
084: * <BR>Timeouts used: <BR>
085: * Action.WaitAfterShortcutTimeout - time to sleep between shortcuts in sequence (default 0) <BR>
086: *
087: * @author <a href="mailto:adam.sotona@sun.com">Adam Sotona</a> */
088: public class Action {
089:
090: /** through menu action performing mode */
091: public static final int MENU_MODE = 0;
092: /** through popup menu action performing mode */
093: public static final int POPUP_MODE = 1;
094: /** through API action performing mode */
095: public static final int API_MODE = 2;
096: /** through shortcut action performing mode */
097: public static final int SHORTCUT_MODE = 3;
098:
099: /** sleep time between nodes selection and action execution */
100: protected static final long SELECTION_WAIT_TIME = 300;
101: /** sleep time after action execution */
102: protected static final long AFTER_ACTION_WAIT_TIME = 0;
103: /** sleep time between sequence of shortcuts */
104: protected static final long WAIT_AFTER_SHORTCUT_TIMEOUT = 0;
105:
106: private static final int sequence[][] = {
107: { MENU_MODE, POPUP_MODE, SHORTCUT_MODE, API_MODE },
108: { POPUP_MODE, MENU_MODE, SHORTCUT_MODE, API_MODE },
109: { API_MODE, POPUP_MODE, MENU_MODE, SHORTCUT_MODE },
110: { SHORTCUT_MODE, POPUP_MODE, MENU_MODE, API_MODE } };
111:
112: /** menu path of current action or null when MENU_MODE is not supported */
113: protected String menuPath;
114: /** popup menu path of current action or null when POPUP_MODE is not supported */
115: protected String popupPath;
116: /** SystemAction class of current action or null when API_MODE is not supported */
117: protected Class systemActionClass;
118: /** array of shortcuts of current action or null when SHORTCUT_MODE is not supported */
119: protected Shortcut[] shortcuts;
120:
121: /** Comparator used as default for all Action instances. It is set in static clause. */
122: private static StringComparator defaultComparator;
123: /** Comparator used for this action instance. */
124: private StringComparator comparator;
125:
126: /** creates new Action instance without API_MODE and SHORTCUT_MODE support
127: * @param menuPath action path in main menu (use null value if menu mode is not supported)
128: * @param popupPath action path in popup menu (use null value if popup mode shell is not supported) */
129: public Action(String menuPath, String popupPath) {
130: this (menuPath, popupPath, null, (Shortcut[]) null);
131: }
132:
133: /** creates new Action instance without SHORTCUT_MODE support
134: * @param menuPath action path in main menu (use null value if menu mode is not supported)
135: * @param popupPath action path in popup menu (use null value if popup mode is not supported)
136: * @param systemActionClass String class name of SystemAction (use null value if API mode is not supported) */
137: public Action(String menuPath, String popupPath,
138: String systemActionClass) {
139: this (menuPath, popupPath, systemActionClass, (Shortcut[]) null);
140: }
141:
142: /** creates new Action instance without API_MODE support
143: * @param shortcuts array of Shortcut instances (use null value if shorcut mode is not supported)
144: * @param menuPath action path in main menu (use null value if menu mode is not supported)
145: * @param popupPath action path in popup menu (use null value if popup mode shell is not supported) */
146: public Action(String menuPath, String popupPath,
147: Shortcut[] shortcuts) {
148: this (menuPath, popupPath, null, shortcuts);
149: }
150:
151: /** creates new Action instance without API_MODE support
152: * @param shortcut Shortcut (use null value if menu mode is not supported)
153: * @param menuPath action path in main menu (use null value if menu mode is not supported)
154: * @param popupPath action path in popup menu (use null value if popup mode shell is not supported) */
155: public Action(String menuPath, String popupPath, Shortcut shortcut) {
156: this (menuPath, popupPath, null, new Shortcut[] { shortcut });
157: }
158:
159: /** creates new Action instance
160: * @param shortcuts array of Shortcut instances (use null value if shortcut mode is not supported)
161: * @param menuPath action path in main menu (use null value if menu mode is not supported)
162: * @param popupPath action path in popup menu (use null value if popup mode is not supported)
163: * @param systemActionClass String class name of SystemAction (use null value if API mode is not supported) */
164: public Action(String menuPath, String popupPath,
165: String systemActionClass, Shortcut[] shortcuts) {
166: this .menuPath = menuPath;
167: this .popupPath = popupPath;
168: if (systemActionClass == null) {
169: this .systemActionClass = null;
170: } else
171: try {
172: this .systemActionClass = Class
173: .forName(systemActionClass);
174: } catch (ClassNotFoundException e) {
175: this .systemActionClass = null;
176: }
177: this .shortcuts = shortcuts;
178: }
179:
180: /** creates new Action instance
181: * @param shortcut Shortcut String (use null value if menu mode is not supported)
182: * @param menuPath action path in main menu (use null value if menu mode is not supported)
183: * @param popupPath action path in popup menu (use null value if popup mode is not supported)
184: * @param systemActionClass String class name of SystemAction (use null value if API mode is not supported) */
185: public Action(String menuPath, String popupPath,
186: String systemActionClass, Shortcut shortcut) {
187: this (menuPath, popupPath, systemActionClass,
188: new Shortcut[] { shortcut });
189: }
190:
191: static {
192: // Checks if you run on correct jemmy version. Writes message to jemmy log if not.
193: JellyVersion.checkJemmyVersion();
194:
195: if (JemmyProperties.getCurrentProperty("Action.DefaultMode") == null)
196: JemmyProperties.setCurrentProperty("Action.DefaultMode",
197: new Integer(POPUP_MODE));
198: Timeouts.initDefault("Action.WaitAfterShortcutTimeout",
199: WAIT_AFTER_SHORTCUT_TIMEOUT);
200: // Set case sensitive comparator as default because of
201: // very often clash between Cut and Execute menu items.
202: // Substring criterion is set according to default string comparator
203: boolean compareExactly = !Operator.getDefaultStringComparator()
204: .equals("abc", "a"); // NOI18N
205: defaultComparator = new DefaultStringComparator(compareExactly,
206: true);
207: }
208:
209: private void perform(int mode) {
210: switch (mode) {
211: case POPUP_MODE:
212: performPopup();
213: break;
214: case MENU_MODE:
215: performMenu();
216: break;
217: case API_MODE:
218: performAPI();
219: break;
220: case SHORTCUT_MODE:
221: performShortcut();
222: break;
223: default:
224: throw new IllegalArgumentException("Wrong Action.MODE");
225: }
226: }
227:
228: private void perform(Node node, int mode) {
229: switch (mode) {
230: case POPUP_MODE:
231: performPopup(node);
232: break;
233: case MENU_MODE:
234: performMenu(node);
235: break;
236: case API_MODE:
237: performAPI(node);
238: break;
239: case SHORTCUT_MODE:
240: performShortcut(node);
241: break;
242: default:
243: throw new IllegalArgumentException("Wrong Action.MODE");
244: }
245: }
246:
247: private void perform(Node[] nodes, int mode) {
248: switch (mode) {
249: case POPUP_MODE:
250: performPopup(nodes);
251: break;
252: case MENU_MODE:
253: performMenu(nodes);
254: break;
255: case API_MODE:
256: performAPI(nodes);
257: break;
258: case SHORTCUT_MODE:
259: performShortcut(nodes);
260: break;
261: default:
262: throw new IllegalArgumentException("Wrong Action.MODE");
263: }
264: }
265:
266: private void perform(ComponentOperator component, int mode) {
267: switch (mode) {
268: case POPUP_MODE:
269: performPopup(component);
270: break;
271: case MENU_MODE:
272: performMenu(component);
273: break;
274: case API_MODE:
275: performAPI(component);
276: break;
277: case SHORTCUT_MODE:
278: performShortcut(component);
279: break;
280: default:
281: throw new IllegalArgumentException("Wrong Action.MODE");
282: }
283: }
284:
285: /** performs action depending on default mode,<br>
286: * calls performPopup(), performMenu() or performAPI(),<br>
287: * when default mode is not supported, others are tried */
288: public void perform() {
289: int modes[] = sequence[getDefaultMode()];
290: for (int i = 0; i < modes.length; i++)
291: try {
292: perform(modes[i]);
293: return;
294: } catch (UnsupportedOperationException e) {
295: }
296: }
297:
298: /** performs action depending on default mode,<br>
299: * calls performPopup(), performMenu() or performAPI(),<br>
300: * when default mode is not supported, others are tried
301: * @param node node to be action performed on */
302: public void perform(Node node) {
303: int modes[] = sequence[getDefaultMode()];
304: for (int i = 0; i < modes.length; i++)
305: try {
306: perform(node, modes[i]);
307: return;
308: } catch (UnsupportedOperationException e) {
309: }
310: }
311:
312: /** performs action depending on default mode,<br>
313: * calls performPopup(), performMenu() or performAPI(),<br>
314: * when default mode is not supported, others are tried
315: * @param nodes nodes to be action performed on */
316: public void perform(Node[] nodes) {
317: int modes[] = sequence[getDefaultMode()];
318: for (int i = 0; i < modes.length; i++)
319: try {
320: perform(nodes, modes[i]);
321: return;
322: } catch (UnsupportedOperationException e) {
323: }
324: }
325:
326: /** performs action depending on default mode,<br>
327: * calls performPopup(), performMenu() or performAPI(),<br>
328: * when default mode is not supported, others are tried
329: * @param component component to be action performed on */
330: public void perform(ComponentOperator component) {
331: int modes[] = sequence[getDefaultMode()];
332: for (int i = 0; i < modes.length; i++)
333: try {
334: perform(component, modes[i]);
335: return;
336: } catch (UnsupportedOperationException e) {
337: }
338: }
339:
340: /** performs action through main menu
341: * @throws UnsupportedOperationException when action does not support menu mode */
342: public void performMenu() {
343: if (menuPath == null) {
344: throw new UnsupportedOperationException(getClass()
345: .toString()
346: + " does not define menu path");
347: }
348: // Need to wait here to be more reliable.
349: // TBD - It can be removed after issue 23663 is solved.
350: new EventTool().waitNoEvent(500);
351: Util.getMainMenu().pushMenu(menuPath);
352: try {
353: Thread.sleep(AFTER_ACTION_WAIT_TIME);
354: } catch (Exception e) {
355: throw new JemmyException("Sleeping interrupted", e);
356: }
357: }
358:
359: /** performs action through main menu
360: * @param node node to be action performed on
361: * @throws UnsupportedOperationException when action does not support menu mode */
362: public void performMenu(Node node) {
363: performMenu(new Node[] { node });
364: }
365:
366: /** performs action through main menu
367: * @param nodes nodes to be action performed on
368: * @throws UnsupportedOperationException when action does not support menu mode */
369: public void performMenu(Node[] nodes) {
370: if (menuPath == null)
371: throw new UnsupportedOperationException(getClass()
372: .toString()
373: + " does not define menu path");
374: testNodes(nodes);
375: nodes[0].select();
376: for (int i = 1; i < nodes.length; i++)
377: nodes[i].addSelectionPath();
378: try {
379: Thread.sleep(SELECTION_WAIT_TIME);
380: } catch (Exception e) {
381: throw new JemmyException("Sleeping interrupted", e);
382: }
383: performMenu();
384: }
385:
386: /** performs action through main menu
387: * @param component component to be action performed on
388: * @throws UnsupportedOperationException when action does not support menu mode */
389: public void performMenu(ComponentOperator component) {
390: component.getFocus();
391: try {
392: Thread.sleep(SELECTION_WAIT_TIME);
393: } catch (Exception e) {
394: throw new JemmyException("Sleeping interrupted", e);
395: }
396: performMenu();
397: }
398:
399: /** performs action through popup menu
400: * @throws UnsupportedOperationException */
401: public void performPopup() {
402: throw new UnsupportedOperationException(getClass().toString()
403: + " does not implement performPopup()");
404: }
405:
406: /** performs action through popup menu
407: * @param node node to be action performed on
408: * @throws UnsupportedOperationException when action does not support popup mode */
409: public void performPopup(Node node) {
410: performPopup(new Node[] { node });
411: }
412:
413: /** performs action through popup menu
414: * @param nodes nodes to be action performed on
415: * @throws UnsupportedOperationException when action does not support popup mode */
416: public void performPopup(Node[] nodes) {
417: if (popupPath == null) {
418: throw new UnsupportedOperationException(getClass()
419: .toString()
420: + " does not define popup path");
421: }
422: testNodes(nodes);
423: TreePath paths[] = new TreePath[nodes.length];
424: for (int i = 0; i < nodes.length; i++) {
425: paths[i] = nodes[i].getTreePath();
426: }
427: ComponentVisualizer treeVisualizer = nodes[0].tree()
428: .getVisualizer();
429: ComponentVisualizer oldVisualizer = null;
430: // If visualizer of JTreeOperator is EmptyVisualizer, we need
431: // to avoid making tree component visible in callPopup method.
432: // So far only known case is tree from TreeTableOperator.
433: if (treeVisualizer instanceof EmptyVisualizer) {
434: oldVisualizer = Operator.getDefaultComponentVisualizer();
435: Operator.setDefaultComponentVisualizer(treeVisualizer);
436: }
437: // Need to wait here to be more reliable.
438: // TBD - It can be removed after issue 23663 is solved.
439: new EventTool().waitNoEvent(500);
440: JPopupMenuOperator popup = new JPopupMenuOperator(nodes[0]
441: .tree().callPopupOnPaths(paths));
442: // restore previously used default visualizer
443: if (oldVisualizer != null) {
444: Operator.setDefaultComponentVisualizer(oldVisualizer);
445: }
446: popup.pushMenu(popupPath, "|", getComparator());
447: try {
448: Thread.sleep(AFTER_ACTION_WAIT_TIME);
449: } catch (Exception e) {
450: throw new JemmyException("Sleeping interrupted", e);
451: }
452: }
453:
454: /** performs action through popup menu
455: * @param component component to be action performed on
456: * @throws UnsupportedOperationException when action does not support popup mode */
457: public void performPopup(ComponentOperator component) {
458: if (popupPath == null) {
459: throw new UnsupportedOperationException(getClass()
460: .toString()
461: + " does not define popup path");
462: }
463: // Need to wait here to be more reliable.
464: // TBD - It can be removed after issue 23663 is solved.
465: new EventTool().waitNoEvent(500);
466: component.clickForPopup();
467: new JPopupMenuOperator(component).pushMenu(popupPath, "|",
468: getComparator());
469: try {
470: Thread.sleep(AFTER_ACTION_WAIT_TIME);
471: } catch (Exception e) {
472: throw new JemmyException("Sleeping interrupted", e);
473: }
474: }
475:
476: /** performs action through API
477: * @throws UnsupportedOperationException when action does not support API mode */
478: public void performAPI() {
479: if (systemActionClass == null) {
480: throw new UnsupportedOperationException(getClass()
481: .toString()
482: + " does not support API call.");
483: }
484: try {
485: // actions has to be invoked in dispatch thread (see http://www.netbeans.org/issues/show_bug.cgi?id=35755)
486: EventQueue.invokeAndWait(new Runnable() {
487: public void run() {
488: if (SystemAction.class
489: .isAssignableFrom(systemActionClass)) {
490: // SystemAction used in IDE
491: SystemAction.get(systemActionClass)
492: .actionPerformed(
493: new ActionEvent(
494: new Container(), 0,
495: null));
496: } else {
497: // action implements javax.swing.Action
498: try {
499: ((javax.swing.Action) systemActionClass
500: .newInstance())
501: .actionPerformed(null);
502: } catch (Exception e) {
503: throw new JemmyException(
504: "Exception when trying to create instance of action \""
505: + systemActionClass
506: .getName() + "\".",
507: e);
508: }
509: }
510: }
511: });
512: Thread.sleep(AFTER_ACTION_WAIT_TIME);
513: } catch (Exception e) {
514: throw new JemmyException("Interrupted", e);
515: }
516: }
517:
518: /** performs action through API
519: * @param node node to be action performed on
520: * @throws UnsupportedOperationException when action does not support API mode */
521: public void performAPI(Node node) {
522: performAPI(new Node[] { node });
523: }
524:
525: /** performs action through API
526: * @param nodes nodes to be action performed on
527: * @throws UnsupportedOperationException when action does not support API mode */
528: public void performAPI(Node[] nodes) {
529: if (systemActionClass == null)
530: throw new UnsupportedOperationException(getClass()
531: .toString()
532: + " does not define SystemAction");
533: testNodes(nodes);
534: nodes[0].select();
535: for (int i = 1; i < nodes.length; i++)
536: nodes[i].addSelectionPath();
537: try {
538: Thread.sleep(SELECTION_WAIT_TIME);
539: } catch (Exception e) {
540: throw new JemmyException("Sleeping interrupted", e);
541: }
542: performAPI();
543: }
544:
545: /** performs action through API
546: * @param component component to be action performed on
547: * @throws UnsupportedOperationException when action does not support API mode */
548: public void performAPI(ComponentOperator component) {
549: component.getFocus();
550: try {
551: Thread.sleep(SELECTION_WAIT_TIME);
552: } catch (Exception e) {
553: throw new JemmyException("Sleeping interrupted", e);
554: }
555: performAPI();
556: }
557:
558: /** performs action through shortcut
559: * @throws UnsupportedOperationException if no shortcut is defined */
560: public void performShortcut() {
561: if (shortcuts == null) {
562: throw new UnsupportedOperationException(getClass()
563: .toString()
564: + " does not define shortcut");
565: }
566: for (int i = 0; i < shortcuts.length; i++) {
567: new KeyRobotDriver(null).pushKey(null, shortcuts[i]
568: .getKeyCode(), shortcuts[i].getKeyModifiers(),
569: JemmyProperties.getCurrentTimeouts().create(
570: "Timeouts.DeltaTimeout"));
571: JemmyProperties.getProperties().getTimeouts().sleep(
572: "Action.WaitAfterShortcutTimeout");
573: }
574: try {
575: Thread.sleep(AFTER_ACTION_WAIT_TIME);
576: } catch (Exception e) {
577: throw new JemmyException("Sleeping interrupted", e);
578: }
579: }
580:
581: /** performs action through shortcut
582: * @param node node to be action performed on
583: * @throws UnsupportedOperationException when action does not support shortcut mode */
584: public void performShortcut(Node node) {
585: performShortcut(new Node[] { node });
586: }
587:
588: /** performs action through shortcut
589: * @param nodes nodes to be action performed on
590: * @throws UnsupportedOperationException when action does not support shortcut mode */
591: public void performShortcut(Node[] nodes) {
592: if (shortcuts == null) {
593: throw new UnsupportedOperationException(getClass()
594: .toString()
595: + " does not define shortcut");
596: }
597: testNodes(nodes);
598: nodes[0].select();
599: for (int i = 1; i < nodes.length; i++)
600: nodes[i].addSelectionPath();
601: try {
602: Thread.sleep(SELECTION_WAIT_TIME);
603: } catch (Exception e) {
604: throw new JemmyException("Sleeping interrupted", e);
605: }
606: performShortcut();
607: }
608:
609: /** performs action through shortcut
610: * @param component component to be action performed on
611: * @throws UnsupportedOperationException when action does not support shortcut mode */
612: public void performShortcut(ComponentOperator component) {
613: component.getFocus();
614: try {
615: Thread.sleep(SELECTION_WAIT_TIME);
616: } catch (Exception e) {
617: throw new JemmyException("Sleeping interrupted", e);
618: }
619: performShortcut();
620: }
621:
622: /** tests if nodes are all from the same tree
623: * @param nodes nodes
624: * @throws IllegalArgumentException when given nodes does not pass */
625: protected void testNodes(Node[] nodes) {
626: if ((nodes == null) || (nodes.length == 0))
627: throw new IllegalArgumentException(
628: "argument nodes is null or empty");
629: Class myClass = getClass();
630: Component nodesTree = nodes[0].tree().getSource();
631: for (int i = 0; i < nodes.length; i++) {
632: // if (!nodes[i].hasAction(myClass))
633: // throw new IllegalArgumentException(this.toString()+" could not be performed on "+nodes[i].toString());
634: if (nodes[i] == null)
635: throw new IllegalArgumentException(
636: "argument nodes contains null value");
637: if (!nodesTree.equals(nodes[i].tree().getSource()))
638: throw new IllegalArgumentException(nodes[i].toString()
639: + " is from different tree");
640: }
641: }
642:
643: /** Returns default mode in which actions are performed.
644: * @return default mode in which actions are performed
645: * @see #POPUP_MODE
646: * @see #MENU_MODE
647: * @see #API_MODE
648: * @see #SHORTCUT_MODE
649: */
650: public int getDefaultMode() {
651: int mode = (((Integer) JemmyProperties
652: .getCurrentProperty("Action.DefaultMode")).intValue());
653: if (mode < 0 || mode > 3)
654: return POPUP_MODE;
655: return mode;
656: }
657:
658: /** Sets default mode in which actions are performed. If given mode value
659: * is not valid, it sets {@link #POPUP_MODE} as default.
660: * @param mode mode to be set
661: * @return previous value
662: * @see #POPUP_MODE
663: * @see #MENU_MODE
664: * @see #API_MODE
665: * @see #SHORTCUT_MODE
666: */
667: public int setDefaultMode(int mode) {
668: int oldMode = (((Integer) JemmyProperties
669: .getCurrentProperty("Action.DefaultMode")).intValue());
670: if (mode < 0 || mode > 3) {
671: mode = POPUP_MODE;
672: }
673: JemmyProperties.setCurrentProperty("Action.DefaultMode",
674: new Integer(mode));
675: return oldMode;
676: }
677:
678: /** Sets comparator fot this action. Comparator is used for all actions
679: * after this method is called.
680: * @param comparator new comparator to be set (e.g.
681: * new Operator.DefaultStringComparator(true, true);
682: * to search string item exactly and case sensitive)
683: */
684: public void setComparator(StringComparator comparator) {
685: this .comparator = comparator;
686: }
687:
688: /** Gets comparator set for this action instance.
689: * @return comparator set for this action instance.
690: */
691: public StringComparator getComparator() {
692: if (comparator == null) {
693: comparator = defaultComparator;
694: }
695: return comparator;
696: }
697:
698: /** getter for popup menu path
699: * @return String popup menu path (or null if not suported)
700: */
701: public String getPopupPath() {
702: return popupPath;
703: }
704:
705: /** getter for main menu path
706: * @return String main menu path (or null if not suported)
707: */
708: public String getMenuPath() {
709: return menuPath;
710: }
711:
712: /** getter for system action class
713: * @return Class of system action (or null if not suported)
714: */
715: public Class getSystemActionClass() {
716: return systemActionClass;
717: }
718:
719: /** getter for array of shortcuts
720: * @return Shortcut[] (or null if not suported)
721: */
722: public Shortcut[] getShortcuts() {
723: return shortcuts;
724: }
725:
726: /** Checks whether this action is enabled. If IDE system action class
727: * is defined, it calls its isEnabled() method else if main menu path is
728: * defined, it checks menu item is enabled. Otherwise it throws
729: * UnsupportedOperationException.
730: * @return true if this action is enabled; false otherwise
731: */
732: public boolean isEnabled() {
733: if (systemActionClass != null) {
734: return SystemAction.get(systemActionClass).isEnabled();
735: } else if (menuPath != null) {
736: return Util.getMainMenu().showMenuItem(menuPath, "|",
737: getComparator()).isEnabled();
738: } else {
739: throw new UnsupportedOperationException("Cannot detect if "
740: + getClass().getName() + " is enabled.");
741: }
742: }
743:
744: /** Checks whether this action on given nodes is enabled. If IDE system action class
745: * is defined, it calls its isEnabled() method. Nodes are selected first.
746: * Else if popup menu path is defined, it checks menu item is enabled.
747: * Otherwise it throws UnsupportedOperationException.
748: * @param nodes array of nodes to be selected before a check
749: * @return true if this action is enabled; false otherwise
750: */
751: public boolean isEnabled(Node[] nodes) {
752: testNodes(nodes);
753: if (systemActionClass != null) {
754: nodes[0].select();
755: for (int i = 1; i < nodes.length; i++)
756: nodes[i].addSelectionPath();
757: try {
758: Thread.sleep(SELECTION_WAIT_TIME);
759: } catch (Exception e) {
760: throw new JemmyException("Sleeping interrupted", e);
761: }
762: return SystemAction.get(systemActionClass).isEnabled();
763: } else if (popupPath != null) {
764: TreePath paths[] = new TreePath[nodes.length];
765: for (int i = 0; i < nodes.length; i++) {
766: paths[i] = nodes[i].getTreePath();
767: }
768: ComponentVisualizer treeVisualizer = nodes[0].tree()
769: .getVisualizer();
770: ComponentVisualizer oldVisualizer = null;
771: // If visualizer of JTreeOperator is EmptyVisualizer, we need
772: // to avoid making tree component visible in callPopup method.
773: // So far only known case is tree from TreeTableOperator.
774: if (treeVisualizer instanceof EmptyVisualizer) {
775: oldVisualizer = Operator
776: .getDefaultComponentVisualizer();
777: Operator.setDefaultComponentVisualizer(treeVisualizer);
778: }
779: // Need to wait here to be more reliable.
780: // TBD - It can be removed after issue 23663 is solved.
781: new EventTool().waitNoEvent(500);
782: JPopupMenuOperator popup = new JPopupMenuOperator(nodes[0]
783: .tree().callPopupOnPaths(paths));
784: // restore previously used default visualizer
785: if (oldVisualizer != null) {
786: Operator.setDefaultComponentVisualizer(oldVisualizer);
787: }
788: return popup.showMenuItem(popupPath, "|", getComparator())
789: .isEnabled();
790: } else {
791: throw new UnsupportedOperationException("Cannot detect if "
792: + getClass().getName() + " is enabled.");
793: }
794: }
795:
796: /** Checks whether this action on given node is enabled. If IDE system action class
797: * is defined, it calls its isEnabled() method. Node is selected first.
798: * Else if popup menu path is defined, it checks menu item is enabled.
799: * Otherwise it throws UnsupportedOperationException.
800: * @param node node to be selected before a check
801: * @return true if this action is enabled; false otherwise
802: */
803: public boolean isEnabled(Node node) {
804: return isEnabled(new Node[] { node });
805: }
806:
807: /** Checks whether this action is enabled for given ComponentOperator.
808: * First it makes component visible and focused.
809: * If IDE system action class is defined, it calls its isEnabled() method.
810: * Else if main menu path is defined, it checks menu item is enabled.
811: * Otherwise it throws UnsupportedOperationException.
812: * @param componentOperator instance of ComponentOperator
813: * @return true if this action is enabled; false otherwise
814: */
815: public boolean isEnabled(ComponentOperator componentOperator) {
816: componentOperator.makeComponentVisible();
817: componentOperator.getFocus();
818: if (systemActionClass != null) {
819: return SystemAction.get(systemActionClass).isEnabled();
820: } else if (popupPath != null) {
821: // Need to wait here to be more reliable.
822: // TBD - It can be removed after issue 23663 is solved.
823: new EventTool().waitNoEvent(500);
824: componentOperator.clickForPopup();
825: return new JPopupMenuOperator(componentOperator)
826: .showMenuItem(popupPath, "|", getComparator())
827: .isEnabled();
828: } else if (menuPath != null) {
829: return Util.getMainMenu().showMenuItem(menuPath, "|",
830: getComparator()).isEnabled();
831: } else {
832: throw new UnsupportedOperationException("Cannot detect if "
833: + getClass().getName() + " is enabled.");
834: }
835: }
836:
837: /** This class defines keyboard shortcut for action execution */
838: public static class Shortcut extends Object {
839: /** key code of shortcut (see KeyEvent) */
840: protected int keyCode;
841: /** key modifiers of shortcut (see KeyEvent) */
842: protected int keyModifiers;
843:
844: /** creates new shortcut
845: * @param keyCode int key code (see KeyEvent) */
846: public Shortcut(int keyCode) {
847: this (keyCode, 0);
848: }
849:
850: /** creates new shortcut
851: * @param keyCode int key code (see KeyEvent)
852: * @param keyModifiers int key modifiers (see KeyEvent) */
853: public Shortcut(int keyCode, int keyModifiers) {
854: this .keyCode = keyCode;
855: this .keyModifiers = keyModifiers;
856: }
857:
858: /** getter for key code
859: * @return int key code */
860: public int getKeyCode() {
861: return keyCode;
862: }
863:
864: /** getter for key modifiers
865: * @return int key modifiers */
866: public int getKeyModifiers() {
867: return keyModifiers;
868: }
869:
870: /** returns String representation of shortcut
871: * @return String representation of shortcut */
872: public String toString() {
873: String s = KeyEvent.getKeyModifiersText(getKeyModifiers());
874: return s + (s.length() > 0 ? "+" : "")
875: + KeyEvent.getKeyText(getKeyCode());
876: }
877: }
878: }
|