1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.jemmy.swt; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 29 import org.eclipse.swt.widgets.Control; 30 import org.eclipse.swt.widgets.Display; 31 import org.eclipse.swt.widgets.MenuItem; 32 import org.eclipse.swt.widgets.Shell; 33 import org.jemmy.JemmyException; 34 import org.jemmy.control.Wrap; 35 import org.jemmy.env.Timeout; 36 import org.jemmy.interfaces.Keyboard.KeyboardButton; 37 import org.jemmy.interfaces.Keyboard.KeyboardButtons; 38 import org.jemmy.interfaces.MenuSelectable; 39 import org.jemmy.lookup.LookupCriteria; 40 import org.jemmy.resources.StringComparePolicy; 41 import org.jemmy.swt.lookup.ByTextItem; 42 import org.jemmy.timing.State; 43 44 /** 45 * 46 * @author shura, erikgreijus 47 */ 48 public class SWTMenu implements MenuSelectable<MenuItem> { 49 50 public static final Timeout BETWEEN_KEYS_SLEEP = new Timeout( 51 SWTMenu.class.getName() + ".between.keys.timeout", 100); 52 public static final Timeout SUBMENU_WAIT_TIMEOUT = new Timeout( 53 SWTMenu.class.getName() + ".submenu.wait.timeout", 1000); 54 public static final String SELECTION_BUTTON_PROP 55 = SWTMenu.class.getName() + ".selection.button"; 56 public static final String ESCAPE_BUTTON_PROP 57 = SWTMenu.class.getName() + ".escape.button"; 58 public static final String MULTI_LEVEL_DISCARD_PROP 59 = SWTMenu.class.getName() + ".multi.level.discard"; 60 public static final String SKIPS_DISABLED_PROP 61 = SWTMenu.class.getName() + ".skips.disabled"; 62 Wrap<? extends Control> owner; 63 boolean isBar; 64 private final KeyboardButton selectionButton; 65 private final KeyboardButton escapeButton; 66 private final boolean skipsDisabled; 67 private final boolean multiLevelDiscard; 68 69 static { 70 Shells.SHELLS.getEnvironment().initTimeout(BETWEEN_KEYS_SLEEP); 71 Shells.SHELLS.getEnvironment().initTimeout(SUBMENU_WAIT_TIMEOUT); 72 } 73 74 public SWTMenu(Wrap<? extends Control> owner, boolean isBar) { 75 this.owner = owner; 76 this.isBar = isBar; 77 78 KeyboardButtons defaultSelectionButton = System.getProperty("os.name") 79 .contains("Linux") ? KeyboardButtons.SPACE : KeyboardButtons.ENTER; 80 KeyboardButtons defaultEscapeButton = KeyboardButtons.ESCAPE; 81 Boolean defaultSkipsDisabled = System.getProperty("os.name").contains("Linux"); 82 Boolean defaultMultiLevelDiscard = System.getProperty("os.name").contains("Windows"); 83 84 selectionButton = (KeyboardButton) owner.getEnvironment().getProperty(KeyboardButton.class, 85 SELECTION_BUTTON_PROP, defaultSelectionButton); 86 escapeButton = (KeyboardButton) owner.getEnvironment().getProperty(KeyboardButton.class, 87 ESCAPE_BUTTON_PROP, defaultEscapeButton); 88 skipsDisabled = owner.getEnvironment().getProperty(Boolean.class, 89 SKIPS_DISABLED_PROP, defaultSkipsDisabled); 90 multiLevelDiscard = owner.getEnvironment().getProperty(Boolean.class, 91 MULTI_LEVEL_DISCARD_PROP, defaultMultiLevelDiscard); 92 } 93 94 @Override 95 public void push(LookupCriteria<MenuItem>... criteria) { 96 select(criteria); 97 owner.keyboard().pushKey(selectionButton); 98 owner.getEnvironment().getTimeout(BETWEEN_KEYS_SLEEP).sleep(); 99 } 100 101 @Override 102 public void push(boolean desiredSelectionState, LookupCriteria<MenuItem>... criteria) { 103 if (desiredSelectionState != getSelection(select(criteria))) { 104 owner.keyboard().pushKey(selectionButton); 105 } else { 106 pushEscape((multiLevelDiscard) ? criteria.length : 1); 107 } 108 owner.getEnvironment().getTimeout(BETWEEN_KEYS_SLEEP).sleep(); 109 } 110 111 @Override 112 public boolean getState(LookupCriteria<MenuItem>... criteria) { 113 boolean result = getSelection(select(criteria)); 114 pushEscape((multiLevelDiscard) ? criteria.length : 1); 115 return result; 116 } 117 118 private void pushEscape(int times) { 119 for (int i = 0; i < times; i++) { 120 owner.keyboard().pushKey(escapeButton); 121 owner.getEnvironment().getTimeout(BETWEEN_KEYS_SLEEP).sleep(); 122 } 123 } 124 125 private boolean getSelection(Wrap<MenuItem> menuItem) { 126 final boolean[] result = new boolean[]{false}; 127 Display.getDefault().syncExec(() -> { 128 result[0] = menuItem.getControl().getSelection(); 129 }); 130 return result[0]; 131 } 132 133 @Override 134 public Wrap<MenuItem> select(LookupCriteria<MenuItem>... criteria) { 135 if (criteria.length == 0) { 136 throw new IllegalArgumentException("Menu path length should be greater than 0"); 137 } 138 final org.eclipse.swt.widgets.Menu[] start = new org.eclipse.swt.widgets.Menu[1]; 139 if (isBar) { 140 if (!(owner.getControl() instanceof Shell)) { 141 throw new JemmyException("Menu bars are in shells"); 142 } 143 Display.getDefault().syncExec(() -> { 144 start[0] = ((Shell) owner.getControl()).getMenuBar(); 145 }); 146 } else { 147 Display.getDefault().syncExec(() -> { 148 start[0] = owner.getControl().getMenu(); 149 }); 150 } 151 return select(start[0], Arrays.asList(criteria), true); 152 } 153 154 //selects hierarchically 155 //assumes first item in the menu is selected 156 private Wrap<MenuItem> select(final org.eclipse.swt.widgets.Menu menu, final List<LookupCriteria<MenuItem>> criteria, boolean entry) { 157 waitVisible(menu); 158 final int[] moveTimes = new int[]{0}; 159 final MenuItem[] current = new MenuItem[]{null}; 160 //find the one we're looking for 161 Display.getDefault().syncExec(() -> { 162 for (MenuItem item : menu.getItems()) { 163 if (criteria.get(0).check(item)) { 164 current[0] = item; 165 break; 166 } 167 if ((item.isEnabled() || !skipsDisabled) && !item.toString().contains("{|}")) { 168 moveTimes[0]++; 169 } 170 } 171 }); 172 173 if (current[0] == null) { 174 throw new JemmyException("Unable to find menu item conforming criteria " 175 + criteria.get(0).toString(), menu); 176 } 177 178 boolean horizontal = entry ? isBar : false; 179 move(moveTimes[0], horizontal); 180 181 if (criteria.size() > 1) { 182 final org.eclipse.swt.widgets.Menu[] nextMenu = new org.eclipse.swt.widgets.Menu[1]; 183 List<LookupCriteria<MenuItem>> nextCriteria = new ArrayList<>(); 184 nextCriteria.addAll(criteria); 185 nextCriteria.remove(0); 186 KeyboardButton key = horizontal ? KeyboardButtons.DOWN : KeyboardButtons.RIGHT; 187 owner.keyboard().pushKey(key); 188 owner.getEnvironment().getTimeout(BETWEEN_KEYS_SLEEP).sleep(); 189 Display.getDefault().syncExec(() -> { 190 nextMenu[0] = current[0].getMenu(); 191 }); 192 if (nextMenu[0] == null) { 193 throw new JemmyException("No submenu while criteria list length is still " + criteria.size(), 194 criteria.get(0)); 195 } 196 return select(nextMenu[0], nextCriteria, false); 197 } 198 return new ItemWrap<>(owner, current[0]); 199 } 200 201 void waitVisible(final org.eclipse.swt.widgets.Menu menu) { 202 owner.getEnvironment().getWaiter(SUBMENU_WAIT_TIMEOUT).waitValue(true, 203 new State<Boolean>() { 204 205 boolean state = false; 206 207 @Override 208 public Boolean reached() { 209 Display.getDefault().syncExec(() -> { 210 state = menu.isVisible(); 211 }); 212 return state; 213 } 214 }); 215 } 216 217 private void move(int moveTimes, boolean horizontal) { 218 KeyboardButton key = horizontal ? KeyboardButtons.RIGHT : KeyboardButtons.DOWN; 219 for (int i = 0; i < moveTimes; i++) { 220 owner.keyboard().pushKey(key); 221 owner.getEnvironment().getTimeout(BETWEEN_KEYS_SLEEP).sleep(); 222 } 223 } 224 225 static LookupCriteria<MenuItem> createCriteria(String text, StringComparePolicy policy) { 226 return new ByTextItem<>(text, policy); 227 } 228 229 }