1/* 2 * Copyright (c) 1997, 2016, 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 */ 23package org.netbeans.jemmy.drivers.menus; 24 25import java.awt.Component; 26 27import javax.swing.JMenu; 28import javax.swing.JMenuBar; 29import javax.swing.JMenuItem; 30import javax.swing.JPopupMenu; 31import javax.swing.MenuElement; 32 33import org.netbeans.jemmy.ComponentChooser; 34import org.netbeans.jemmy.JemmyException; 35import org.netbeans.jemmy.Waitable; 36import org.netbeans.jemmy.Waiter; 37import org.netbeans.jemmy.drivers.DriverManager; 38import org.netbeans.jemmy.drivers.LightSupportiveDriver; 39import org.netbeans.jemmy.drivers.MenuDriver; 40import org.netbeans.jemmy.drivers.MouseDriver; 41import org.netbeans.jemmy.drivers.PathChooser; 42import org.netbeans.jemmy.operators.ComponentOperator; 43import org.netbeans.jemmy.operators.JMenuBarOperator; 44import org.netbeans.jemmy.operators.JMenuItemOperator; 45import org.netbeans.jemmy.operators.JMenuOperator; 46import org.netbeans.jemmy.operators.JPopupMenuOperator; 47 48public class DefaultJMenuDriver extends LightSupportiveDriver implements MenuDriver { 49 50 public DefaultJMenuDriver() { 51 super(new String[]{"org.netbeans.jemmy.operators.JMenuOperator", 52 "org.netbeans.jemmy.operators.JMenuBarOperator", 53 "org.netbeans.jemmy.operators.JPopupMenuOperator"}); 54 } 55 56 @Override 57 public Object pushMenu(ComponentOperator oper, PathChooser chooser) { 58 checkSupported(oper); 59 if (oper instanceof JMenuBarOperator 60 || oper instanceof JPopupMenuOperator) { 61 JMenuItem item; 62 if (oper instanceof JMenuBarOperator) { 63 item = waitItem(oper, 64 (JMenuBar) oper.getSource(), 65 chooser, 0); 66 } else { 67 item = waitItem(oper, 68 (JPopupMenu) oper.getSource(), 69 chooser, 0); 70 } 71 JMenuItemOperator itemOper; 72 if (item instanceof JMenu) { 73 itemOper = new JMenuOperator((JMenu) item); 74 } else { 75 itemOper = new JMenuItemOperator(item); 76 } 77 itemOper.copyEnvironment(oper); 78 return (push(itemOper, null, (oper instanceof JMenuBarOperator) ? ((JMenuBar) oper.getSource()) : null, 79 chooser, 1, true)); 80 } else { 81 return push(oper, null, null, chooser, 0, true); 82 } 83 } 84 85 protected Object push(ComponentOperator oper, ComponentOperator lastItem, 86 JMenuBar menuBar, 87 PathChooser chooser, int depth, boolean pressMouse) { 88 try { 89 oper.waitComponentVisible(true); 90 oper.waitComponentEnabled(); 91 } catch (InterruptedException e) { 92 throw (new JemmyException("Interrupted!", e)); 93 } 94 MouseDriver mDriver = DriverManager.getMouseDriver(oper); 95 //mDriver.enterMouse(oper); 96 //use enhanced algorithm instead 97 smartMove(lastItem, oper); 98 if (depth > chooser.getDepth() - 1) { 99 if (oper instanceof JMenuOperator 100 && menuBar != null && getSelectedElement(menuBar) != null) { 101 //mDriver.enterMouse(oper); 102 } else { 103 DriverManager.getButtonDriver(oper).push(oper); 104 } 105 return oper.getSource(); 106 } 107 if (pressMouse && !((JMenuOperator) oper).isPopupMenuVisible() 108 && !(menuBar != null && getSelectedElement(menuBar) != null)) { 109 DriverManager.getButtonDriver(oper).push(oper); 110 } 111 oper.getTimeouts().sleep("JMenuOperator.WaitBeforePopupTimeout"); 112 JMenuItem item = waitItem(oper, waitPopupMenu(oper), chooser, depth); 113 mDriver.exitMouse(oper); 114 if (item instanceof JMenu) { 115 JMenuOperator mo = new JMenuOperator((JMenu) item); 116 mo.copyEnvironment(oper); 117 return push(mo, oper, null, chooser, depth + 1, false); 118 } else { 119 JMenuItemOperator mio = new JMenuItemOperator(item); 120 mio.copyEnvironment(oper); 121 try { 122 mio.waitComponentEnabled(); 123 } catch (InterruptedException e) { 124 throw (new JemmyException("Interrupted!", e)); 125 } 126 //move here first 127 smartMove(oper, mio); 128 DriverManager.getButtonDriver(oper).push(mio); 129 return item; 130 } 131 } 132 133 private void smartMove(ComponentOperator last, ComponentOperator oper) { 134 if (last == null) { 135 oper.enterMouse(); 136 return; 137 } 138 //get all the coordinates first 139 //previous item 140 long lastXl, lastXr, lastYl, lastYr; 141 lastXl = (long) last.getSource().getLocationOnScreen().getX(); 142 lastXr = lastXl + last.getSource().getWidth(); 143 lastYl = (long) last.getSource().getLocationOnScreen().getY(); 144 lastYr = lastYl + last.getSource().getHeight(); 145 //this item 146 long operXl, operXr, operYl, operYr; 147 operXl = (long) oper.getSource().getLocationOnScreen().getX(); 148 operXr = operXl + oper.getSource().getWidth(); 149 operYl = (long) oper.getSource().getLocationOnScreen().getY(); 150 operYr = operYl + oper.getSource().getHeight(); 151 //get the overlap borders 152 long overXl, overXr, overYl, overYr; 153 overXl = (lastXl > operXl) ? lastXl : operXl; 154 overXr = (lastXr < operXr) ? lastXr : operXr; 155 overYl = (lastYl > operYl) ? lastYl : operYl; 156 overYr = (lastYr < operYr) ? lastYr : operYr; 157 //now, let's see ... 158 //what if it overlaps by x? 159 if (overXl < overXr) { 160 //good - move mose to the center of the overlap 161 last.moveMouse((int) ((overXr - overXl) / 2 - lastXl), 162 last.getCenterY()); 163 //move mouse inside 164 oper.moveMouse((int) ((overXr - overXl) / 2 - operXl), 165 oper.getCenterY()); 166 //done - now move to the center 167 oper.enterMouse(); 168 return; 169 } 170 //ok, what if it overlaps by y? 171 if (overYl < overYr) { 172 //good - move mose to the center of the overlap 173 last.moveMouse(last.getCenterX(), 174 (int) ((overYr - overYl) / 2 - lastYl)); 175 //move mouse inside 176 oper.moveMouse(last.getCenterX(), 177 (int) ((overYr - overYl) / 2 - operYl)); 178 //done - now move to the center 179 oper.enterMouse(); 180 return; 181 } 182 //well - can't help it 183 oper.enterMouse(); 184 } 185 186 protected JPopupMenu waitPopupMenu(final ComponentOperator oper) { 187 return ((JPopupMenu) JPopupMenuOperator.waitJPopupMenu(new ComponentChooser() { 188 @Override 189 public boolean checkComponent(Component comp) { 190 return (comp == ((JMenuOperator) oper).getPopupMenu() 191 && comp.isShowing()); 192 } 193 194 @Override 195 public String getDescription() { 196 return ((JMenuOperator) oper).getText() + "'s popup"; 197 } 198 199 @Override 200 public String toString() { 201 return "waitPopupMenu.ComponentChooser{description = " + getDescription() + '}'; 202 } 203 }).getSource()); 204 } 205 206 protected JMenuItem waitItem(ComponentOperator oper, MenuElement element, PathChooser chooser, int depth) { 207 Waiter<MenuElement, Void> waiter = new Waiter<>(new JMenuItemWaiter(element, chooser, depth)); 208 waiter.setOutput(oper.getOutput().createErrorOutput()); 209 waiter.setTimeouts(oper.getTimeouts()); 210 try { 211 return (JMenuItem) waiter.waitAction(null); 212 } catch (InterruptedException e) { 213 throw (new JemmyException("Waiting has been interrupted", e)); 214 } 215 } 216 217 public static Object getSelectedElement(JMenuBar bar) { 218 MenuElement[] subElements = bar.getSubElements(); 219 for (MenuElement subElement : subElements) { 220 if (subElement instanceof JMenu 221 && ((JMenu) subElement).isPopupMenuVisible()) { 222 return subElement; 223 } 224 } 225 return null; 226 } 227 228 private static class JMenuItemWaiter implements Waitable<MenuElement, Void> { 229 230 MenuElement cont; 231 PathChooser chooser; 232 int depth; 233 234 public JMenuItemWaiter(MenuElement cont, PathChooser chooser, int depth) { 235 this.cont = cont; 236 this.chooser = chooser; 237 this.depth = depth; 238 } 239 240 @Override 241 public MenuElement actionProduced(Void obj) { 242 if (!((Component) cont).isShowing()) { 243 return null; 244 } 245 MenuElement[] subElements = cont.getSubElements(); 246 for (MenuElement subElement : subElements) { 247 if (((Component) subElement).isShowing() && ((Component) subElement).isEnabled() 248 && chooser.checkPathComponent(depth, subElement)) { 249 return subElement; 250 } 251 } 252 return null; 253 } 254 255 @Override 256 public String getDescription() { 257 return ""; 258 } 259 260 @Override 261 public String toString() { 262 return "JMenuItemWaiter{" + "cont=" + cont + ", chooser=" + chooser + ", depth=" + depth + '}'; 263 } 264 } 265} 266