Coverage Report - org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractSWTBot
75%
135/180
60%
12/20
1.493
AbstractSWTBot$1
100%
3/3
N/A
1.493
AbstractSWTBot$10
0%
0/5
0%
0/2
1.493
AbstractSWTBot$11
100%
11/11
75%
3/4
1.493
AbstractSWTBot$12
100%
3/3
N/A
1.493
AbstractSWTBot$13
100%
3/3
100%
2/2
1.493
AbstractSWTBot$14
100%
6/6
N/A
1.493
AbstractSWTBot$15
0%
0/6
N/A
1.493
AbstractSWTBot$16
100%
6/6
N/A
1.493
AbstractSWTBot$17
100%
6/6
N/A
1.493
AbstractSWTBot$18
100%
6/6
N/A
1.493
AbstractSWTBot$2
83%
10/12
66%
4/6
1.493
AbstractSWTBot$3
100%
3/3
N/A
1.493
AbstractSWTBot$4
100%
3/3
N/A
1.493
AbstractSWTBot$5
100%
3/3
N/A
1.493
AbstractSWTBot$6
75%
3/4
50%
1/2
1.493
AbstractSWTBot$7
100%
3/3
N/A
1.493
AbstractSWTBot$8
80%
4/5
50%
1/2
1.493
AbstractSWTBot$9
80%
4/5
50%
1/2
1.493
 
 1  1
 /*******************************************************************************
 2  
  * Copyright (c) 2008 Ketan Padegaonkar and others.
 3  
  * All rights reserved. This program and the accompanying materials
 4  
  * are made available under the terms of the Eclipse Public License v1.0
 5  
  * which accompanies this distribution, and is available at
 6  
  * http://www.eclipse.org/legal/epl-v10.html
 7  
  * 
 8  
  * Contributors:
 9  
  *     Ketan Padegaonkar - initial API and implementation
 10  
  *******************************************************************************/
 11  
 package org.eclipse.swtbot.swt.finder.widgets;
 12  
 
 13  
 import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType;
 14  
 import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withMnemonic;
 15  
 import static org.eclipse.swtbot.swt.finder.waits.Conditions.widgetIsEnabled;
 16  
 import static org.hamcrest.Matchers.allOf;
 17  
 
 18  
 import java.util.List;
 19  
 
 20  
 import org.apache.log4j.Logger;
 21  
 import org.eclipse.jface.bindings.keys.KeyStroke;
 22  
 import org.eclipse.swt.SWT;
 23  
 import org.eclipse.swt.graphics.Color;
 24  
 import org.eclipse.swt.graphics.Rectangle;
 25  
 import org.eclipse.swt.widgets.Control;
 26  
 import org.eclipse.swt.widgets.Display;
 27  
 import org.eclipse.swt.widgets.Event;
 28  
 import org.eclipse.swt.widgets.MenuItem;
 29  
 import org.eclipse.swt.widgets.Widget;
 30  
 import org.eclipse.swtbot.swt.finder.SWTBot;
 31  
 import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
 32  
 import org.eclipse.swtbot.swt.finder.finders.ContextMenuFinder;
 33  
 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
 34  
 import org.eclipse.swtbot.swt.finder.keyboard.Keyboard;
 35  
 import org.eclipse.swtbot.swt.finder.keyboard.KeyboardFactory;
 36  
 import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes;
 37  
 import org.eclipse.swtbot.swt.finder.matchers.WithId;
 38  
 import org.eclipse.swtbot.swt.finder.results.ArrayResult;
 39  
 import org.eclipse.swtbot.swt.finder.results.BoolResult;
 40  
 import org.eclipse.swtbot.swt.finder.results.IntResult;
 41  
 import org.eclipse.swtbot.swt.finder.results.ListResult;
 42  
 import org.eclipse.swtbot.swt.finder.results.Result;
 43  
 import org.eclipse.swtbot.swt.finder.results.StringResult;
 44  
 import org.eclipse.swtbot.swt.finder.results.VoidResult;
 45  
 import org.eclipse.swtbot.swt.finder.results.WidgetResult;
 46  
 import org.eclipse.swtbot.swt.finder.utils.MessageFormat;
 47  
 import org.eclipse.swtbot.swt.finder.utils.SWTBotEvents;
 48  
 import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
 49  
 import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
 50  
 import org.eclipse.swtbot.swt.finder.utils.Traverse;
 51  
 import org.eclipse.swtbot.swt.finder.utils.WidgetTextDescription;
 52  
 import org.eclipse.swtbot.swt.finder.utils.internal.Assert;
 53  
 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
 54  
 import org.hamcrest.Matcher;
 55  
 import org.hamcrest.SelfDescribing;
 56  
 import org.hamcrest.StringDescription;
 57  
 
 58  
 /**
 59  
  * Helper to find SWT {@link Widget}s and perform operations on them.
 60  
  * 
 61  
  * @author Ketan Padegaonkar <KetanPadegaonkar [at] gmail [dot] com>
 62  
  * @author Joshua Gosse <jlgosse [at] ca [dot] ibm [dot] com>
 63  
  * @version $Id$
 64  
  */
 65  
 public abstract class AbstractSWTBot<T extends Widget> {
 66  
 
 67  
         /** The logger. */
 68  
         protected final Logger                        log;
 69  
         /** With great power comes great responsibility, use carefully. */
 70  
         public final T                                        widget;
 71  
         /** With great power comes great responsibility, use carefully. */
 72  
         public final Display                        display;
 73  
         /** The description of the widget. */
 74  
         protected final SelfDescribing        description;
 75  
         /** The keyboard to use to type on the widget. */
 76  
         private Keyboard                                keyboard;
 77  
 
 78  
         /**
 79  
          * Constructs a new instance with the given widget.
 80  
          * 
 81  
          * @param w the widget.
 82  
          * @throws WidgetNotFoundException if the widget is <code>null</code> or widget has been disposed.
 83  
          */
 84  
         public AbstractSWTBot(T w) throws WidgetNotFoundException {
 85  17
                 this(w, new WidgetTextDescription(w));
 86  15
         }
 87  
 
 88  
         /**
 89  
          * Constructs a new instance with the given widget.
 90  
          * 
 91  
          * @param w the widget.
 92  
          * @param description the description of the widget, this will be reported by {@link #toString()}
 93  
          * @throws WidgetNotFoundException if the widget is <code>null</code> or widget has been disposed.
 94  
          */
 95  1541
         public AbstractSWTBot(T w, SelfDescribing description) throws WidgetNotFoundException {
 96  1541
                 if (w == null)
 97  1
                         throw new WidgetNotFoundException("The widget was null."); //$NON-NLS-1$
 98  
 
 99  1540
                 this.widget = w;
 100  1540
                 if (description == null)
 101  244
                         this.description = new WidgetTextDescription(w);
 102  
                 else
 103  1296
                         this.description = description;
 104  
 
 105  1540
                 if (w.isDisposed())
 106  1
                         throw new WidgetNotFoundException("The widget {" + description + "} was disposed." + SWTUtils.toString(w)); //$NON-NLS-1$ //$NON-NLS-2$
 107  
 
 108  1539
                 display = w.getDisplay();
 109  1539
                 log = Logger.getLogger(getClass());
 110  1539
         }
 111  
 
 112  
         /**
 113  
          * Sends a non-blocking notification of the specified type to the widget.
 114  
          * 
 115  
          * @param eventType the event type.
 116  
          * @see Widget#notifyListeners(int, Event)
 117  
          */
 118  
         protected void notify(final int eventType) {
 119  5904
                 notify(eventType, createEvent());
 120  5904
         }
 121  
 
 122  
         /**
 123  
          * Sends a non-blocking notification of the specified type to the {@link #widget}.
 124  
          * 
 125  
          * @param eventType the type of event.
 126  
          * @param createEvent the event to be sent to the {@link #widget}.
 127  
          */
 128  
         protected void notify(final int eventType, final Event createEvent) {
 129  6013
                 notify(eventType, createEvent, widget);
 130  6013
         }
 131  
 
 132  
         /**
 133  
          * Sends a non-blocking notification of the specified type to the widget.
 134  
          * 
 135  
          * @param eventType the type of event.
 136  
          * @param createEvent the event to be sent to the {@link #widget}.
 137  
          * @param widget the widget to send the event to.
 138  
          */
 139  
         protected void notify(final int eventType, final Event createEvent, final Widget widget) {
 140  6571
                 createEvent.type = eventType;
 141  6571
                 final Object[] result = syncExec(new ArrayResult<Object>() {
 142  
                         public Object[] run() {
 143  6571
                                 return new Object[] { SWTBotEvents.toString(createEvent), AbstractSWTBot.this.toString() };
 144  
                         }
 145  
                 });
 146  
 
 147  6571
                 log.trace(MessageFormat.format("Enquing event {0} on {1}", result)); //$NON-NLS-1$
 148  6571
                 asyncExec(new VoidResult() {
 149  
                         public void run() {
 150  6571
                                 if ((widget == null) || widget.isDisposed()) {
 151  6
                                         log.trace(MessageFormat.format("Not notifying {0} is null or has been disposed", AbstractSWTBot.this)); //$NON-NLS-1$
 152  6
                                         return;
 153  
                                 }
 154  6565
                                 if (!isEnabledInternal()) {
 155  0
                                         log.warn(MessageFormat.format("Widget is not enabled: {0}", AbstractSWTBot.this)); //$NON-NLS-1$
 156  0
                                         return;
 157  
                                 }
 158  6565
                                 log.trace(MessageFormat.format("Sending event {0} to {1}", result)); //$NON-NLS-1$
 159  6565
                                 widget.notifyListeners(eventType, createEvent);
 160  6565
                                 log.debug(MessageFormat.format("Sent event {0} to {1}", result)); //$NON-NLS-1$
 161  6565
                         }
 162  
                 });
 163  
 
 164  6571
                 UIThreadRunnable.syncExec(new VoidResult() {
 165  
                         public void run() {
 166  
                                 // do nothing, just wait for sync.
 167  6571
                         }
 168  
                 });
 169  
 
 170  6571
                 long playbackDelay = SWTBotPreferences.PLAYBACK_DELAY;
 171  6571
                 if (playbackDelay > 0)
 172  0
                         sleep(playbackDelay);
 173  6571
         }
 174  
 
 175  
         /**
 176  
          * Sleeps for millis milliseconds. Delegate to {@link SWTUtils#sleep(long)}
 177  
          * 
 178  
          * @param millis the time in milli seconds
 179  
          */
 180  
         protected static void sleep(long millis) {
 181  1
                 SWTUtils.sleep(millis);
 182  1
         }
 183  
 
 184  
         /**
 185  
          * Creates an event.
 186  
          * 
 187  
          * @return an event that encapsulates {@link #widget} and {@link #display}. Subclasses may override to set other
 188  
          *         event properties.
 189  
          */
 190  
         protected Event createEvent() {
 191  6485
                 Event event = new Event();
 192  6485
                 event.time = (int) System.currentTimeMillis();
 193  6485
                 event.widget = widget;
 194  6485
                 event.display = display;
 195  6485
                 return event;
 196  
         }
 197  
 
 198  
         /**
 199  
          * Create a mouse event
 200  
          * 
 201  
          * @param x the x co-ordinate of the mouse event.
 202  
          * @param y the y co-ordinate of the mouse event.
 203  
          * @param button the mouse button that was clicked.
 204  
          * @param stateMask the state of the keyboard modifier keys.
 205  
          * @param count the number of times the mouse was clicked.
 206  
          * @return an event that encapsulates {@link #widget} and {@link #display}
 207  
          * @since 1.2
 208  
          */
 209  
         protected Event createMouseEvent(int x, int y, int button, int stateMask, int count) {
 210  99
                 Event event = new Event();
 211  99
                 event.time = (int) System.currentTimeMillis();
 212  99
                 event.widget = widget;
 213  99
                 event.display = display;
 214  99
                 event.x = x;
 215  99
                 event.y = y;
 216  99
                 event.button = button;
 217  99
                 event.stateMask = stateMask;
 218  99
                 event.count = count;
 219  99
                 return event;
 220  
         }
 221  
 
 222  
         /**
 223  
          * Create a selection event with a particular state mask
 224  
          * 
 225  
          * @param stateMask the state of the keyboard modifier keys.
 226  
          */
 227  
         protected Event createSelectionEvent(int stateMask) {
 228  7
                 Event event = createEvent();
 229  7
                 event.stateMask = stateMask;
 230  7
                 return event;
 231  
         }
 232  
 
 233  
         /**
 234  
          * Create a key event
 235  
          * 
 236  
          * @param keyCode the key code of the key pressed
 237  
          * @param character the character representation of the key
 238  
          * @return an event that encapsulates {@link #widget} and {@link #display}
 239  
          */
 240  
         private Event createKeyEvent(int keyCode, char character) {
 241  0
                 Event event = createEvent();
 242  0
                 event.keyCode = keyCode;
 243  0
                 event.character = character;
 244  0
                 return event;
 245  
         }
 246  
 
 247  
         /**
 248  
          * Click on the table at given coordinates
 249  
          * 
 250  
          * @param x the x co-ordinate of the click
 251  
          * @param y the y co-ordinate of the click
 252  
          * @since 2.0
 253  
          */
 254  
         protected void clickXY(int x, int y) {
 255  2
                 log.debug(MessageFormat.format("Clicking on {0}", this)); //$NON-NLS-1$
 256  2
                 notify(SWT.MouseEnter);
 257  2
                 notify(SWT.MouseMove);
 258  2
                 notify(SWT.Activate);
 259  2
                 notify(SWT.FocusIn);
 260  2
                 notify(SWT.MouseDown, createMouseEvent(x, y, 1, SWT.NONE, 1));
 261  2
                 notify(SWT.MouseUp, createMouseEvent(x, y, 1, SWT.BUTTON1, 1));
 262  2
                 notify(SWT.Selection, createSelectionEvent(SWT.BUTTON1));
 263  2
                 notify(SWT.MouseHover);
 264  2
                 notify(SWT.MouseMove);
 265  2
                 notify(SWT.MouseExit);
 266  2
                 notify(SWT.Deactivate);
 267  2
                 notify(SWT.FocusOut);
 268  2
                 log.debug(MessageFormat.format("Clicked on {0}", this)); //$NON-NLS-1$
 269  2
         }
 270  
 
 271  
         /**
 272  
          * Right click on the widget at given coordinates
 273  
          * 
 274  
          * @param x the x co-ordinate of the click
 275  
          * @param y the y co-ordinate of the click
 276  
          * @since 2.0
 277  
          */
 278  
         private void rightClickXY(int x, int y) {
 279  0
                 log.debug(MessageFormat.format("Right clicking on {0}", this)); //$NON-NLS-1$
 280  0
                 notify(SWT.MouseEnter);
 281  0
                 notify(SWT.MouseMove);
 282  0
                 notify(SWT.Activate);
 283  0
                 notify(SWT.FocusIn);
 284  0
                 notify(SWT.MouseDown, createMouseEvent(x, y, 1, SWT.BUTTON3, 1));
 285  0
                 notify(SWT.MouseUp);
 286  0
                 notify(SWT.Selection, createSelectionEvent(SWT.BUTTON3));                
 287  0
                 notify(SWT.MouseHover);
 288  0
                 notify(SWT.MouseMove);
 289  0
                 notify(SWT.MouseExit);
 290  0
                 notify(SWT.Deactivate);
 291  0
                 notify(SWT.FocusOut);
 292  0
                 log.debug(MessageFormat.format("Right clicked on {0}", this)); //$NON-NLS-1$
 293  0
         }
 294  
 
 295  
         /**
 296  
          * Double-click on the table at given coordinates
 297  
          * 
 298  
          * @param x the x co-ordinate of the click
 299  
          * @param y the y co-ordinate of the click
 300  
          * @since 2.0
 301  
          */
 302  
         protected void doubleClickXY(int x, int y) {
 303  1
                 log.debug(MessageFormat.format("Double-clicking on {0}", widget)); //$NON-NLS-1$
 304  1
                 notify(SWT.MouseEnter);
 305  1
                 notify(SWT.MouseMove);
 306  1
                 notify(SWT.Activate);
 307  1
                 notify(SWT.FocusIn);
 308  1
                 notify(SWT.MouseDown, createMouseEvent(x, y, 1, SWT.NONE, 1));
 309  1
                 notify(SWT.MouseUp, createMouseEvent(x, y, 1, SWT.BUTTON1, 1));
 310  1
                 notify(SWT.Selection, createSelectionEvent(SWT.BUTTON1));
 311  1
                 notify(SWT.MouseDoubleClick, createMouseEvent(x, y, 1, SWT.BUTTON1, 2));
 312  1
                 notify(SWT.MouseHover);
 313  1
                 notify(SWT.MouseMove);
 314  1
                 notify(SWT.MouseExit);
 315  1
                 notify(SWT.Deactivate);
 316  1
                 notify(SWT.FocusOut);
 317  1
                 log.debug(MessageFormat.format("Double-clicked on {0}", widget)); //$NON-NLS-1$
 318  1
         }
 319  
 
 320  
         @Override
 321  
         public String toString() {
 322  6586
                 return StringDescription.toString(description);
 323  
         }
 324  
 
 325  
         // /**
 326  
         // * Finds a menu matching the current {@link Matcher}.
 327  
         // *
 328  
         // * @param matcher the matcher used to find menus.
 329  
         // * @return all menus that match the matcher.
 330  
         // */
 331  
         // protected List findMenus(Matcher<?> matcher) {
 332  
         // return finder.findMenus(matcher);
 333  
         // }
 334  
 
 335  
         // /**
 336  
         // * Finds the menu on the main menu bar matching the given information.
 337  
         // *
 338  
         // * @param menuName the name of the menu.
 339  
         // * @param matcher the matcher used to find the menu.
 340  
         // * @return the first menuItem that matches the matcher
 341  
         // * @throws WidgetNotFoundException if the widget is not found.
 342  
         // */
 343  
         // protected Widget findMenu(Matcher<?> matcher, String menuName) throws WidgetNotFoundException {
 344  
         // return findMenu(getMenuMatcher(menuName), 0);
 345  
         // }
 346  
 
 347  
         // /**
 348  
         // * Gets the menu matcher for the given name.
 349  
         // *
 350  
         // * @param menuName the name of the menuitem that the matcher must match.
 351  
         // * @return {@link WidgetMatcherFactory#menuMatcher(String)}
 352  
         // */
 353  
         // protected Matcher getMenuMatcher(String menuName) {
 354  
         // return WidgetMatcherFactory.menuMatcher(menuName);
 355  
         // }
 356  
 
 357  
         // /**
 358  
         // * Finds the menu on the main menu bar matching the given information.
 359  
         // *
 360  
         // * @param matcher the matcher used to find the menu.
 361  
         // * @param index the index in the list of the menu items that match the matcher.
 362  
         // * @return the index(th) menuItem that matches the matcher
 363  
         // * @throws WidgetNotFoundException if the widget is not found.
 364  
         // */
 365  
         // protected Widget findMenu(Matcher<?> matcher, int index) throws WidgetNotFoundException {
 366  
         // List findMenus = findMenus(matcher);
 367  
         // if (!findMenus.isEmpty())
 368  
         // return (MenuItem) findMenus.get(index);
 369  
         // throw new WidgetNotFoundException("Could not find menu using matcher " + matcher);
 370  
         // }
 371  
 
 372  
         /**
 373  
          * Gets the text of this object's widget.
 374  
          * 
 375  
          * @return the text on the widget.
 376  
          */
 377  
         public String getText() {
 378  101
                 return SWTUtils.getText(widget);
 379  
         }
 380  
 
 381  
         /**
 382  
          * Gets the value of {@link Widget#getData(String))} for the key {@link SWTBotPreferences#DEFAULT_KEY} of this
 383  
          * object's widget.
 384  
          * 
 385  
          * @return the id that SWTBot may use to search this widget.
 386  
          * @see WithId
 387  
          */
 388  
         public String getId() {
 389  2
                 return syncExec(new StringResult() {
 390  
                         public String run() {
 391  2
                                 return (String) widget.getData(SWTBotPreferences.DEFAULT_KEY);
 392  
                         }
 393  
                 });
 394  
         }
 395  
 
 396  
         /**
 397  
          * Gets the tooltip of this object's widget.
 398  
          * 
 399  
          * @return the tooltip on the widget.
 400  
          * @since 1.0
 401  
          */
 402  
         public String getToolTipText() {
 403  1
                 return syncExec(new StringResult() {
 404  
                         public String run() {
 405  1
                                 return SWTUtils.getToolTipText(widget);
 406  
                         }
 407  
                 });
 408  
         }
 409  
 
 410  
         /**
 411  
          * Check if this widget has a style attribute.
 412  
          * 
 413  
          * @param w the widget.
 414  
          * @param style the style bits, one of the constants in {@link SWT}.
 415  
          * @return <code>true</code> if style is set on the widget.
 416  
          */
 417  
         protected boolean hasStyle(Widget w, int style) {
 418  126
                 return SWTUtils.hasStyle(w, style);
 419  
         }
 420  
 
 421  
         /**
 422  
          * Gets the context menu matching the text.
 423  
          * 
 424  
          * @param text the text on the context menu.
 425  
          * @return the menu that has the given text.
 426  
          * @throws WidgetNotFoundException if the widget is not found.
 427  
          */
 428  
         public SWTBotMenu contextMenu(final String text) throws WidgetNotFoundException {
 429  7
                 if (widget instanceof Control) {
 430  7
                         return contextMenu((Control) widget, text);
 431  
                 }
 432  0
                 throw new WidgetNotFoundException("Could not find menu: " + text); //$NON-NLS-1$
 433  
         }
 434  
 
 435  
         /**
 436  
          * Gets the context menu on the given control, matching the text.
 437  
          * 
 438  
          * @param control the control
 439  
          * @param text the text on the context menu.
 440  
          * @return the menu that has the given text.
 441  
          * @throws WidgetNotFoundException if the widget is not found.
 442  
          * @since 2.0
 443  
          */
 444  
         @SuppressWarnings("unchecked")
 445  
         // varargs and generics doesn't mix well!
 446  
         protected SWTBotMenu contextMenu(final Control control, final String text) {
 447  9
                 Matcher<MenuItem> withMnemonic = withMnemonic(text);
 448  9
                 final Matcher<MenuItem> matcher = allOf(widgetOfType(MenuItem.class), withMnemonic);
 449  9
                 final ContextMenuFinder menuFinder = new ContextMenuFinder(control);
 450  
 
 451  9
                 new SWTBot().waitUntil(new DefaultCondition() {
 452  
                         public String getFailureMessage() {
 453  0
                                 return "Could not find context menu with text: " + text; //$NON-NLS-1$
 454  
                         }
 455  
 
 456  
                         public boolean test() throws Exception {
 457  9
                                 return !menuFinder.findMenus(matcher).isEmpty();
 458  
                         }
 459  
                 });
 460  9
                 return new SWTBotMenu(menuFinder.findMenus(matcher).get(0), matcher);
 461  
         }
 462  
 
 463  
         /**
 464  
          * Gets if the object's widget is enabled.
 465  
          * 
 466  
          * @return <code>true</code> if the widget is enabled.
 467  
          * @see Control#isEnabled()
 468  
          */
 469  
         public boolean isEnabled() {
 470  870
                 if (widget instanceof Control)
 471  870
                         return syncExec(new BoolResult() {
 472  
                                 public Boolean run() {
 473  870
                                         return isEnabledInternal();
 474  
                                 }
 475  
                         });
 476  0
                 return false;
 477  
         }
 478  
 
 479  
         /**
 480  
          * Gets if the widget is enabled.
 481  
          * <p>
 482  
          * This method is not thread safe, and must be called from the UI thread.
 483  
          * </p>
 484  
          * 
 485  
          * @return <code>true</code> if the widget is enabled.
 486  
          * @since 1.0
 487  
          */
 488  
         protected boolean isEnabledInternal() {
 489  
                 try {
 490  7435
                         return ((Boolean) SWTUtils.invokeMethod(widget, "isEnabled")).booleanValue(); //$NON-NLS-1$
 491  553
                 } catch (Exception e) {
 492  553
                         return true;
 493  
                 }
 494  
         }
 495  
 
 496  
         /**
 497  
          * Invokes {@link ArrayResult#run()} on the UI thread.
 498  
          * 
 499  
          * @param toExecute the object to be invoked in the UI thread.
 500  
          * @return the array returned by toExecute.
 501  
          */
 502  
         protected <T> T[] syncExec(ArrayResult<T> toExecute) {
 503  6584
                 return UIThreadRunnable.syncExec(display, toExecute);
 504  
         }
 505  
 
 506  
         /**
 507  
          * Invokes {@link VoidResult#run()} on the UI thread.
 508  
          * 
 509  
          * @param toExecute the object to be invoked in the UI thread.
 510  
          */
 511  
         protected void syncExec(VoidResult toExecute) {
 512  121
                 UIThreadRunnable.syncExec(display, toExecute);
 513  121
         }
 514  
 
 515  
         /**
 516  
          * Invokes {@link ListResult#run()} on the UI thread.
 517  
          * 
 518  
          * @param toExecute the object to be invoked in the UI thread.
 519  
          * @return the list returned by toExecute
 520  
          */
 521  
         protected <E> List<E> syncExec(ListResult<E> toExecute) {
 522  40
                 return UIThreadRunnable.syncExec(display, toExecute);
 523  
         }
 524  
 
 525  
         /**
 526  
          * Invokes {@link BoolResult#run()} synchronously on the UI thread.
 527  
          * 
 528  
          * @param toExecute the object to be invoked in the UI thread.
 529  
          * @return the boolean returned by toExecute
 530  
          */
 531  
         protected boolean syncExec(BoolResult toExecute) {
 532  2011
                 return UIThreadRunnable.syncExec(display, toExecute);
 533  
         }
 534  
 
 535  
         /**
 536  
          * Invokes {@link BoolResult#run()} synchronously on the UI thread.
 537  
          * 
 538  
          * @param toExecute the object to be invoked in the UI thread.
 539  
          * @return the boolean returned by toExecute
 540  
          */
 541  
 
 542  
         protected String syncExec(StringResult toExecute) {
 543  39
                 return UIThreadRunnable.syncExec(display, toExecute);
 544  
         }
 545  
 
 546  
         /**
 547  
          * Invokes {@link Result#run()} synchronously on the UI thread.
 548  
          * 
 549  
          * @param toExecute the object to be invoked in the UI thread.
 550  
          * @return the boolean returned by toExecute
 551  
          */
 552  
         protected <T> T syncExec(Result<T> toExecute) {
 553  461
                 return UIThreadRunnable.syncExec(display, toExecute);
 554  
         }
 555  
 
 556  
         /**
 557  
          * Invokes {@link WidgetResult#run()} synchronously on the UI thread.
 558  
          * 
 559  
          * @param toExecute the object to be invoked in the UI thread.
 560  
          * @return the Widget returned by toExecute
 561  
          */
 562  
         protected T syncExec(WidgetResult<T> toExecute) {
 563  41
                 return UIThreadRunnable.syncExec(display, toExecute);
 564  
         }
 565  
 
 566  
         /**
 567  
          * Invokes {@link IntResult#run()} synchronously on the UI thread.
 568  
          * 
 569  
          * @param toExecute the object to be invoked in the UI thread.
 570  
          * @return the integer returned by toExecute
 571  
          */
 572  
 
 573  
         protected int syncExec(IntResult toExecute) {
 574  183
                 return UIThreadRunnable.syncExec(display, toExecute);
 575  
         }
 576  
 
 577  
         /**
 578  
          * Invokes {@link BoolResult#run()} asynchronously on the UI thread.
 579  
          * 
 580  
          * @param toExecute the object to be invoked in the UI thread.
 581  
          */
 582  
         protected void asyncExec(VoidResult toExecute) {
 583  7371
                 UIThreadRunnable.asyncExec(display, toExecute);
 584  7371
         }
 585  
 
 586  
         /**
 587  
          * Gets the foreground color of the widget.
 588  
          * 
 589  
          * @return the foreground color on the widget, or <code>null</code> if the widget is not an instance of
 590  
          *         {@link Control}.
 591  
          * @since 1.0
 592  
          */
 593  
         public Color foregroundColor() {
 594  1
                 return syncExec(new Result<Color>() {
 595  
                         public Color run() {
 596  1
                                 if (widget instanceof Control)
 597  1
                                         return ((Control) widget).getForeground();
 598  0
                                 return null;
 599  
                         }
 600  
                 });
 601  
         }
 602  
 
 603  
         /**
 604  
          * Gets the background color of the widget.
 605  
          * 
 606  
          * @return the background color on the widget, or <code>null</code> if the widget is not an instance of
 607  
          *         {@link Control}.
 608  
          * @since 1.0
 609  
          */
 610  
         public Color backgroundColor() {
 611  1
                 return syncExec(new Result<Color>() {
 612  
                         public Color run() {
 613  1
                                 if (widget instanceof Control)
 614  1
                                         return ((Control) widget).getBackground();
 615  0
                                 return null;
 616  
                         }
 617  
                 });
 618  
         }
 619  
 
 620  
         /**
 621  
          * Check if the widget is enabled, throws if the widget is disabled.
 622  
          * 
 623  
          * @since 1.3
 624  
          */
 625  
         protected void assertEnabled() {
 626  77
                 Assert.isTrue(isEnabled(), MessageFormat.format("Widget {0} is not enabled.", this)); //$NON-NLS-1$ //$NON-NLS-2$
 627  77
         }
 628  
         
 629  
         /**
 630  
          * Wait until the widget is enabled.
 631  
          * 
 632  
          * @since 2.0
 633  
          */
 634  
         protected void waitForEnabled() {
 635  1051
                 new SWTBot().waitUntil(widgetIsEnabled(this));
 636  1051
         }
 637  
 
 638  
         /**
 639  
          * Checks if the widget is visible.
 640  
          * 
 641  
          * @return <code>true</code> if the widget is visible, <code>false</code> otherwise.
 642  
          * @since 1.0
 643  
          */
 644  
         public boolean isVisible() {
 645  0
                 return syncExec(new BoolResult() {
 646  
                         public Boolean run() {
 647  0
                                 if (widget instanceof Control)
 648  0
                                         return ((Control) widget).isVisible();
 649  0
                                 return true;
 650  
                         }
 651  
                 });
 652  
         }
 653  
 
 654  
         /**
 655  
          * Sets the focus on this control.
 656  
          * 
 657  
          * @since 1.2
 658  
          */
 659  
         public void setFocus() {
 660  47
                 waitForEnabled();
 661  47
                 log.debug(MessageFormat.format("Attempting to set focus on {0}", this));
 662  47
                 syncExec(new VoidResult() {
 663  
                         public void run() {
 664  47
                                 if (widget instanceof Control) {
 665  47
                                         Control control = (Control) widget;
 666  47
                                         if (hasFocus(control))
 667  23
                                                 return;
 668  24
                                         control.getShell().forceActive();
 669  24
                                         control.getShell().forceFocus();
 670  24
                                         control.forceFocus();
 671  
                                 }
 672  24
                         }
 673  
 
 674  
                         private boolean hasFocus(Control control) {
 675  47
                                 return control.isFocusControl();
 676  
                         }
 677  
                 });
 678  47
         }
 679  
 
 680  
         /**
 681  
          * @param traverse the kind of traversal to perform.
 682  
          * @return <code>true</code> if the traversal succeeded.
 683  
          * @see Control#traverse(int)
 684  
          */
 685  
         public boolean traverse(final Traverse traverse) {
 686  1
                 waitForEnabled();
 687  1
                 setFocus();
 688  
 
 689  1
                 if (!(widget instanceof Control))
 690  0
                         throw new UnsupportedOperationException("Can only traverse widgets of type Control. You're traversing a widget of type: " //$NON-NLS-1$
 691  0
                                         + widget.getClass().getName());
 692  
 
 693  1
                 return syncExec(new BoolResult() {
 694  
                         public Boolean run() {
 695  1
                                 return ((Control) widget).traverse(traverse.type);
 696  
                         }
 697  
                 });
 698  
         }
 699  
 
 700  
         /**
 701  
          * @return <code>true</code> if this widget has focus.
 702  
          * @see Display#getFocusControl()
 703  
          */
 704  
         public boolean isActive() {
 705  4
                 return syncExec(new BoolResult() {
 706  
                         public Boolean run() {
 707  4
                                 return display.getFocusControl() == widget;
 708  
                         }
 709  
                 });
 710  
         }
 711  
 
 712  
         /**
 713  
          * Clicks on this widget.
 714  
          * 
 715  
          * @return itself.
 716  
          */
 717  
         protected AbstractSWTBot<T> click() {
 718  0
                 throw new UnsupportedOperationException("This operation is not supported by this widget.");
 719  
         }
 720  
 
 721  
         /**
 722  
          * Empty method stub, since it should be overridden by subclass#rightClick
 723  
          * 
 724  
          * @return itself.
 725  
          */
 726  
         protected AbstractSWTBot<T> rightClick() {
 727  0
                 throw new UnsupportedOperationException("This operation is not supported by this widget.");
 728  
         }
 729  
 
 730  
         /**
 731  
          * Perform a click action at the given coordinates
 732  
          * 
 733  
          * @param x the x coordinate
 734  
          * @param y the y coordinate
 735  
          * @param post Whether or not {@link Display#post} should be used
 736  
          * @return itself.
 737  
          */
 738  
         protected AbstractSWTBot<T> click(final int x, final int y, final boolean post) {
 739  1
                 if (post) {
 740  1
                         asyncExec(new VoidResult() {
 741  
                                 public void run() {
 742  1
                                         moveMouse(x, y);
 743  1
                                         mouseDown(x, y, 1);
 744  1
                                         mouseUp(x, y, 1);
 745  1
                                 }
 746  
                         });
 747  1
                         sleep(500);
 748  
                 } else
 749  0
                         clickXY(x, y);
 750  1
                 return this;
 751  
         }
 752  
 
 753  
         /**
 754  
          * Perform a right-click action at the given coordinates
 755  
          * 
 756  
          * @param x the x coordinate
 757  
          * @param y the y coordinate
 758  
          * @param post Whether or not {@link Display#post} should be used
 759  
          * @return itself.
 760  
          */
 761  
         protected AbstractSWTBot<T> rightClick(final int x, final int y, final boolean post) {
 762  0
                 if (post) {
 763  0
                         syncExec(new VoidResult() {
 764  
                                 public void run() {
 765  0
                                         moveMouse(x, y);
 766  0
                                         mouseDown(x, y, 3);
 767  0
                                         mouseUp(x, y, 3);
 768  0
                                 }
 769  
                         });
 770  
                 } else
 771  0
                         rightClickXY(x, y);
 772  0
                 return this;
 773  
         }
 774  
 
 775  
         /**
 776  
          * Post an SWT.MouseMove event
 777  
          * 
 778  
          * @param x the x coordinate
 779  
          * @param y the y coordinate
 780  
          */
 781  
         void moveMouse(final int x, final int y) {
 782  1
                 asyncExec(new VoidResult() {
 783  
                         public void run() {
 784  1
                                 Event event = createMouseEvent(x, y, 0, 0, 0);
 785  1
                                 event.type = SWT.MouseMove;
 786  1
                                 display.post(event);
 787  1
                         }
 788  
                 });
 789  1
         }
 790  
 
 791  
         /**
 792  
          * Post an SWT.MouseDown event
 793  
          * 
 794  
          * @param x the x coordinate
 795  
          * @param y the y coordinate
 796  
          * @param button the mouse button to be pressed
 797  
          */
 798  1
         private void mouseDown(final int x, final int y, final int button) {
 799  1
                 asyncExec(new VoidResult() {
 800  
                         public void run() {
 801  1
                                 Event event = createMouseEvent(x, y, button, 0, 0);
 802  1
                                 event.type = SWT.MouseDown;
 803  1
                                 display.post(event);
 804  1
                         }
 805  
                 });
 806  1
         }
 807  
 
 808  
         /**
 809  
          * Post an SWT.MouseUp event.
 810  
          * 
 811  
          * @param x the x coordinate
 812  
          * @param y the y coordinate
 813  
          * @param button the mouse button to be pressed
 814  
          */
 815  1
         private void mouseUp(final int x, final int y, final int button) {
 816  1
                 asyncExec(new VoidResult() {
 817  
                         public void run() {
 818  1
                                 Event event = createMouseEvent(x, y, button, 0, 0);
 819  1
                                 event.type = SWT.MouseUp;
 820  1
                                 display.post(event);
 821  1
                         }
 822  
                 });
 823  1
         }
 824  
 
 825  
         /**
 826  
          * @return the absolute location of the widget relative to the display.
 827  
          */
 828  
         protected Rectangle absoluteLocation() {
 829  0
                 throw new UnsupportedOperationException("This operation is not supported by this widget.");
 830  
         }
 831  
 
 832  
         /**
 833  
          * @return the keyboard to use to type on this widget.
 834  
          */
 835  
         protected Keyboard keyboard() {
 836  4
                 if (keyboard == null)
 837  4
                         keyboard = KeyboardFactory.getDefaultKeyboard(widget, description);
 838  4
                 return keyboard;
 839  
         }
 840  
 
 841  
         /**
 842  
          * Presses the shortcut specified by the given keys.
 843  
          *
 844  
          * @param modificationKeys the combination of {@link SWT#ALT} | {@link SWT#CTRL} | {@link SWT#SHIFT} |
 845  
          *            {@link SWT#COMMAND}.
 846  
          * @param c the character
 847  
          * @return the same instance
 848  
          * @see Keystrokes#toKeys(int, char)
 849  
          */
 850  
         public AbstractSWTBot<T> pressShortcut(int modificationKeys, char c) {
 851  0
                 waitForEnabled();
 852  0
                 setFocus();
 853  0
                 keyboard().pressShortcut(modificationKeys, c);
 854  0
                 return this;
 855  
         }
 856  
 
 857  
         /**
 858  
          * Presses the shortcut specified by the given keys.
 859  
          *
 860  
          * @param modificationKeys the combination of {@link SWT#ALT} | {@link SWT#CTRL} | {@link SWT#SHIFT} | {@link SWT#COMMAND}.
 861  
          * @param keyCode the keyCode, these may be special keys like F1-F12, or navigation keys like HOME, PAGE_UP
 862  
          * @param c the character
 863  
          * @return the same instance
 864  
          * @see Keystrokes#toKeys(int, char)
 865  
          */
 866  
         public AbstractSWTBot<T> pressShortcut(int modificationKeys, int keyCode, char c) {
 867  0
                 waitForEnabled();
 868  0
                 setFocus();
 869  0
                 keyboard().pressShortcut(modificationKeys, keyCode, c);
 870  0
                 return this;
 871  
         }
 872  
 
 873  
         /**
 874  
          * Presses the shortcut specified by the given keys.
 875  
          *
 876  
          * @param keys the keys to press
 877  
          * @return the same instance
 878  
          * @see Keyboard#pressShortcut(KeyStroke...)
 879  
          * @see Keystrokes
 880  
          */
 881  
         public AbstractSWTBot<T> pressShortcut(KeyStroke... keys) {
 882  0
                 waitForEnabled();
 883  0
                 setFocus();
 884  0
                 keyboard().pressShortcut(keys);
 885  0
                 return this;
 886  
         }
 887  
 }