1 /* |
|
2 * Copyright (c) 2014, 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 |
|
24 import java.awt.Component; |
|
25 import java.awt.Frame; |
|
26 import java.awt.Dialog; |
|
27 import java.awt.Window; |
|
28 import java.awt.Button; |
|
29 import java.awt.Point; |
|
30 import java.awt.Dimension; |
|
31 import java.awt.Rectangle; |
|
32 import java.awt.Robot; |
|
33 import java.awt.Toolkit; |
|
34 import java.awt.IllegalComponentStateException; |
|
35 import java.awt.AWTException; |
|
36 import java.awt.AWTEvent; |
|
37 |
|
38 import java.awt.event.InputEvent; |
|
39 import java.awt.event.WindowAdapter; |
|
40 import java.awt.event.WindowEvent; |
|
41 import java.awt.event.ActionEvent; |
|
42 import java.awt.event.FocusEvent; |
|
43 import java.awt.event.WindowListener; |
|
44 import java.awt.event.WindowFocusListener; |
|
45 import java.awt.event.FocusListener; |
|
46 import java.awt.event.ActionListener; |
|
47 |
|
48 import java.awt.peer.FramePeer; |
|
49 |
|
50 import java.lang.reflect.Constructor; |
|
51 import java.lang.reflect.Field; |
|
52 import java.lang.reflect.InvocationTargetException; |
|
53 import java.lang.reflect.Method; |
|
54 |
|
55 import java.security.PrivilegedAction; |
|
56 import java.security.AccessController; |
|
57 |
|
58 import java.util.concurrent.atomic.AtomicBoolean; |
|
59 |
|
60 /** |
|
61 * <p>This class contains utilities useful for regression testing. |
|
62 * <p>When using jtreg you would include this class into the build |
|
63 * list via something like: |
|
64 * <pre> |
|
65 &library ../../../../share/lib/AWT_Mixing/src/regtesthelpers/ |
|
66 &build Util |
|
67 &run main YourTest |
|
68 </pre> |
|
69 * Note that if you are about to create a test based on |
|
70 * Applet-template, then put those lines into html-file, not in java-file. |
|
71 * <p> And put an |
|
72 * import regtesthelpers.Util; |
|
73 * into the java source of test. |
|
74 */ |
|
75 public final class Util { |
|
76 private Util() {} // this is a helper class with static methods :) |
|
77 |
|
78 /* |
|
79 * @throws RuntimeException when creation failed |
|
80 */ |
|
81 public static Robot createRobot() { |
|
82 try { |
|
83 return new Robot(); |
|
84 } catch (AWTException e) { |
|
85 throw new RuntimeException("Error: unable to create robot", e); |
|
86 } |
|
87 } |
|
88 |
|
89 public static Frame createEmbeddedFrame(final Frame embedder) |
|
90 throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, |
|
91 InstantiationException, InvocationTargetException |
|
92 { |
|
93 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
94 FramePeer frame_peer = (FramePeer) embedder.getPeer(); |
|
95 System.out.println("frame's peer = " + frame_peer); |
|
96 if ("sun.awt.windows.WToolkit".equals(tk.getClass().getName())) { |
|
97 Class comp_peer_class = |
|
98 Class.forName("sun.awt.windows.WComponentPeer"); |
|
99 System.out.println("comp peer class = " + comp_peer_class); |
|
100 Field hwnd_field = comp_peer_class.getDeclaredField("hwnd"); |
|
101 hwnd_field.setAccessible(true); |
|
102 System.out.println("hwnd_field =" + hwnd_field); |
|
103 long hwnd = hwnd_field.getLong(frame_peer); |
|
104 System.out.println("hwnd = " + hwnd); |
|
105 |
|
106 Class clazz = Class.forName("sun.awt.windows.WEmbeddedFrame"); |
|
107 Constructor constructor = clazz.getConstructor (new Class [] {Long.TYPE}); |
|
108 return (Frame) constructor.newInstance (new Object[] {hwnd}); |
|
109 } else if ("sun.awt.X11.XToolkit".equals(tk.getClass().getName())) { |
|
110 Class x_base_window_class = Class.forName("sun.awt.X11.XBaseWindow"); |
|
111 System.out.println("x_base_window_class = " + x_base_window_class); |
|
112 Method get_window = x_base_window_class.getMethod("getWindow", new Class[0]); |
|
113 System.out.println("get_window = " + get_window); |
|
114 long window = (Long) get_window.invoke(frame_peer, new Object[0]); |
|
115 System.out.println("window = " + window); |
|
116 Class clazz = Class.forName("sun.awt.X11.XEmbeddedFrame"); |
|
117 Constructor constructor = clazz.getConstructor (new Class [] {Long.TYPE, Boolean.TYPE}); |
|
118 return (Frame) constructor.newInstance (new Object[] {window, true}); |
|
119 } |
|
120 |
|
121 throw new RuntimeException("Unexpected toolkit - " + tk); |
|
122 } |
|
123 |
|
124 /** |
|
125 * Makes the window visible and waits until it's shown. |
|
126 */ |
|
127 public static void showWindowWait(Window win) { |
|
128 win.setVisible(true); |
|
129 waitTillShown(win); |
|
130 } |
|
131 |
|
132 /** |
|
133 * Moves mouse pointer in the center of given {@code comp} component |
|
134 * using {@code robot} parameter. |
|
135 */ |
|
136 public static void pointOnComp(final Component comp, final Robot robot) { |
|
137 Rectangle bounds = new Rectangle(comp.getLocationOnScreen(), comp.getSize()); |
|
138 robot.mouseMove(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); |
|
139 } |
|
140 |
|
141 /** |
|
142 * Moves mouse pointer in the center of a given {@code comp} component |
|
143 * and performs a left mouse button click using the {@code robot} parameter |
|
144 * with the {@code delay} delay between press and release. |
|
145 */ |
|
146 public static void clickOnComp(final Component comp, final Robot robot, int delay) { |
|
147 pointOnComp(comp, robot); |
|
148 robot.delay(delay); |
|
149 robot.mousePress(InputEvent.BUTTON1_MASK); |
|
150 robot.delay(delay); |
|
151 robot.mouseRelease(InputEvent.BUTTON1_MASK); |
|
152 } |
|
153 |
|
154 /** |
|
155 * Moves mouse pointer in the center of a given {@code comp} component |
|
156 * and performs a left mouse button click using the {@code robot} parameter |
|
157 * with the default delay between press and release. |
|
158 */ |
|
159 public static void clickOnComp(final Component comp, final Robot robot) { |
|
160 clickOnComp(comp, robot, 50); |
|
161 } |
|
162 |
|
163 /* |
|
164 * Clicks on a title of Frame/Dialog. |
|
165 * WARNING: it may fail on some platforms when the window is not wide enough. |
|
166 */ |
|
167 public static void clickOnTitle(final Window decoratedWindow, final Robot robot) { |
|
168 Point p = decoratedWindow.getLocationOnScreen(); |
|
169 Dimension d = decoratedWindow.getSize(); |
|
170 |
|
171 if (decoratedWindow instanceof Frame || decoratedWindow instanceof Dialog) { |
|
172 robot.mouseMove(p.x + (int)(d.getWidth()/2), p.y + (int)decoratedWindow.getInsets().top/2); |
|
173 robot.delay(50); |
|
174 robot.mousePress(InputEvent.BUTTON1_MASK); |
|
175 robot.delay(50); |
|
176 robot.mouseRelease(InputEvent.BUTTON1_MASK); |
|
177 } |
|
178 } |
|
179 |
|
180 public static void waitForIdle(final Robot robot) { |
|
181 // we do not use robot for now, use SunToolkit.realSync() instead |
|
182 ((sun.awt.SunToolkit)Toolkit.getDefaultToolkit()).realSync(); |
|
183 } |
|
184 |
|
185 public static Field getField(final Class klass, final String fieldName) { |
|
186 return AccessController.doPrivileged(new PrivilegedAction<Field>() { |
|
187 public Field run() { |
|
188 try { |
|
189 Field field = klass.getDeclaredField(fieldName); |
|
190 assert (field != null); |
|
191 field.setAccessible(true); |
|
192 return field; |
|
193 } catch (SecurityException se) { |
|
194 throw new RuntimeException("Error: unexpected exception caught!", se); |
|
195 } catch (NoSuchFieldException nsfe) { |
|
196 throw new RuntimeException("Error: unexpected exception caught!", nsfe); |
|
197 } |
|
198 } |
|
199 }); |
|
200 } |
|
201 |
|
202 /* |
|
203 * Waits for a notification and for a boolean condition to become true. |
|
204 * The method returns when the above conditions are fullfilled or when the timeout |
|
205 * occurs. |
|
206 * |
|
207 * @param condition the object to be notified and the booelan condition to wait for |
|
208 * @param timeout the maximum time to wait in milliseconds |
|
209 * @param catchExceptions if {@code true} the method catches InterruptedException |
|
210 * @return the final boolean value of the {@code condition} |
|
211 * @throws InterruptedException if the awaiting proccess has been interrupted |
|
212 */ |
|
213 public static boolean waitForConditionEx(final AtomicBoolean condition, long timeout) |
|
214 throws InterruptedException |
|
215 { |
|
216 synchronized (condition) { |
|
217 long startTime = System.currentTimeMillis(); |
|
218 while (!condition.get()) { |
|
219 condition.wait(timeout); |
|
220 if (System.currentTimeMillis() - startTime >= timeout ) { |
|
221 break; |
|
222 } |
|
223 } |
|
224 } |
|
225 return condition.get(); |
|
226 } |
|
227 |
|
228 /* |
|
229 * The same as {@code waitForConditionEx(AtomicBoolean, long)} except that it |
|
230 * doesn't throw InterruptedException. |
|
231 */ |
|
232 public static boolean waitForCondition(final AtomicBoolean condition, long timeout) { |
|
233 try { |
|
234 return waitForConditionEx(condition, timeout); |
|
235 } catch (InterruptedException e) { |
|
236 throw new RuntimeException("Error: unexpected exception caught!", e); |
|
237 } |
|
238 } |
|
239 |
|
240 /* |
|
241 * The same as {@code waitForConditionEx(AtomicBoolean, long)} but without a timeout. |
|
242 */ |
|
243 public static void waitForConditionEx(final AtomicBoolean condition) |
|
244 throws InterruptedException |
|
245 { |
|
246 synchronized (condition) { |
|
247 while (!condition.get()) { |
|
248 condition.wait(); |
|
249 } |
|
250 } |
|
251 } |
|
252 |
|
253 /* |
|
254 * The same as {@code waitForConditionEx(AtomicBoolean)} except that it |
|
255 * doesn't throw InterruptedException. |
|
256 */ |
|
257 public static void waitForCondition(final AtomicBoolean condition) { |
|
258 try { |
|
259 waitForConditionEx(condition); |
|
260 } catch (InterruptedException e) { |
|
261 throw new RuntimeException("Error: unexpected exception caught!", e); |
|
262 } |
|
263 } |
|
264 |
|
265 public static void waitTillShownEx(final Component comp) throws InterruptedException { |
|
266 while (true) { |
|
267 try { |
|
268 Thread.sleep(100); |
|
269 comp.getLocationOnScreen(); |
|
270 break; |
|
271 } catch (IllegalComponentStateException e) {} |
|
272 } |
|
273 } |
|
274 public static void waitTillShown(final Component comp) { |
|
275 try { |
|
276 waitTillShownEx(comp); |
|
277 } catch (InterruptedException e) { |
|
278 throw new RuntimeException("Error: unexpected exception caught!", e); |
|
279 } |
|
280 } |
|
281 |
|
282 /** |
|
283 * Drags from one point to another with the specified mouse button pressed. |
|
284 * |
|
285 * @param robot a robot to use for moving the mouse, etc. |
|
286 * @param startPoint a start point of the drag |
|
287 * @param endPoint an end point of the drag |
|
288 * @param button one of {@code InputEvent.BUTTON1_MASK}, |
|
289 * {@code InputEvent.BUTTON2_MASK}, {@code InputEvent.BUTTON3_MASK} |
|
290 * |
|
291 * @throws IllegalArgumentException if {@code button} is not one of |
|
292 * {@code InputEvent.BUTTON1_MASK}, {@code InputEvent.BUTTON2_MASK}, |
|
293 * {@code InputEvent.BUTTON3_MASK} |
|
294 */ |
|
295 public static void drag(Robot robot, Point startPoint, Point endPoint, int button) { |
|
296 if (!(button == InputEvent.BUTTON1_MASK || button == InputEvent.BUTTON2_MASK |
|
297 || button == InputEvent.BUTTON3_MASK)) |
|
298 { |
|
299 throw new IllegalArgumentException("invalid mouse button"); |
|
300 } |
|
301 |
|
302 robot.mouseMove(startPoint.x, startPoint.y); |
|
303 robot.mousePress(button); |
|
304 try { |
|
305 mouseMove(robot, startPoint, endPoint); |
|
306 } finally { |
|
307 robot.mouseRelease(button); |
|
308 } |
|
309 } |
|
310 |
|
311 /** |
|
312 * Moves the mouse pointer from one point to another. |
|
313 * Uses Bresenham's algorithm. |
|
314 * |
|
315 * @param robot a robot to use for moving the mouse |
|
316 * @param startPoint a start point of the drag |
|
317 * @param endPoint an end point of the drag |
|
318 */ |
|
319 public static void mouseMove(Robot robot, Point startPoint, Point endPoint) { |
|
320 int dx = endPoint.x - startPoint.x; |
|
321 int dy = endPoint.y - startPoint.y; |
|
322 |
|
323 int ax = Math.abs(dx) * 2; |
|
324 int ay = Math.abs(dy) * 2; |
|
325 |
|
326 int sx = signWOZero(dx); |
|
327 int sy = signWOZero(dy); |
|
328 |
|
329 int x = startPoint.x; |
|
330 int y = startPoint.y; |
|
331 |
|
332 int d = 0; |
|
333 |
|
334 if (ax > ay) { |
|
335 d = ay - ax/2; |
|
336 while (true){ |
|
337 robot.mouseMove(x, y); |
|
338 robot.delay(50); |
|
339 |
|
340 if (x == endPoint.x){ |
|
341 return; |
|
342 } |
|
343 if (d >= 0){ |
|
344 y = y + sy; |
|
345 d = d - ax; |
|
346 } |
|
347 x = x + sx; |
|
348 d = d + ay; |
|
349 } |
|
350 } else { |
|
351 d = ax - ay/2; |
|
352 while (true){ |
|
353 robot.mouseMove(x, y); |
|
354 robot.delay(50); |
|
355 |
|
356 if (y == endPoint.y){ |
|
357 return; |
|
358 } |
|
359 if (d >= 0){ |
|
360 x = x + sx; |
|
361 d = d - ay; |
|
362 } |
|
363 y = y + sy; |
|
364 d = d + ax; |
|
365 } |
|
366 } |
|
367 } |
|
368 |
|
369 private static int signWOZero(int i){ |
|
370 return (i > 0)? 1: -1; |
|
371 } |
|
372 |
|
373 private static int sign(int n) { |
|
374 return n < 0 ? -1 : n == 0 ? 0 : 1; |
|
375 } |
|
376 |
|
377 /** Returns {@code WindowListener} instance that diposes {@code Window} on |
|
378 * "window closing" event. |
|
379 * |
|
380 * @return the {@code WindowListener} instance that could be set |
|
381 * on a {@code Window}. After that |
|
382 * the {@code Window} is disposed when "window closed" |
|
383 * event is sent to the {@code Window} |
|
384 */ |
|
385 public static WindowListener getClosingWindowAdapter() { |
|
386 return new WindowAdapter () { |
|
387 public void windowClosing(WindowEvent e) { |
|
388 e.getWindow().dispose(); |
|
389 } |
|
390 }; |
|
391 } |
|
392 |
|
393 /* |
|
394 * The values directly map to the ones of |
|
395 * sun.awt.X11.XWM & sun.awt.motif.MToolkit classes. |
|
396 */ |
|
397 public final static int |
|
398 UNDETERMINED_WM = 1, |
|
399 NO_WM = 2, |
|
400 OTHER_WM = 3, |
|
401 OPENLOOK_WM = 4, |
|
402 MOTIF_WM = 5, |
|
403 CDE_WM = 6, |
|
404 ENLIGHTEN_WM = 7, |
|
405 KDE2_WM = 8, |
|
406 SAWFISH_WM = 9, |
|
407 ICE_WM = 10, |
|
408 METACITY_WM = 11, |
|
409 COMPIZ_WM = 12, |
|
410 LG3D_WM = 13; |
|
411 |
|
412 /* |
|
413 * Returns -1 in case of not X Window or any problems. |
|
414 */ |
|
415 public static int getWMID() { |
|
416 Class clazz = null; |
|
417 try { |
|
418 if ("sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { |
|
419 clazz = Class.forName("sun.awt.X11.XWM"); |
|
420 } else if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { |
|
421 clazz = Class.forName("sun.awt.motif.MToolkit"); |
|
422 } |
|
423 } catch (ClassNotFoundException cnfe) { |
|
424 cnfe.printStackTrace(); |
|
425 } |
|
426 if (clazz == null) { |
|
427 return -1; |
|
428 } |
|
429 |
|
430 try { |
|
431 final Class _clazz = clazz; |
|
432 Method m_getWMID = (Method)AccessController.doPrivileged(new PrivilegedAction() { |
|
433 public Object run() { |
|
434 try { |
|
435 Method method = _clazz.getDeclaredMethod("getWMID", new Class[] {}); |
|
436 if (method != null) { |
|
437 method.setAccessible(true); |
|
438 } |
|
439 return method; |
|
440 } catch (NoSuchMethodException e) { |
|
441 assert false; |
|
442 } catch (SecurityException e) { |
|
443 assert false; |
|
444 } |
|
445 return null; |
|
446 } |
|
447 }); |
|
448 return ((Integer)m_getWMID.invoke(null, new Object[] {})).intValue(); |
|
449 } catch (IllegalAccessException iae) { |
|
450 iae.printStackTrace(); |
|
451 } catch (InvocationTargetException ite) { |
|
452 ite.printStackTrace(); |
|
453 } |
|
454 return -1; |
|
455 } |
|
456 |
|
457 |
|
458 //////////////////////////// |
|
459 // Some stuff to test focus. |
|
460 //////////////////////////// |
|
461 |
|
462 private static WindowGainedFocusListener wgfListener = new WindowGainedFocusListener(); |
|
463 private static FocusGainedListener fgListener = new FocusGainedListener(); |
|
464 private static ActionPerformedListener apListener = new ActionPerformedListener(); |
|
465 |
|
466 private abstract static class EventListener { |
|
467 AtomicBoolean notifier = new AtomicBoolean(false); |
|
468 Component comp; |
|
469 boolean printEvent; |
|
470 |
|
471 public void listen(Component comp, boolean printEvent) { |
|
472 this.comp = comp; |
|
473 this.printEvent = printEvent; |
|
474 notifier.set(false); |
|
475 setListener(comp); |
|
476 } |
|
477 |
|
478 public AtomicBoolean getNotifier() { |
|
479 return notifier; |
|
480 } |
|
481 |
|
482 abstract void setListener(Component comp); |
|
483 |
|
484 void printAndNotify(AWTEvent e) { |
|
485 if (printEvent) { |
|
486 System.err.println(e); |
|
487 } |
|
488 synchronized (notifier) { |
|
489 notifier.set(true); |
|
490 notifier.notifyAll(); |
|
491 } |
|
492 } |
|
493 } |
|
494 |
|
495 private static class WindowGainedFocusListener extends EventListener implements WindowFocusListener { |
|
496 |
|
497 void setListener(Component comp) { |
|
498 ((Window)comp).addWindowFocusListener(this); |
|
499 } |
|
500 |
|
501 public void windowGainedFocus(WindowEvent e) { |
|
502 |
|
503 ((Window)comp).removeWindowFocusListener(this); |
|
504 printAndNotify(e); |
|
505 } |
|
506 |
|
507 public void windowLostFocus(WindowEvent e) {} |
|
508 } |
|
509 |
|
510 private static class FocusGainedListener extends EventListener implements FocusListener { |
|
511 |
|
512 void setListener(Component comp) { |
|
513 comp.addFocusListener(this); |
|
514 } |
|
515 |
|
516 public void focusGained(FocusEvent e) { |
|
517 comp.removeFocusListener(this); |
|
518 printAndNotify(e); |
|
519 } |
|
520 |
|
521 public void focusLost(FocusEvent e) {} |
|
522 } |
|
523 |
|
524 private static class ActionPerformedListener extends EventListener implements ActionListener { |
|
525 |
|
526 void setListener(Component comp) { |
|
527 ((Button)comp).addActionListener(this); |
|
528 } |
|
529 |
|
530 public void actionPerformed(ActionEvent e) { |
|
531 ((Button)comp).removeActionListener(this); |
|
532 printAndNotify(e); |
|
533 } |
|
534 } |
|
535 |
|
536 private static boolean trackEvent(int eventID, Component comp, Runnable action, int time, boolean printEvent) { |
|
537 EventListener listener = null; |
|
538 |
|
539 switch (eventID) { |
|
540 case WindowEvent.WINDOW_GAINED_FOCUS: |
|
541 listener = wgfListener; |
|
542 break; |
|
543 case FocusEvent.FOCUS_GAINED: |
|
544 listener = fgListener; |
|
545 break; |
|
546 case ActionEvent.ACTION_PERFORMED: |
|
547 listener = apListener; |
|
548 break; |
|
549 } |
|
550 |
|
551 listener.listen(comp, printEvent); |
|
552 action.run(); |
|
553 return Util.waitForCondition(listener.getNotifier(), time); |
|
554 } |
|
555 |
|
556 /* |
|
557 * Tracks WINDOW_GAINED_FOCUS event for a window caused by an action. |
|
558 * @param window the window to track the event for |
|
559 * @param action the action to perform |
|
560 * @param time the max time to wait for the event |
|
561 * @param printEvent should the event received be printed or doesn't |
|
562 * @return true if the event has been received, otherwise false |
|
563 */ |
|
564 public static boolean trackWindowGainedFocus(Window window, Runnable action, int time, boolean printEvent) { |
|
565 return trackEvent(WindowEvent.WINDOW_GAINED_FOCUS, window, action, time, printEvent); |
|
566 } |
|
567 |
|
568 /* |
|
569 * Tracks FOCUS_GAINED event for a component caused by an action. |
|
570 * @see #trackWindowGainedFocus |
|
571 */ |
|
572 public static boolean trackFocusGained(Component comp, Runnable action, int time, boolean printEvent) { |
|
573 return trackEvent(FocusEvent.FOCUS_GAINED, comp, action, time, printEvent); |
|
574 } |
|
575 |
|
576 /* |
|
577 * Tracks ACTION_PERFORMED event for a button caused by an action. |
|
578 * @see #trackWindowGainedFocus |
|
579 */ |
|
580 public static boolean trackActionPerformed(Button button, Runnable action, int time, boolean printEvent) { |
|
581 return trackEvent(ActionEvent.ACTION_PERFORMED, button, action, time, printEvent); |
|
582 } |
|
583 |
|
584 /* |
|
585 * Requests focus on the component provided and waits for the result. |
|
586 * @return true if the component has been focused, false otherwise. |
|
587 */ |
|
588 public static boolean focusComponent(Component comp, int time) { |
|
589 return focusComponent(comp, time, false); |
|
590 } |
|
591 public static boolean focusComponent(final Component comp, int time, boolean printEvent) { |
|
592 return trackFocusGained(comp, |
|
593 new Runnable() { |
|
594 public void run() { |
|
595 comp.requestFocus(); |
|
596 } |
|
597 }, |
|
598 time, printEvent); |
|
599 |
|
600 } |
|
601 } |
|