org.eclipse.rcptt.tesla.internal.ui.player.SWTUIPlayer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.rcptt.tesla.internal.ui.player.SWTUIPlayer.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2016 Xored Software Inc and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Xored Software Inc - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.rcptt.tesla.internal.ui.player;

import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerTextUtils.getMenuText;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerTextUtils.getRawText;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerTextUtils.getText;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerTextUtils.removeAcceleratorFromText;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerTextUtils.safeMatches;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerWidgetUtils.canClick;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerWrapUtils.unwrap;
import static org.eclipse.rcptt.tesla.internal.ui.player.PlayerWrapUtils.unwrapWidget;
import static org.eclipse.rcptt.util.swt.Bounds.centerAbs;
import static org.eclipse.rcptt.util.swt.Bounds.centerRel;
import static org.eclipse.rcptt.util.swt.Events.createClick;
import static org.eclipse.rcptt.util.swt.Events.createMouseDown;
import static org.eclipse.rcptt.util.swt.Events.createMouseUp;
import static org.eclipse.rcptt.util.swt.Events.createSelection;
import static org.eclipse.rcptt.util.swt.TableTreeUtil.getItemBounds;
import static org.eclipse.rcptt.util.swt.Widgets.isToggleButton;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.preference.ColorSelector;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerColumn;
import org.eclipse.osgi.util.NLS;
import org.eclipse.rcptt.sherlock.core.SherlockTimerRunnable;
import org.eclipse.rcptt.tesla.core.Q7WaitUtils;
import org.eclipse.rcptt.tesla.core.TeslaLimits;
import org.eclipse.rcptt.tesla.core.context.ContextManagement;
import org.eclipse.rcptt.tesla.core.context.ContextManagement.Context;
import org.eclipse.rcptt.tesla.core.info.Q7WaitInfoRoot;
import org.eclipse.rcptt.tesla.core.protocol.ElementKind;
import org.eclipse.rcptt.tesla.core.protocol.GenericElementKind;
import org.eclipse.rcptt.tesla.core.protocol.UIColor;
import org.eclipse.rcptt.tesla.internal.core.TeslaCore;
import org.eclipse.rcptt.tesla.internal.core.TeslaExecutionFailedException;
import org.eclipse.rcptt.tesla.internal.ui.player.specific.GetWindowPlayer;
import org.eclipse.rcptt.tesla.internal.ui.player.viewers.TableViewerItem;
import org.eclipse.rcptt.tesla.internal.ui.player.viewers.Viewers;
import org.eclipse.rcptt.tesla.swt.TeslaSWTMessages;
import org.eclipse.rcptt.tesla.swt.dialogs.SWTDialogManager;
import org.eclipse.rcptt.tesla.swt.events.ITimerExecHelper;
import org.eclipse.rcptt.tesla.swt.events.TeslaEventManager;
import org.eclipse.rcptt.tesla.swt.events.TeslaTimerExecManager;
import org.eclipse.rcptt.tesla.swt.events.TeslaTimerExecManager.TimerInfo;
import org.eclipse.rcptt.tesla.swt.workbench.EclipseWorkbenchProvider;
import org.eclipse.rcptt.tesla.ui.IViewerItem;
import org.eclipse.rcptt.util.ShellUtilsProvider;
import org.eclipse.rcptt.util.swt.Bounds;
import org.eclipse.rcptt.util.swt.Events;
import org.eclipse.rcptt.util.swt.TabCTabUtil;
import org.eclipse.rcptt.util.swt.TableTreeUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.CBanner;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.CoolBar;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Synchronizer;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.ToolTip;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.decorators.DecorationScheduler;
import org.eclipse.ui.internal.decorators.DecoratorManager;
import org.eclipse.ui.internal.registry.EditorRegistry;

@SuppressWarnings("restriction")
public final class SWTUIPlayer {

    final Display display;
    private SWTUIElement[] ignoreWindows;
    private Shell[] ignoredShells;
    private final List<File> screenshotsDuringSession = null;
    private final SWTEvents events;
    private volatile Throwable error = null;

    public Throwable getError() {
        return error;
    }

    private static SWTKeyboard keyboard = new SWTKeyboard();

    private final Map<Context, List<Runnable>> runnables = new HashMap<Context, List<Runnable>>();
    private static Map<Display, SWTUIPlayer> players = new HashMap<Display, SWTUIPlayer>();

    protected static Map<Class<?>, ElementKind> elementKinds = new LinkedHashMap<Class<?>, ElementKind>();
    private final ITimerExecHelper timerListener;
    private static List<ISWTUIPlayerExtension> extensions = new ArrayList<ISWTUIPlayerExtension>();
    static {
        elementKinds.put(Shell.class, ElementKind.Window);
        elementKinds.put(CBanner.class, ElementKind.CBanner);
        elementKinds.put(ToolBar.class, ElementKind.Toolbar);
        elementKinds.put(CoolBar.class, ElementKind.CoolBar);
        elementKinds.put(Button.class, ElementKind.Button);
        elementKinds.put(ToolItem.class, ElementKind.Button);
        elementKinds.put(Label.class, ElementKind.Label);
        elementKinds.put(CLabel.class, ElementKind.Label);
        elementKinds.put(Group.class, ElementKind.Group);
        elementKinds.put(TabFolder.class, ElementKind.TabFolder);
        elementKinds.put(CTabFolder.class, ElementKind.TabFolder);
        elementKinds.put(Text.class, ElementKind.Text);
        elementKinds.put(StyledText.class, ElementKind.Text);
        elementKinds.put(Spinner.class, ElementKind.Text);
        elementKinds.put(Link.class, ElementKind.Link);
        elementKinds.put(Combo.class, ElementKind.Combo);
        elementKinds.put(CCombo.class, ElementKind.Combo);
        elementKinds.put(Tree.class, ElementKind.Tree);
        elementKinds.put(org.eclipse.swt.widgets.List.class, ElementKind.List);
        elementKinds.put(MenuItem.class, ElementKind.Menu);
        elementKinds.put(Menu.class, ElementKind.Menu);
        elementKinds.put(Table.class, ElementKind.Table);
        elementKinds.put(CTabItem.class, ElementKind.TabItem);
        elementKinds.put(TabItem.class, ElementKind.TabItem);
        elementKinds.put(IViewReference.class, ElementKind.View);
        elementKinds.put(IEditorReference.class, ElementKind.Editor);
        elementKinds.put(DateTime.class, ElementKind.DateTime);
        elementKinds.put(Slider.class, ElementKind.Slider);
        elementKinds.put(Link.class, ElementKind.Link);
        elementKinds.put(Shell.class, ElementKind.Window);
        elementKinds.put(TreeItem.class, ElementKind.Item);
        elementKinds.put(TableItem.class, ElementKind.Item);
        elementKinds.put(Canvas.class, ElementKind.Canvas);
        elementKinds.put(Browser.class, ElementKind.Browser);
        elementKinds.put(TreeColumn.class, ElementKind.ColumnHeader);
        elementKinds.put(TableColumn.class, ElementKind.ColumnHeader);

        // Debug check right order of classes in elementKings Map
        // checkIntegrity(elementKinds.keySet().toArray(new Class<?>[0]));
    }

    public Shell[] getIgnored() {
        return ignoredShells;
    }

    protected SWTUIPlayer(Display display, Shell... ignoreWindows) {
        this.display = display;
        this.events = new SWTEvents(display);
        if (ignoreWindows != null) {
            this.ignoreWindows = new SWTUIElement[ignoreWindows.length];
            this.ignoredShells = ignoreWindows;
            int i = 0;
            for (Shell shell : ignoreWindows) {
                this.ignoreWindows[i++] = wrap(shell);
            }
        }
        collector = new UIJobCollector();
        Job.getJobManager().addJobChangeListener(collector);
        timerListener = getTimerExecHelper();
        TeslaTimerExecManager.getManager().addEventListener(timerListener);
    }

    private ITimerExecHelper getTimerExecHelper() {
        return new ITimerExecHelper() {
            @Override
            public boolean needNullify(Runnable run, int time) {
                String clName = null;
                if (run instanceof SherlockTimerRunnable) {
                    clName = ((SherlockTimerRunnable) run).getRunnable().getClass().getName();
                } else {
                    clName = run.getClass().getName();
                }
                if (!clName.contains("org.eclipse.swt") && !clName.contains("org.eclipse.gmf")
                        && !clName.contains("org.eclipse.jface") && !clName.contains("org.eclipse.gef")) {
                    if (clName.contains(Display.class.getName())) {
                        return false;
                    }
                    if (clName.startsWith("org.eclipse.nebula.widgets.oscilloscope.OscilloscopeDispatcher")) {
                        return false;
                    }
                    if (clName.startsWith("org.eclipse.tm.internal.terminal.textcanvas.PollingTextCanvasModel")) {
                        return false;
                    }

                    if (time < TeslaLimits.getTimerExecsWaitNullify()) {
                        // Check if same thread is not already in stack trace.
                        Context currentCtx = ContextManagement.makeContext(Thread.currentThread().getStackTrace());
                        if (currentCtx.containsClass(clName)) {
                            // Do not allow to nullity timers executed from timers.
                            return false;
                        }
                        // System.out.println("Nullifying timerexec:" + clName);
                        return true;
                    }
                }
                return false;
            }
        };
    }

    private SWTUIPlayer(Display display) {
        this(display, (Shell[]) null);
    }

    private static Class<?>[] one(Class<?> cl) {
        return new Class[] { cl };
    }

    /**
     * Determines the UI element matching the filter.
     * <p>
     * Can be extended by {@link ISWTUIPlayerExtension#select(SWTUIPlayer, PlayerSelectionFilter)}
     */
    public SWTUIElement select(PlayerSelectionFilter filter) {
        SWTUIElement result = null;

        for (ISWTUIPlayerExtension ext : getExtensions()) {
            result = ext.select(this, filter);
            if (result != null) {
                return result;
            }
        }

        switch (filter.kind.kind) {
        case Unknown:
            result = null;
            break;
        case EclipseWindow:
            result = selectEclipseWindow(filter.index);
            break;
        case QuickAccess:
            result = selectQuickAccess();
            break;
        case Window:
            result = new GetWindowPlayer(this, ignoreWindows).selectShell(filter);
            break;
        case Menu:
            result = selectMenu(filter);
            break;
        case Button:
            result = selectWidget(filter, Button.class, ToolItem.class);
            if (result == null) {
                result = selectButton(filter);
            }
            break;
        case Group:
            result = selectWidget(filter, Group.class);
            break;
        case Text:
            result = selectWidget(filter, Text.class, StyledText.class, Spinner.class);
            break;
        case Combo:
            result = selectWidget(filter, Combo.class, CCombo.class);
            break;
        case Tree:
            result = selectWidget(filter.withoutPattern(), false, Tree.class);
            break;
        case Toolbar:
            result = selectWidget(filter.withoutPattern(), false, ToolBar.class);
            break;
        case CBanner:
            result = selectWidget(filter.withoutPattern(), false, CBanner.class);
            break;
        case CoolBar:
            result = selectWidget(filter.withoutPattern(), false, CoolBar.class);
            break;
        case Canvas:
            result = selectWidget(filter.withoutPattern(), false, Canvas.class);
            break;
        case List:
            result = selectWidget(filter.withoutPattern(), false, org.eclipse.swt.widgets.List.class);
            break;
        case Table:
            result = selectWidget(filter.withoutPattern(), false, Table.class);
            break;
        case Label:
            result = selectWidget(filter, Label.class, CLabel.class);
            break;
        case TabItem:
            result = selectWidget(filter, CTabItem.class, TabItem.class);
            break;
        case TabFolder:
            result = selectWidget(filter, CTabFolder.class, TabFolder.class);
            break;
        case View:
            result = selectView(filter);
            break;
        case Editor:
            result = selectEditor(filter);
            break;
        case Any:
            result = selectWidget(filter.withoutKind());
            break;
        case Item:
            result = selectItem(filter);
            break;
        case Browser:
            result = selectWidget(filter.withoutPattern(), Browser.class);
            break;
        case DateTime:
            result = selectWidget(filter.withoutPattern(), DateTime.class);
            break;
        case Slider:
            result = selectWidget(filter.withoutPattern(), Slider.class);
            break;
        case ColumnHeader:
            result = selectColumnHeader(filter);
            break;
        }
        // if (result != null) {
        // if (isDisabled(result)) {
        // // System.out.println("#");
        // }
        // }
        if (result == null) {
            makeScreenShot();
        }
        return result;
    }

    private SWTUIElement selectColumnHeader(PlayerSelectionFilter f) {
        Widget unwrapped = unwrapWidget(f.parent);
        if (!(unwrapped instanceof Tree || unwrapped instanceof Table))
            return null;

        return wrap(TableTreeUtil.findColumn(unwrapped, f.pattern, f.index == null ? 0 : f.index));
    }

    private SWTUIElement selectButton(PlayerSelectionFilter f) {
        final String pattern = f.pattern;
        if (pattern != null && pattern.contains("(") && pattern.endsWith(")")) {
            // With accelerator
            int pos = pattern.indexOf('(');
            String prefix = pattern.substring(0, pos);
            String accel = pattern.substring(pos + 1, pattern.length() - 1);
            String[] split = accel.split("\\+");
            Arrays.sort(split);
            if (split.length > 2) {
                SWTUIElement[] children = this.children.collectFor(f.parent, ignoreWindows, true,
                        new Class[] { Button.class, ToolItem.class }, f.after);
                // Ctrl + Alt + S like combinations
                int cur = 0;
                for (SWTUIElement widget : children) {
                    String text = getText(widget);
                    if (text != null) {
                        text = removeAcceleratorFromText(text);
                        if (text != null && text.contains("(") && text.endsWith(")")) {
                            int pos2 = text.indexOf('(');
                            String prefix2 = text.substring(0, pos2);
                            String accel2 = text.substring(pos2 + 1, text.length() - 1);
                            String[] split2 = accel2.split("\\+");
                            Arrays.sort(split2);

                            if (prefix2.equals(prefix) && Arrays.equals(split2, split)) {
                                if ((f.index != null && cur == f.index.intValue()) || f.index == null) {
                                    return widget;
                                }
                                cur++;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    @SuppressWarnings("unused")
    private SWTUIElement selectItem(PlayerSelectionFilter f) {
        final SWTUIElement parent = f.parent;
        if (parent != null && unwrapWidget(parent) instanceof Tree && f.path != null) {
            final Tree tree = (Tree) unwrapWidget(parent);
            TreeItem current = (TreeItem) Viewers.firstMatch(f.path, tree);
            if (current != null) {
                return wrap(current);
            }
        } else if (parent != null && unwrapWidget(parent) instanceof Tree && f.indexes != null
                && f.indexes.length == 2) {
            // Select item with column
            final Tree tree = (Tree) unwrapWidget(parent);
            TreeItem[] items = getExpandedTreeItems(tree);
            if (items.length > f.indexes[1]) {
                return new ItemUIElement(items[f.indexes[1]], this, f.indexes[0]);
            }
        }
        if (parent != null && unwrapWidget(parent) instanceof Table && f.path != null && f.path.length > 0) {
            final Table table = (Table) unwrapWidget(parent);
            Object current = unwrapWidget(parent);
            TableItem[] items = ((Table) current).getItems();
            if (items == null) {
                return null;
            }

            IViewerItem[] viewerItems = Viewers.getViewerItems(items);
            for (String part : f.path) {
                for (TableItem item : items) {
                    if (item.isDisposed()) {
                        continue; // Skip disposed items
                    }
                    String itemText = toSelectionItem(
                            Viewers.getTableItemText(new TableViewerItem(item), part, viewerItems));
                    if (itemText != null) {
                        if (itemText.equals(part) || safeMatches(itemText, part)) {
                            return wrap(item);
                        }
                    }
                }
            }

        } else if (parent != null && unwrapWidget(parent) instanceof Table && f.indexes != null
                && f.indexes.length == 2) {
            Object current = unwrapWidget(parent);
            TableItem[] items = ((Table) current).getItems();
            if (items.length > f.indexes[1]) {
                return new ItemUIElement(items[f.indexes[1]], this, f.indexes[0]);
            }
        }
        return null;
    }

    public static TreeItem[] getExpandedTreeItems(Tree tree) {
        List<TreeItem> items = new ArrayList<TreeItem>();
        for (int i = 0; i < tree.getItemCount(); i++) {
            TreeItem currentItem = tree.getItem(i);
            if (currentItem.isDisposed()) {
                continue;// Skip disposed items
            }
            items.add(currentItem);
            if (currentItem.getExpanded()) {
                items.addAll(getExpandedTreeItems(currentItem));
            }
        }
        return items.toArray(new TreeItem[items.size()]);
    }

    private static List<TreeItem> getExpandedTreeItems(TreeItem item) {
        List<TreeItem> items = new ArrayList<TreeItem>();
        for (int i = 0; i < item.getItemCount(); i++) {
            TreeItem currentItem = item.getItem(i);
            items.add(currentItem);
            if (currentItem.getExpanded()) {
                items.addAll(getExpandedTreeItems(currentItem));
            }
        }
        return items;
    }

    private SWTUIElement selectQuickAccess() {
        Text quickAccess = EclipseWorkbenchProvider.getProvider().getQuickAccess();
        return quickAccess == null ? null : wrap(quickAccess);
    }

    private SWTUIElement selectEclipseWindow(Integer index) {
        IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
        if (index == null) {
            return wrap(windows[0].getShell());
        } else if (index.intValue() < windows.length) {
            return wrap(windows[index.intValue()].getShell());
        }
        return null;
    }

    // private IScreenCapturer screenCapturer = null;
    private Context context;
    private final UIJobCollector collector;
    private final Map<Widget, Point> widgetToMouseForMenus = new HashMap<Widget, Point>();
    private static Set<WeakReference<Menu>> shownMenus = new HashSet<WeakReference<Menu>>();

    public void makeScreenShot() {
        // Disable making of screenshots

        // if (screenCapturer == null) {
        // screenCapturer = new AWTScreenCapturer(TeslaCore.getDefault()
        // .getStateLocation().append("screenshots").toFile());
        // }
        // final Rectangle bounds = display.getBounds();
        // screenshotsDuringSession.add(screenCapturer.makeScreenShot(bounds.x,
        // bounds.y, bounds.width, bounds.height));
    }

    public UIColor getSelectedColor(SWTUIElement uiElement) {
        Widget widget = unwrapWidget(uiElement);
        if (widget instanceof Button) {
            ColorSelector colorSelector = TeslaSWTAccess.getColorSelector((Button) widget);
            if (colorSelector != null) {
                return new UIColor(colorSelector.getColorValue().red, colorSelector.getColorValue().green,
                        colorSelector.getColorValue().blue);// colorSelector.getColorValue();
            }
        }
        return null;

    }

    public void setSelectedColor(SWTUIElement uiElement, final UIColor color) {
        Widget widget = unwrapWidget(uiElement);
        if (widget instanceof Button) {
            ColorSelector colorSelector = TeslaSWTAccess.getColorSelector((Button) widget);
            if (colorSelector != null) {
                colorSelector.setColorValue(new RGB(color.r, color.g, color.b));
            }
        }
    }

    public SWTUIElement selectWidget(PlayerSelectionFilter filter, Class<?>... classes) {
        return selectWidget(filter, true, classes);
    }

    public SWTUIElement selectWidget(PlayerSelectionFilter f, boolean checkText, Class<?>... classes) {

        SWTUIElement[] children = this.children.collectFor(f.parent, ignoreWindows, true, classes, f.after);

        final GenericElementKind kind = f.kind;

        // filter the children by kind
        if (kind != null) {
            List<SWTUIElement> filteredByKind = new ArrayList<SWTUIElement>();
            for (SWTUIElement element : children) {
                if (element.isSuitableForKind(kind)) {
                    filteredByKind.add(element);
                }
            }
            children = filteredByKind.toArray(new SWTUIElement[filteredByKind.size()]);
        }
        //

        if (f.pattern == null) {

            if (f.index == null && children.length > 0) {
                // Locate first button without title
                if (classes != null) {
                    List<Class<?>> classesList = Arrays.asList(classes);
                    if (classesList.contains(Button.class)) {

                        for (SWTUIElement swtuiElement : children) {
                            String text = swtuiElement.getText();
                            if (text != null && text.trim().length() == 0) {
                                return swtuiElement;
                            }
                        }
                    }
                }
            }
            //

            int index = (f.index == null) ? 0 : f.index;
            if (children.length > index) {
                // Search index same as calculated when record in
                // SWTWidgetLocator

                // the children array is already filtered
                SWTUIElement child = children[index];

                // TODO analyze usages & eliminate kind overriding
                if (kind != null && !kind.is(child.getKind()))
                    child.overrideKind(kind);

                return child;
            }
        }

        if (checkText) {
            int cur = 0;
            Map<SWTUIElement, String> wtMap = new HashMap<SWTUIElement, String>();
            if (f.pattern != null) {
                for (SWTUIElement widget : children) {
                    String text = getText(widget);
                    if (text != null) {
                        text = removeAcceleratorFromText(text);
                        wtMap.put(widget, text);
                        if (text != null && text.equals(f.pattern)) {
                            if ((f.index != null && cur == f.index.intValue()) || f.index == null) {
                                return widget;
                            }
                            if (kind == null || widget.getKind().is(kind)) {
                                cur++;
                            }
                        }
                    }
                }
                cur = 0;
                for (SWTUIElement widget : children) {
                    String text = getText(widget);
                    if (text != null) {
                        text = removeAcceleratorFromText(text);
                        wtMap.put(widget, text);
                        if (text != null && (safeMatches(text, f.pattern))) {
                            if ((f.index != null && cur == f.index.intValue()) || f.index == null) {
                                return widget;
                            }
                            if (kind == null || widget.getKind().is(kind)) {
                                cur++;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    // stable view is a view that do not change its title, so
    // we can skip it while gathering actual titles of views
    private static final Set<String> stableViews = new HashSet<String>();
    static {
        stableViews.add("org.eclipse.ui.views.PropertySheet");
        stableViews.add("org.eclipse.ui.views.ProblemView");
    }

    // @SuppressWarnings("restriction")
    public SWTUIElement selectView(PlayerSelectionFilter f) {
        final String pattern = f.pattern;

        // IViewDescriptor[] views =
        // PlatformUI.getWorkbench().getViewRegistry().getViews();
        IViewReference[] views = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                .getViewReferences();
        int currIdx = 0;
        for (IViewReference iViewRef : views) {
            try {
                String label = iViewRef.getPartName();
                String id = iViewRef.getId();

                if ((label != null && (label.equals(pattern) || safeMatches(label, pattern)))
                        || (id != null && (id.equals(pattern) || safeMatches(id, pattern)))) {
                    if (f.index == null || f.index.equals(currIdx))
                        return wrap(iViewRef);
                    currIdx++;
                }
            } catch (Exception e) {
                // Skip brokeb parts.
                TeslaCore.log(e);
            }
        }

        TeslaCore.log("Can not find view by pattern \"" + pattern + "\". Activating views...");

        // Not found, lets go with resolve of view parts, it will initialize
        // titles.
        currIdx = 0;
        for (IViewReference iViewRef : views) {
            // try to skip well-known buggy views with immutable titles
            if (stableViews.contains(iViewRef.getId()))
                continue;

            IWorkbenchPart part = iViewRef.getPart(true);
            String title = part != null ? part.getTitle() : null;

            if ((title != null && (title.equals(pattern) || safeMatches(title, pattern)))) {
                if (f.index == null || f.index.equals(currIdx))
                    return wrap(iViewRef);
                currIdx++;
            }
        }
        return null;
    }

    private static boolean matches(String value, String pattern) {
        return pattern == null || (value != null && (value.equals(pattern) || safeMatches(value, pattern)));
    }

    private static boolean matches(Integer value, Integer pattern) {
        return pattern == null || (value != null && value.equals(pattern));
    }

    public SWTUIElement selectEditor(PlayerSelectionFilter f) {
        String title = f.pattern;
        if (title != null && title.length() == 0)
            title = null;

        String type = f.classPattern;
        if (type != null && type.length() == 0)
            type = null;

        //

        IEditorReference[] refs = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                .getEditorReferences();

        if (f.index != null && f.index < 0)
            return null;

        // -- old style

        if (title != null && type == null && f.index == null) {
            for (IEditorReference ref : refs)
                if (matches(ref.getPartName(), title) || matches(ref.getId(), title))
                    return wrap(ref);
            return null;
        }

        // -- new style

        String id = null;
        if (type != null)
            for (IEditorDescriptor desc : ((EditorRegistry) PlatformUI.getWorkbench().getEditorRegistry())
                    .getSortedEditorsFromPlugins())
                if (matches(desc.getLabel(), type))
                    id = desc.getId();

        int counter = 0;
        for (IEditorReference ref : refs)
            if (matches(ref.getPartName(), title) && matches(ref.getId(), id))
                if (matches(counter++, f.index))
                    return wrap(ref);

        return null;
    }

    /**
     * Put the caret at the given position in the styled text widget by clicking there. This also clears the current
     * text selection.
     */
    public void setTextOffset(final StyledText styledText, final int offset, final int line) {
        exec("Set text offset", new Runnable() {
            @Override
            public void run() {
                // styledText.setFocus();// forced focus (see QS-910)
                // Point selectionRange = styledText.getSelection();
                events.sendFocus(styledText);
                int actualOffset = offset;
                if (line != -1) {
                    actualOffset += styledText.getOffsetAtLine(line);
                }
                Point clickPoint = styledText.getLocationAtOffset(actualOffset);
                styledText.setCaretOffset(actualOffset);
                styledText.getAccessible().textCaretMoved(offset);
                events.sendEvent(styledText, SWT.MouseDown, clickPoint, 1);
                events.sendEvent(styledText, SWT.MouseUp, clickPoint, 1);
                styledText.setSelectionRange(actualOffset, 0);
                // styledText.setSelection(selectionRange.x,
                // selectionRange.y);
            }
        });
    }

    public void click(final SWTUIElement w) {
        click(w, false, false, false);
    }

    public void click(final SWTUIElement w, final boolean isDefault, final boolean doubleClick,
            final boolean arrow) {
        exec("click", new Runnable() {
            @Override
            public void run() {
                for (ISWTUIPlayerExtension ext : getExtensions()) {
                    if (ext.canClick(w, isDefault, doubleClick, arrow)) {
                        ext.click(w, isDefault, doubleClick, arrow);
                        return;
                    }
                }

                if (!canClick(w)) {
                    failClick(w);
                }

                IWorkbenchPage page = getTargetPage();
                switch (w.getKind().kind) {
                case View:
                    clickView(w, page);
                    break;
                case Editor:
                    clickEditor(w, page);
                    break;
                case TabItem:
                    clickTabItem(w, isDefault);
                    break;
                case Link:
                    clickLink(w, doubleClick);
                    break;
                case Item:
                    clickTableTreeItem(w, doubleClick);
                    break;
                case Label:
                    clickLabel(w);
                    break;
                default:
                    Widget widget = unwrapWidget(w);
                    if (widget.isDisposed()) {
                        break;
                    }

                    if (doubleClick) {
                        events.sendFocus(widget);
                        events.sendAll(widget, Events.createDoubleClick());
                        events.sendUnfocus(widget);
                        break;
                    }

                    if (widget instanceof MenuItem) {
                        clickMenuItem(w, isDefault, widget);
                        break;
                    }
                    final boolean isButton = widget instanceof Button;
                    final boolean isRadioButton = isButton && (widget.getStyle() & SWT.RADIO) != 0;
                    final boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32);
                    final boolean isSelectionButton = isButton && ((Button) widget).getSelection();
                    events.sendFocus(widget);
                    if (isRadioButton && (!isWin32 || !isSelectionButton)) {
                        sendEventsToRadioButtons(widget);
                    }
                    if (isButton && ((widget.getStyle() & SWT.CHECK) != 0)) {
                        Button b = (Button) widget;
                        b.setSelection(!b.getSelection());
                    }
                    if (widget instanceof ToolItem
                            && ((widget.getStyle() & SWT.CHECK) != 0 || (widget.getStyle() & SWT.RADIO) != 0)) {
                        ToolItem b = (ToolItem) widget;
                        b.setSelection(!b.getSelection());
                    }

                    Point clickPoint = getClickPoint(w);

                    events.sendEvent(w, SWT.MouseEnter);
                    events.sendEvent(w, SWT.MouseHover);
                    events.sendEvent(w, SWT.MouseDown, clickPoint, 1);

                    if (isToggleButton(widget)) {
                        ((Button) widget).setSelection(!((Button) widget).getSelection());
                    }
                    if (!isRadioButton || !isWin32 || isSelectionButton) {
                        Event event = events.createEvent(w);
                        if (arrow) {
                            event.detail = SWT.ARROW;
                        }
                        event.type = isDefault ? SWT.DefaultSelection : SWT.Selection;
                        events.sendEvent(w, event);
                    }

                    events.sendEvent(w, SWT.MouseUp, clickPoint, 1);
                    events.sendEvent(w, SWT.MouseExit);
                    events.sendUnfocus(widget);

                    break;
                }
            }

            private void sendEventsToRadioButtons(Widget widget) {
                Button button = (Button) widget;
                sendEventPreviousSelected(button);
                button.setSelection(true);
            }

            private void sendEventPreviousSelected(Button button) {
                SWTUIElement[] siblings = null;
                int parentStyle = button.getParent().getStyle();
                if ((parentStyle & SWT.NO_RADIO_GROUP) == 0) {
                    siblings = children.collectFor(wrap(button.getParent()), new SWTUIElement[] { w }, false,
                            Button.class);
                }
                if (siblings == null)
                    return;
                for (SWTUIElement element : siblings) {
                    Button previousButton = (Button) unwrap(element);
                    if ((previousButton.getStyle() & SWT.RADIO) != 0 && previousButton.getSelection()) {
                        events.sendEvent(element, SWT.Selection);
                        previousButton.setSelection(false);
                    }
                }
            }

        });
    }

    private static final Point LEGACY_CLICK_POINT = new Point(0, 0);

    private static Point getMiddleClickPoint(Control w) {
        Point size = w.getSize();
        return new Point(size.x / 2, size.y / 2);
    }

    private static Point getClickPoint(SWTUIElement element) {
        Widget widget = unwrapWidget(element);
        switch (element.getKind().kind) {
        case Tree:
            Tree tree = (Tree) widget;
            TreeItem[] selection = tree.getSelection();
            if (selection.length < 1)
                return LEGACY_CLICK_POINT;

            TreeItem item = selection[0];
            Point point = Viewers.getSafeToClickPoint(item);
            if (point == Viewers.UNSAFE_CLICK_POINT)
                return LEGACY_CLICK_POINT;
            return point;
        default:
            if (widget instanceof Control)
                return getMiddleClickPoint((Control) widget);
            return LEGACY_CLICK_POINT;
        }
    }

    private void clickMenuItem(final SWTUIElement w, final boolean isDefault, Widget widget) {
        MenuItem menuItem = (MenuItem) widget;
        hidePopupMenus(menuItem);
        // Radio MenuItem
        if ((menuItem.getStyle() & SWT.RADIO) != 0) {
            Menu parentMenu = menuItem.getParent();
            if (parentMenu != null && !parentMenu.isDisposed()
                    && (parentMenu.getStyle() & SWT.NO_RADIO_GROUP) == 0) {
                int index = 0;
                MenuItem[] items = parentMenu.getItems();

                while (index < items.length && items[index] != menuItem)
                    index++;
                int lowBound = index - 1;
                while (lowBound >= 0 && (items[lowBound].getStyle() & SWT.RADIO) != 0
                        && !items[lowBound].isDisposed()) {
                    items[lowBound].setSelection(false);
                    events.sendEvent(items[lowBound], SWT.Selection);
                    --lowBound;
                }
                int upperBound = index + 1;
                while (upperBound < items.length && (items[upperBound].getStyle() & SWT.RADIO) != 0
                        && !items[upperBound].isDisposed()) {
                    items[upperBound].setSelection(false);
                    events.sendEvent(items[upperBound], SWT.Selection);
                    ++upperBound;
                }
            }
            if (!menuItem.isDisposed()) {
                menuItem.setSelection(true);
                events.sendEvent(menuItem, SWT.Selection);
            }
        }
        // Other MenuItem
        else {
            boolean newSelection = menuItem.getSelection();
            if ((menuItem.getStyle() & SWT.CHECK) != 0) {
                newSelection = !menuItem.getSelection();
                menuItem.setSelection(newSelection);
            }

            events.sendEvent(w, isDefault ? SWT.DefaultSelection : SWT.Selection);

            if (!menuItem.isDisposed()) {
                menuItem.setSelection(newSelection);
            }
        }

        // -- simulate normal flow of events

        if (menuItem.isDisposed())
            return;
        Menu parent = menuItem.getParent();
        while (parent != null) {
            if (parent.isDisposed())
                return;
            events.sendEvent(parent, SWT.Hide);
            if (parent.isDisposed())
                return;
            parent = parent.getParentMenu();
        }
    }

    private void hidePopupMenus(MenuItem menuItem) {
        Menu parent = menuItem.getParent();
        while (parent != null) {
            List<WeakReference<Menu>> popupMenus = TeslaEventManager.getManager().getPopupMenus();
            for (WeakReference<Menu> weakReference : popupMenus) {
                Menu reffered = weakReference.get();
                if (parent.equals(reffered)) {
                    popupMenus.remove(weakReference);
                    break;
                }
            }
            parent = parent.getParentMenu();
        }
    }

    private void failClick(final SWTUIElement w) {
        setBackgroundColor(w, SWT.COLOR_RED);
        makeScreenShot();
        throw new RuntimeException(
                NLS.bind(TeslaSWTMessages.SWTUIPlayer_CannotClickOnDisabledControl, w.toString()));
    }

    private IWorkbenchPage getTargetPage() {
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        if (page == null) {
            IWorkbenchWindow window = PlatformUI.getWorkbench().getWorkbenchWindows()[0];
            page = window.getActivePage();
            if (page == null) {
                page = window.getPages()[0];
            }
        }
        return page;
    }

    private void clickView(final SWTUIElement w, IWorkbenchPage page) {
        IViewReference view = (IViewReference) (((WorkbenchUIElement) w).reference);

        IWorkbenchPart part = view.getPart(true);
        if (part == null) {
            return;
        }
        page.activate(part);
        IWorkbenchPart activePart = page.getActivePart();
        if (!part.equals(activePart)) {
            throw new RuntimeException(
                    NLS.bind(TeslaSWTMessages.SWTUIPlayer_WorkbenchPartNotActivated, w.toString()));
        }
    }

    private void clickLink(final SWTUIElement w, boolean doubleClick) {
        Widget widget = unwrapWidget(w);
        if (widget.isDisposed()) {
            return;
        }
        if (doubleClick) {
            events.sendFocus(widget);
            events.sendAll(widget, Events.createDoubleClick());
            events.sendUnfocus(widget);
        } else {
            events.sendAll(widget, new Event[] { createMouseDown(), createSelection(), createMouseUp() });
        }
    }

    private void clickEditor(final SWTUIElement w, IWorkbenchPage page) {
        IEditorReference editor = (IEditorReference) (((WorkbenchUIElement) w).reference);
        IWorkbenchPart editorPart = editor.getPart(true);
        page.bringToTop(editorPart);
        page.activate(editorPart);
    }

    private void clickTabItem(final SWTUIElement w, final boolean isDefault) {
        Item rawItem = (Item) unwrapWidget(w);
        Composite tabParent = TabCTabUtil.getParent(rawItem);
        TabCTabUtil.setSelection(tabParent, rawItem);
        Point pos = Bounds.centerAbs(TabCTabUtil.getBounds(rawItem));
        events.sendAll(tabParent, rawItem, new Event[] { Events.createMouseDown(pos),
                Events.createSelection(isDefault), Events.createMouseUp(pos) });
    }

    private void clickLabel(final SWTUIElement w) {
        Widget widget = w.unwrap();
        events.sendFocus(widget);
        for (Event event : createClick(centerRel(w.getBounds()))) {
            events.sendEvent(w, event);
        }
        events.sendUnfocus(widget);
    }

    private void clickTableTreeItem(final SWTUIElement w, final boolean doubleClick) {
        final int column = w instanceof ItemUIElement ? ((ItemUIElement) w).getColumn() : -1;
        final Widget item = (Widget) unwrap(w);
        final Widget itemParent = TableTreeUtil.getParent(item);
        Viewers.selectItem(w, false);
        final Point itemCenter = centerAbs(column >= 0 ? getItemBounds(item, column) : getItemBounds(item));
        w.getPlayer().exec("click cell", new Runnable() {
            @Override
            public void run() {
                if (doubleClick) {
                    getEvents().sendFocus(itemParent);
                    getEvents().sendAll(itemParent, Events.createDoubleClick(itemCenter));
                    getEvents().sendUnfocus(itemParent);
                } else {
                    getEvents().sendEvent(itemParent, Events.createMouseDown(itemCenter));
                    getEvents().sendEvent(itemParent, Events.createMouseUp(itemCenter));
                }
            }
        });
    }

    private static void checkCell(Item item, int column, boolean state) {
        if (item instanceof TreeItem) {
            Tree tree = ((TreeItem) item).getParent();
            TreeViewer v = (TreeViewer) TeslaSWTAccess.getViewer(tree);
            if (v == null)
                throw new RuntimeException("No tree viewer.");
            if (column < 0 || column >= tree.getColumnCount())
                throw new RuntimeException("Invalid tree column index.");
            TreeColumn c = tree.getColumn(column);
            Object data = c.getData("org.eclipse.jface.columnViewer");
            if (!(data instanceof ViewerColumn))
                throw new RuntimeException("No tree column viewer.");
            ViewerColumn cv = (ViewerColumn) data;
            EditingSupport es = TeslaSWTAccess.getField(EditingSupport.class, cv, "editingSupport");
            if (es == null)
                throw new RuntimeException("No editing support for tree column viewer.");
            Object value = TeslaSWTAccess.callMethod(EditingSupport.class, es, "getValue",
                    new Class[] { Object.class }, item.getData());
            if (!(value instanceof Boolean))
                throw new RuntimeException("Does not look like a checkbox tree column.");
            if ((Boolean) value == state)
                return;
            v.editElement(item.getData(), column);
        } else if (item instanceof TableItem) {
            Table table = ((TableItem) item).getParent();
            TableViewer v = (TableViewer) TeslaSWTAccess.getViewer(table);
            if (v == null)
                throw new RuntimeException("No table viewer.");
            if (column < 0 || column >= table.getColumnCount())
                throw new RuntimeException("Invalid table column index.");
            TableColumn c = table.getColumn(column);
            Object data = c.getData("org.eclipse.jface.columnViewer");
            if (!(data instanceof ViewerColumn))
                throw new RuntimeException("No table column viewer.");
            ViewerColumn cv = (ViewerColumn) data;
            EditingSupport es = TeslaSWTAccess.getField(EditingSupport.class, cv, "editingSupport");
            if (es == null)
                throw new RuntimeException("No editing support for table column viewer.");
            Object value = TeslaSWTAccess.callMethod(EditingSupport.class, es, "getValue",
                    new Class[] { Object.class }, item.getData());
            if (!(value instanceof Boolean))
                throw new RuntimeException("Does not look like a checkbox table column.");
            if ((Boolean) value == state)
                return;
            v.editElement(item.getData(), column);
        }
    }

    public void check(final SWTUIElement w, final boolean state) {
        Widget widget = unwrapWidget(w);
        if (!widget.isDisposed()) {
            if (w instanceof ItemUIElement && widget instanceof Item) {
                checkCell((Item) widget, ((ItemUIElement) w).getColumn(), state);
            } else if ((widget.getStyle() & SWT.CHECK) != 0) {
                if (widget instanceof Button) {
                    if (((Button) widget).getSelection() != state) {
                        click(w);
                    }
                } else if (widget instanceof MenuItem) {
                    if (((MenuItem) widget).getSelection() != state) {
                        click(w);
                    }
                } else if (widget instanceof ToolItem) {
                    if (((ToolItem) widget).getSelection() != state) {
                        click(w);
                    }
                }
            }
        }
    }

    /**
     * Wraps an UI element (e.g. SWT widget, but not only them) into a
     * convenient wrap to perform various actions on it with the wrap's methods.
     * <p>
     * The wrapping layer also serves as an unification of various kinds of UI elements.
     * <p>
     * The functionality of this method can be extended by {@link ISWTUIPlayerExtension#wrap(Object, SWTUIPlayer)}
     * method.
     *
     * @see SWTUIElement
     */
    public SWTUIElement wrap(Object s) {

        for (ISWTUIPlayerExtension ext : getExtensions()) {
            SWTUIElement result = ext.wrap(s, this);
            if (result != null) {
                return result;
            }
        }

        //

        if (s instanceof SWTUIElement) {
            return (SWTUIElement) s;
        }

        if (s instanceof IWorkbenchPart) {
            IWorkbenchPart part = (IWorkbenchPart) s;
            IWorkbenchPartSite site = part.getSite();
            if (site != null) {
                // System.out.println("Site is not null");
                IWorkbenchWindow window = site.getWorkbenchWindow();
                IWorkbenchPage page = window.getActivePage();
                if (page != null) {
                    IWorkbenchPartReference reference = page.getReference(part);
                    if (reference != null) {
                        return new WorkbenchUIElement(reference, this);
                    }
                }
                IWorkbenchPage[] pages = window.getPages();
                for (IWorkbenchPage wp : pages) {
                    IWorkbenchPartReference ref = wp.getReference(part);
                    if (ref != null) {
                        return new WorkbenchUIElement(ref, this);
                    }
                }
            } else {
                // System.out.println("Site is null");
            }
            // Obtain using initialization
            IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
            for (IWorkbenchWindow win : windows) {
                IWorkbenchPage[] pages = win.getPages();
                for (IWorkbenchPage wp : pages) {
                    IWorkbenchPartReference reference = wp.getReference(part);
                    if (reference != null) {
                        return new WorkbenchUIElement(reference, this);
                    }
                }
            }
            // System.out.println("Failed to create wrap for item:"
            // + part.getTitle());
        }
        if (s instanceof IWorkbenchPartReference) {
            return new WorkbenchUIElement((IWorkbenchPartReference) s, this);
        }
        if (s instanceof Widget) {
            return new SWTUIElement((Widget) s, this);
        }
        return null;
    }

    public final ChildrenCollector children = new ChildrenCollector(this);

    Point getMousePos(Widget c) {
        Point xy = new Point(0, 0);
        Point point = widgetToMouseForMenus.get(c);
        if (point != null) {
            xy.x = point.x;
            xy.y = point.y;
        } else {
            // Calculate correct position
            if (c instanceof TabFolder) {
                TabItem[] items = ((TabFolder) c).getSelection();
                if (items.length > 0) {
                    Rectangle bounds = items[0].getBounds();
                    return new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
                }
            } else if (c instanceof CTabFolder) {
                CTabItem selection = ((CTabFolder) c).getSelection();
                if (selection != null) {
                    Rectangle bounds = selection.getBounds();
                    xy.x = bounds.x + bounds.width / 2;
                    xy.y = bounds.y + bounds.height / 2;
                    return new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
                }
            } else if (c instanceof Table) {
                if (((Table) c).getItemCount() == 0) {
                    // Calculate center by x, and 1/10 from below
                    Rectangle bounds = ((Table) c).getBounds();
                    return new Point(bounds.width / 2, 9 * (bounds.height / 10));
                }
            } else if (c instanceof Tree) {
                if (((Tree) c).getItemCount() == 0) {
                    // Calculate center by x, and 1/10 from below
                    Rectangle bounds = ((Tree) c).getBounds();
                    return new Point(bounds.width / 2, 9 * (bounds.height / 10));
                }
            }
        }
        return xy;
    }

    public UIColor getSystemColor(int color) {
        return new SWTUIColor(display.getSystemColor(color));
    }

    public SWTUIElement selectMenu(PlayerSelectionFilter f) {
        if (f.path == null) {
            return null;
        }

        SWTUIElement currentParent = f.parent;
        if (currentParent.getKind().is(ElementKind.Item)) {
            // so that get-menu can be invoked in tree and table items
            Widget unwrapped = unwrapWidget(currentParent);
            if (unwrapped instanceof TreeItem || unwrapped instanceof TableItem) {
                currentParent = getParentElement(currentParent);
            }
        } else if (currentParent.getKind().is(ElementKind.Button)) {
            // support button drop-downs that are not children of Button itself

            while (true) {
                SWTUIElement[] children = this.children.collectFor(currentParent, ignoreWindows, false,
                        one(MenuItem.class), f.after);
                if (children.length > 0)
                    break;

                currentParent = getParentElement(currentParent);
                if (currentParent == null) {
                    currentParent = f.parent;
                    break;
                }
            }

        }

        for (String part : f.path) {
            SWTUIElement[] children = this.children.collectFor(currentParent, ignoreWindows, false,
                    one(MenuItem.class), f.after);
            boolean found = false;
            for (SWTUIElement uiElement : children) {
                String elementName = getText(uiElement);
                if (elementName == null) {
                    continue;
                }
                elementName = getMenuText(elementName);
                if (elementName != null && (elementName.equals(part) || safeMatches(elementName, part))) {
                    // -- simulate normal flow of events

                    // disabled since probably it is not the best place to do
                    // such things, needs discussion

                    /*
                     * MenuItem menuItem = (MenuItem) unwrapWidget(uiElement);
                     * Menu menu = menuItem.getMenu();
                     * events.sendEvent(uiElement, SWT.Arm); if (menu != null) {
                     * events.sendEvent(menu, SWT.Show);
                     * menuItem.setSelection(true); events.sendEvent(menuItem,
                     * SWT.Selection); }
                     */
                    // --

                    found = true;
                    currentParent = uiElement;
                    break;
                }
            }
            if (!found) {
                return null;
            }
        }
        return currentParent;
    }

    public UIColor getBackgroundColor(SWTUIElement uiElement) {

        final Widget widget = unwrapWidget(uiElement);
        if (widget instanceof Control) {
            return new SWTUIColor(((Control) widget).getBackground());

        }
        return null;
    }

    public int countItems(SWTUIElement element, String[] selection) {
        final Object widget = unwrap(element);
        if (widget instanceof Tree) {
            return Viewers.countTreeItemChildren(element, selection);
        } else if (widget instanceof Table) {
            return ((Table) widget).getItemCount();
        } else if (widget instanceof org.eclipse.swt.widgets.List) {
            return ((org.eclipse.swt.widgets.List) widget).getItemCount();
        } else if (widget instanceof TabFolder) {
            return ((TabFolder) widget).getItemCount();
        } else if (widget instanceof CTabFolder) {
            return ((CTabFolder) widget).getItemCount();
        }
        // TODO other controls?
        return 0;
    }

    public void setBackgroundColor(final SWTUIElement uiElement, final int color) {

        setBackgroundColor(uiElement, new SWTUIColor(display.getSystemColor(color)));
    }

    public void setBackgroundColor(SWTUIElement uiElement, final UIColor color) {
        final Widget widget = unwrapWidget(uiElement);
        if (widget instanceof Control && !widget.isDisposed()) {
            exec("setBackground", new Runnable() {
                @Override
                public void run() {
                    if (!widget.isDisposed()) {
                        ((Control) widget).setBackground(((SWTUIColor) color).getColor());
                    }
                }
            });
        }
    }

    public void setDateTime(final SWTUIElement uiElement, final int year, final int month, final int day,
            final int hours, final int minutes, final int second) {
        final Widget widget = unwrapWidget(uiElement);
        exec("setDateTime", new Runnable() {
            @Override
            public void run() {
                if (widget.isDisposed()) {
                    return;
                }
                if (widget instanceof DateTime) {
                    DateTime dt = (DateTime) widget;
                    events.sendFocus(widget);
                    dt.setDate(year, month - 1, day);
                    dt.setTime(hours, minutes, second);
                    events.sendEvent(uiElement, SWT.Selection);
                    events.sendUnfocus(widget);
                }
            }
        });
    }

    public void setText(final SWTUIElement uiElement, final String text) {
        setText(uiElement, text, false);
    }

    public static final int COMBO_ITEM_NOT_FOUND = -1;

    public static int findComboItem(String[] items, String item, boolean select) {
        for (int i = 0; i < items.length; ++i)
            if (items[i].equals(item))
                return i;

        if (!select)
            return COMBO_ITEM_NOT_FOUND;

        for (int i = 0; i < items.length; ++i)
            if (safeMatches(items[i], item))
                return i;

        item = item.toLowerCase();
        String[] lowerItems = new String[items.length];
        for (int i = 0; i < items.length; ++i)
            lowerItems[i] = items[i].toLowerCase();

        for (int i = 0; i < items.length; ++i)
            if (lowerItems[i].equals(item))
                return i;

        for (int i = 0; i < items.length; ++i)
            if (safeMatches(lowerItems[i], item))
                return i;

        return COMBO_ITEM_NOT_FOUND;
    }

    public void setText(final SWTUIElement uiElement, final String text, final boolean select) {
        final Widget widget = unwrapWidget(uiElement);
        exec("setText", new Runnable() {
            @Override
            public void run() {
                if (widget.isDisposed()) {
                    return;
                }
                if (widget instanceof Text) {
                    Text textElement = (Text) widget;
                    events.sendFocus(widget);
                    textElement.setText(text);
                    events.sendEvent(uiElement, SWT.Verify);
                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendUnfocus(widget);
                }
                if (widget instanceof Spinner) {
                    events.sendFocus(widget);
                    int Val = (int) (Double.parseDouble(text) * Math.pow(10, ((Spinner) widget).getDigits()));
                    ((Spinner) widget).setSelection(Val);
                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendUnfocus(widget);
                }
                if (widget instanceof Slider) {
                    events.sendFocus(widget);
                    int Val = Integer.parseInt(text);
                    ((Slider) widget).setSelection(Val);
                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendEvent(uiElement, SWT.Selection);
                    events.sendUnfocus(widget);
                }
                if (widget instanceof StyledText) {
                    events.sendFocus(widget);
                    ((StyledText) widget).setText(text);
                    if (widget instanceof StyledText) {
                        StyledText tt = (StyledText) widget;
                        int offset = tt.getCaretOffset();
                        String allText = tt.getText();
                        tt.setText(allText);
                        tt.setCaretOffset(offset);
                    }
                    events.sendUnfocus(widget);
                }
                if (widget instanceof Combo) {
                    Combo combo = (Combo) widget;
                    events.sendFocus(widget);

                    int itemIndex = findComboItem(combo.getItems(), text, select);
                    if (!select)
                        combo.setText(text);
                    if (itemIndex != COMBO_ITEM_NOT_FOUND)
                        combo.select(itemIndex);

                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendEvent(uiElement, SWT.Selection);
                    events.sendUnfocus(widget);
                }
                if (widget instanceof CCombo) {
                    CCombo combo = (CCombo) widget;
                    events.sendFocus(widget);

                    int itemIndex = findComboItem(combo.getItems(), text, select);
                    if (!select)
                        combo.setText(text);
                    if (itemIndex != COMBO_ITEM_NOT_FOUND)
                        combo.select(itemIndex);

                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendEvent(uiElement, SWT.Selection);
                    events.sendUnfocus(widget);
                }
                if (widget instanceof Browser) {
                    Browser browser = (Browser) widget;
                    events.sendFocus(widget);
                    browser.setText(text);
                    events.sendEvent(uiElement, SWT.Verify);
                    events.sendEvent(uiElement, SWT.Modify);
                    events.sendUnfocus(widget);
                }
            }

        });
    }

    protected boolean checkContainsControl(Control control, Combo combo) {
        return false;
    }

    public void show(SWTUIElement uiElement, int x, int y) {
        Menu menu = (Menu) uiElement.unwrap();
        Point pos = new Point(x, y);
        if (pos.x == -1 && pos.y == -1) {
            pos = getMousePos(menu);
        }

        if ((menu.getStyle() & SWT.BAR) == 0) { // Not a menu bar
            shownMenus.add(new WeakReference<Menu>(menu));
        }

        events.sendEvent(uiElement, SWT.Show, pos.x, pos.y, 0);
    }

    public boolean isDisposed(SWTUIElement uiElement) {
        Widget widget = unwrapWidget(uiElement);
        return widget == null || widget.isDisposed();
    }

    protected static void checkIntegrity(Class<?>[] classes) {
        for (int i = 0; i < classes.length; i++) {
            for (int j = i + 1; j < classes.length; j++) {
                Class<?> ci = classes[i];
                Class<?> cj = classes[j];
                if (ci.isAssignableFrom(cj)) {
                    System.out.println(String.format("Achtung! %s should be after %s", ci.getName(), cj.getName()));
                }

            }
        }
    }

    public static Class<?> getSearchableClass(Object widget) {
        ElementKind kind = elementKinds.get(widget.getClass());
        if (kind == null) {
            // Try to find superclass for custom widget in extensions
            for (ISWTUIPlayerExtension extension : getExtensions()) {
                Class<?> searchableClass = extension.getSearchableClass(widget);
                if (searchableClass != null) {
                    return searchableClass;
                }
            }
            // Try to find superclass for custom widget in "default" elements
            for (Map.Entry<Class<?>, ElementKind> entry : elementKinds.entrySet()) {
                Class<?> key = entry.getKey();
                if (key.isInstance(widget)) {
                    return key;
                }
            }
        }
        return widget.getClass();
    }

    public static GenericElementKind getKind(Object w) {
        if (w == null)
            return GenericElementKind.Unknown;

        for (ISWTUIPlayerExtension extension : getExtensions()) {
            GenericElementKind kind = extension.getKind(w);
            if (kind != null) {
                return kind;
            }
        }

        ElementKind kind = elementKinds.get(w.getClass());
        if (kind == null) {
            for (Map.Entry<Class<?>, ElementKind> entry : elementKinds.entrySet()) {
                Class<?> key = entry.getKey();
                if (key.isInstance(w)) {
                    return new GenericElementKind(entry.getValue());
                }
            }
        }
        if (kind != null) {
            return new GenericElementKind(kind);
        }
        return GenericElementKind.Unknown;
    }

    public void close(final SWTUIElement uiElement) {
        exec("close", new Runnable() {
            @Override
            public void run() {
                if (uiElement instanceof WorkbenchUIElement) {
                    IWorkbenchPartReference reference = ((WorkbenchUIElement) uiElement).getReference();
                    if (reference == null) {
                        return;
                    }
                    IWorkbenchPart part = reference.getPart(false);
                    if (part != null) {
                        IWorkbenchPage page = part.getSite().getPage();
                        if (part instanceof IEditorPart) {
                            page.closeEditor((IEditorPart) part, true);
                        } else if (part instanceof IViewPart) {
                            IViewPart vp = (IViewPart) part;
                            page.hideView(vp);
                            // hideView already call dispose for ViewPart
                            // vp.dispose();
                        }
                    }
                } else {
                    Widget widget = unwrapWidget(uiElement);
                    if (widget instanceof CTabItem) {
                        CTabItem item = (CTabItem) widget;
                        Rectangle rect = TeslaSWTAccess.getCTabItemCloseRect(item);
                        if (rect != null && rect.width > 0 && rect.height > 0) {
                            events.sendEvent(item.getParent(), SWT.MouseDown, rect.x + 1, +rect.y + 1, 1);
                            events.sendEvent(item.getParent(), SWT.MouseUp, rect.x + 1, rect.y + 1, 1);
                        }

                    } else if (widget instanceof Shell) {
                        getEvents().sendEvent(widget, SWT.Deactivate);
                        ((Shell) widget).close();
                    } else {
                        Event e = events.sendEvent(uiElement, SWT.Close);
                        if (e != null && e.doit) {
                            if (widget != null && !widget.isDisposed()) {
                                widget.dispose();
                            }
                        }
                    }
                }
            }
        });
    }

    public List<File> getScreenshots() {
        if (screenshotsDuringSession != null) {
            return new ArrayList<File>(screenshotsDuringSession);
        }
        return new ArrayList<File>();
    }

    /**
     * NOTE: check that used widgets are not disposed. See {@link Display#asyncExec(Runnable)} for details.
     */
    public void exec(final String msg, final Runnable runnable) {
        final Exception e = new Exception();
        StackTraceElement stackTraceElement = e.getStackTrace()[1];
        final String errorMethod = stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName() + ":"
                + stackTraceElement.getLineNumber();

        final Context currentContext = context;
        Runnable myr = new ExecRunnable(msg, currentContext, runnable, errorMethod);
        if (context != null) {
            synchronized (runnables) {
                List<Runnable> runs = runnables.get(context);
                if (runs == null) {
                    runs = new ArrayList<Runnable>();
                    runnables.put(context, runs);
                }
                runs.add(myr);
            }
        }
        display.asyncExec(myr);
        display.wake();
        // runnable.run();
    }

    public boolean canProceed(Context context, Q7WaitInfoRoot info) {
        boolean result = true;
        if (!display.equals(Display.getCurrent())) {
            // Q7WaitUtils.updateInfo("display", "non current", info);
            result = false;
        }
        // Return false if we have SWT observable in timers
        if (hasTimers(display, info)) {
            result = false;
        }
        // Check for asyncs in synchronizer
        if (!TeslaEventManager.getManager().isNoWaitForJob() && hasRunnables(display)) {
            // Q7WaitUtils.updateInfo("display", "runnables", info);
            result = false;
        }
        if (!BrowserManager.getInstance().isExecutionAllowed(info)) {
            result = false;
        }
        // Check we don't have decoration object to perform
        if (isHasDecorations(info)) {
            result = false;
        }

        synchronized (runnables) {
            this.context = context;
            List<Runnable> runs = runnables.get(context);
            if ((runs == null || runs.isEmpty())) {
                // Put collector in need disable state, since this method could
                // be only
                // called from sleeping state
                if (TeslaEventManager.getManager().isNoWaitForJob() || collector.isEmpty(context, info)) {
                    // collector.setNeedDisable();
                    if (result) {
                        return true;
                    }
                }
            }
            result = false;
        }
        return result;
    }

    private boolean isHasDecorations(Q7WaitInfoRoot info) {
        DecoratorManager manager = WorkbenchPlugin.getDefault().getDecoratorManager();
        DecorationScheduler scheduler = TeslaSWTAccess.getDecorationScheduler(manager);
        Job[] decorstors = Job.getJobManager().find(DecoratorManager.FAMILY_DECORATE);
        if (decorstors.length != 0) {
            for (Job job : decorstors) {
                Q7WaitUtils.updateInfo("decorator", job.getClass().getName(), info);
            }
            return true;
        }
        return scheduler.processingUpdates() && TeslaSWTAccess.getDecorationResultMap(scheduler).isEmpty();
    }

    public static boolean hasRunnables(Display display) {
        Synchronizer synchronizer = display.getSynchronizer();
        if (TeslaSWTAccess.getRunnables(synchronizer) > 0) {
            return true;
        }
        return false;
    }

    private static long lastTimersSucess = 0;
    private static long lastTimeSucessStep = 0;

    public static boolean hasTimers(Display display, Q7WaitInfoRoot infoRoot) {
        Runnable[] timers = TeslaSWTAccess.getTimers(display);
        List<TimerInfo> map = TeslaTimerExecManager.getManager().getTimers();

        List<TimerInfo> waitFor = new ArrayList<TimerInfo>();
        for (Runnable runnable : timers) {
            if (runnable instanceof SherlockTimerRunnable) {
                runnable = ((SherlockTimerRunnable) runnable).getRunnable();
            }
            TimerInfo current = null;
            for (TimerInfo t : map) {
                if (t.hasRunnable(runnable)) {
                    current = t;
                    break;
                }
            }
            if (current == null) {
                continue;
            }

            Class<? extends Runnable> cl = runnable.getClass();
            String clName = cl.getName();

            // Return true if some of delayed observables are pressent.
            if (clName.startsWith("org.eclipse.core.internal.databinding.observable.DelayedObservableValue")) {
                waitFor.add(current);
                break;
            }

            if (clName.contains("org.eclipse.jface")) { // Wait
                /*
                 * for all jface timer execs
                 */
                waitFor.add(current);
                break;
            }
            if (isTimerIgnored(clName)) {
                continue;
            }

            if (TeslaLimits.getTimerExecsWait() > 0) {
                // Check if runnable in timers list and timeout is less then job
                // timeout
                boolean add = false;
                for (TimerInfo info : map) {
                    if (info.hasRunnable(runnable)) {
                        if (info.time < TeslaLimits.getTimerExecsWait()) {
                            if ((System.currentTimeMillis() - info.firstSheduleTime) > TeslaLimits
                                    .getTimerExecsSkip() && info.resheduleCounter > 0) {
                                // Skip this runnable, since it rescheduling
                                // itself to much.
                                // System.out.println("Skip TimerExec:" +
                                // info.getRunnable().getClass().getName());
                                printTimers(map);
                                continue;
                            }
                            add = true;
                            break;
                        }
                    }
                }
                if (add) {
                    waitFor.add(current);
                }
            }
        }
        if (waitFor.size() > 0 && lastTimersSucess != 0) {
            if ((System.currentTimeMillis() - lastTimersSucess) > TeslaLimits.getTimerExecsTotalWaitTime()) {
                // Enable timers step mode.
                if (lastTimeSucessStep == 0) {
                    lastTimeSucessStep = System.currentTimeMillis();
                    printTimers(waitFor);
                    return false; // Do a step mode.
                } else {
                    if ((System.currentTimeMillis() - lastTimeSucessStep) > TeslaLimits
                            .getTimerExecsTotalWaitTimeStep()) {
                        lastTimeSucessStep = System.currentTimeMillis();
                        printTimers(waitFor);
                        return false;// Do a step mode.
                    }
                }
            }
        }
        if (waitFor.size() > 0) {
            for (TimerInfo timerInfo : waitFor) {
                String timerClassName = timerInfo.getRunnable().getClass().getName();
                synchronized (infoRoot) {
                    if (timerInfo.execQualifier != null) {
                        infoRoot.getInnerClassMap().put(timerClassName, timerInfo.execQualifier);
                    }
                }
                Q7WaitUtils.updateInfo("timer", timerClassName, infoRoot);
            }
            return true;
        }

        lastTimersSucess = System.currentTimeMillis();
        lastTimeSucessStep = 0;
        return false;
    }

    public static boolean isTimerIgnored(String clName) {
        return TeslaTimerExecManager.isTimerIgnored(clName);
    }

    private static void printTimers(List<TimerInfo> map) {
        // StringBuilder bb = new StringBuilder();
        // bb.append("\n<<<<<<---- Timer execs:\n");
        // for (TimerInfo timer : map) {
        // bb.append(timer.getRunnable().getClass().getName())
        // .append(" delay: ")
        // .append(timer.time)
        // .append(" scheduled for time: " + (System.currentTimeMillis() -
        // timer.firstSheduleTime)
        // + " reshedules: " + timer.resheduleCounter)
        // .append("\n");
        // }
        // String msg =
        // "---->>> TimerExec Waiting timeout exceed then execute: "
        // + ReportManager.getBuilder().getCurrentNode()
        // .getName() + bb.toString() + " <<---\n(stepping)";
        // System.err.println(msg);
        // SWTTeslaActivator
        // .log(msg);
    }

    public static String toSelectionItem(String toPattern) {
        if (toPattern != null) {
            toPattern = toPattern.replaceAll("\\+", "\\\\+");
            toPattern = toPattern.replaceAll("\\(", "\\\\(");
            toPattern = toPattern.replaceAll("\\)", "\\\\)");
            toPattern = toPattern.replaceAll("\\[", "\\\\[");
            toPattern = toPattern.replaceAll("\\]", "\\\\]");
            toPattern = toPattern.replaceAll("\\*", "\\\\*");
        }
        return toPattern;
    }

    public static String buildPathFragment(String text, int index) {
        return String.format("%s%%%d%%", toSelectionItem(text).replaceAll("/", "\\\\/"), index);
    }

    public static String buildPathFragment(String text) {
        return toSelectionItem(text).replaceAll("/", "\\\\/");
    }

    public void setIgnores(Shell... ignoreWindows) {
        if (ignoreWindows != null) {
            this.ignoreWindows = new SWTUIElement[ignoreWindows.length];
            this.ignoredShells = ignoreWindows;
            int i = 0;
            for (Shell shell : ignoreWindows) {
                this.ignoreWindows[i++] = wrap(shell);
            }
        }
    }

    public void typeText(final SWTUIElement element, final String text, final int mask, final boolean fromDisplay) {
        exec("typeText", new Runnable() {
            @Override
            public void run() {
                switch (element.getKind().kind) {
                default:
                    Widget widget = unwrapWidget(element);
                    if (!(widget instanceof Control))
                        break;
                    Control ctrl = (Control) widget;
                    if (fromDisplay) {
                        // Necessary for setting focus to control
                        try {
                            ShellUtilsProvider.getShellUtils().forceActive(ctrl.getShell());
                        } catch (CoreException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    events.sendFocus(widget);
                    if (ctrl instanceof CCombo) {
                        CCombo combo = (CCombo) widget;
                        Text tW = getCComboText(combo);
                        events.sendFocus(tW);
                        tW.insert(text);
                        events.sendEvent(wrap(tW), SWT.Modify);
                        events.sendEvent(wrap(tW), SWT.Deactivate);
                    } else if (ctrl instanceof Text) {
                        Text tW = (Text) ctrl;
                        events.sendFocus(tW);
                        tW.insert(text);
                        type(element, 0, SWT.SHIFT, false, '\u0000', 1); // fake
                        events.sendEvent(wrap(tW), SWT.Modify);
                        events.sendEvent(wrap(tW), SWT.Deactivate);
                    } else {
                        sendKeysToWidget(text, mask, fromDisplay, widget);
                        events.sendEvent(wrap(widget), SWT.Deactivate);
                    }
                    break;
                }
            }

            private void sendKeysToWidget(final String text, final int mask, final boolean fromDisplay,
                    Widget widget) {
                SWTUIElement element = wrap(widget);
                if (!widget.isDisposed()) {
                    for (char ch : text.toCharArray()) {
                        updateStyledTextPos(widget);
                        List<KeyStroke> keys = keyboard.splitToKeys(KeyStroke.getInstance(mask, KeyStroke.NO_KEY));
                        if (fromDisplay) {
                            keys.add(SWTKeyboardLayout.getKeyboardLayout().keyStrokeFor(ch));
                        } else {
                            if (Character.isUpperCase(ch)) {
                                ch = Character.toLowerCase(ch);
                                keys.add(KeyStroke.getInstance(SWT.SHIFT, ch));
                            } else
                                keys.add(KeyStroke.getInstance(ch));
                        }
                        keyboard.typeKeys(widget, fromDisplay, keys.toArray(new KeyStroke[0]));

                        // For StyledText, modifyContent method will be called
                        // after MouseDown event and send correct
                        // Verify and Modify events
                        if (widget instanceof Text || widget instanceof Combo || widget instanceof CCombo) {
                            String textValue = getRawText(element);
                            Event ee = events.createEvent(element);
                            ee.start = 0;
                            ee.end = textValue.length();
                            ee.text = textValue;
                            ee.type = SWT.Verify;
                            events.sendEvent(element, ee);
                            events.sendEvent(element, SWT.Modify);
                        }
                    }
                }
            }
        });
    }

    public void traverse(final SWTUIElement element, final int code, final char character, final int times) {
        exec("traverse", new Runnable() {
            @Override
            public void run() {
                Widget widget = unwrapWidget(element);
                if (!(widget instanceof Control))
                    return;

                for (int i = 0; i < times; i++) {
                    Control control = (Control) widget;
                    boolean traverseResult = false;
                    boolean isShellProcessed = false;
                    // send traverse event to control and ancestors up to Shell
                    // while the traversal succeeded
                    while (!traverseResult && !isShellProcessed && control != null) {
                        if (!control.isDisposed()) {
                            Event ee = new Event();
                            if (code == SWT.TRAVERSE_TAB_NEXT) {
                                ee.keyCode = SWT.TAB;
                                ee.stateMask = 0;
                            } else if (code == SWT.TRAVERSE_TAB_PREVIOUS) {
                                ee.keyCode = SWT.TAB;
                                ee.stateMask = SWT.SHIFT;
                            }
                            ee.widget = control;
                            ee.time = (int) System.currentTimeMillis();
                            ee.doit = true;
                            ee.detail = code;

                            try {
                                traverseResult = control.traverse(code, ee);
                            } catch (Exception e) {
                                // In case of invalid method
                                try {
                                    Method method = Control.class.getDeclaredMethod("traverse", Event.class);
                                    method.setAccessible(true);
                                    method.invoke(control, ee);
                                } catch (Exception e1) {
                                    traverseResult = control.traverse(code);
                                }
                            }
                            // method traverse(int, Event) since eclipse 3.6
                            // traverseResult = control.traverse(code,
                            // traverseEvent);
                        }
                        isShellProcessed = control instanceof Shell;
                        if (!control.isDisposed()) {
                            control = control.getParent();
                        } else {
                            control = null;
                        }
                    }
                }
            }
        });
    }

    // * The field is set from
    // org.eclipse.rcptt.tesla.workbench.aspects.WorkbenchAspect.
    //
    // * The field located here to break dependency loop
    // with org.eclipse.rcptt.tesla.workbench.aspects.
    //
    // * There is only one Workbench per Eclipse instance,
    // so for now that is pretty OK to have static field.
    public static volatile Boolean lastWorkbenchKeyboardPressResult;

    public void type(final SWTUIElement element, final int code, final int mask, final boolean fromDisplay,
            final char character, final int times) {
        exec("type", new Runnable() {
            @Override
            public void run() {
                switch (element.getKind().kind) {
                default:
                    Widget widget = unwrapWidget(element);
                    if (!(widget instanceof Control))
                        break;
                    if (fromDisplay) {
                        // Necessary for setting focus to control
                        try {
                            ShellUtilsProvider.getShellUtils().forceActive(((Control) widget).getShell());
                        } catch (CoreException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    events.sendFocus(widget);

                    lastWorkbenchKeyboardPressResult = null;
                    boolean doModify = false;

                    if (widget instanceof CCombo) {
                        CCombo combo = (CCombo) widget;
                        Text text = getCComboText(combo);
                        for (int i = 0; i < times; i++) {
                            sendKeyEvent(code, mask, fromDisplay, character, text);
                        }
                    } else {
                        for (int i = 0; i < times; i++) {
                            sendKeyEvent(code, mask, fromDisplay, character, widget);
                            doModify |= lastWorkbenchKeyboardPressResult != null
                                    && !lastWorkbenchKeyboardPressResult;
                        }
                    }
                    if (widget instanceof Text) {
                        if (doModify)
                            events.sendEvent(element, SWT.Modify);

                        if (code == 13 && mask == 0)
                            events.sendEvent(element, SWT.DefaultSelection);
                    }
                    // events.sendEvent(element, SWT.Deactivate);
                    // events.sendEvent(element, SWT.FocusOut);
                    break;
                }
            }

            private void sendKeyEvent(final int code, final int mask, final boolean fromDisplay,
                    final char character, Widget widget) {

                if (widget.isDisposed())
                    return;

                if (fromDisplay) {
                    KeyStroke[] keys = new KeyStroke[] { KeyStroke.getInstance(mask, code) };
                    keyboard.typeKeys(widget, fromDisplay, keys);
                } else {
                    Event createEvent = keyboard.createEvent(code, mask, character);
                    createEvent.widget = widget;
                    updateStyledTextPos(widget);
                    events.sendEventRaw(SWT.KeyDown, createEvent);
                    if (widget.isDisposed())
                        return;

                    events.sendEventRaw(SWT.KeyUp, createEvent);
                }
            }
        });
    }

    public void typeAction(final SWTUIElement element, final String actionId) {
        exec("typeAction", new Runnable() {
            @Override
            public void run() {
                @SuppressWarnings("cast") // IServiceLocator.getService was not generic in Eclipse 4.4 and older.
                IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench()
                        .getService(IHandlerService.class);
                try {
                    handlerService.executeCommand(actionId, null);
                } catch (Exception e) {
                    TeslaCore.log(e);
                }
            }
        });
    }

    protected Text getCComboText(CCombo combo) {
        return TeslaSWTAccess.getCComboText(combo);
    }

    public void clickAndWait(SWTUIElement w) {
        click(w);
    }

    private void shutdown() {
        Job.getJobManager().removeJobChangeListener(collector);
        BrowserManager.getInstance().clear();
        TeslaTimerExecManager.getManager().removeEventListener(timerListener);
    }

    public void waitFinish() {
        // TODO Auto-generated method stub
    }

    public void save(final SWTUIElement w) {
        exec("save", new Runnable() {

            @Override
            public void run() {
                if (w.getKind().kind == ElementKind.Editor) {
                    IEditorReference editor = (IEditorReference) (((WorkbenchUIElement) w).reference);
                    IEditorPart editorPart = editor.getEditor(false);
                    if (editorPart != null) {
                        editorPart.doSave(new NullProgressMonitor());
                    }
                }

            }
        });

    }

    public boolean isDirty(final SWTUIElement w) {
        if (w.getKind().kind == ElementKind.Editor) {
            IEditorReference editor = (IEditorReference) (((WorkbenchUIElement) w).reference);
            return editor.isDirty();
        }
        return false;
    }

    private static Widget parentFromExtension(Widget current) {
        for (ISWTUIPlayerExtension ext : getExtensions()) {
            Widget result = ext.getIndirectParent(current);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public static List<Widget> collectParents(Widget widget, Map<Control, SWTUIElement> references,
            Widget... stopAt) {
        List<Widget> parents = new ArrayList<Widget>();
        if (widget == null || widget.isDisposed()) {
            return parents;
        }
        Widget current = widget;
        Widget prev = null;
        while (current != null) {
            prev = current;
            Widget indirectParent = parentFromExtension(current);
            if (indirectParent != null) {
                current = indirectParent;
            } else if (current instanceof Control) {
                if (current instanceof ToolBar) {
                    /*
                     * Check work view/editor toolbars, they have different
                     * parent
                     */
                    if (references != null && references.containsKey(current)) {
                        current = references.get(current).unwrap();
                    } else {
                        current = ((Control) current).getParent();
                    }
                } else {
                    current = ((Control) current).getParent();
                }
            } else if (current instanceof TreeItem) {
                current = ((TreeItem) current).getParent();
            } else if (current instanceof TableItem) {
                current = ((TableItem) current).getParent();
            } else if (current instanceof MenuItem) {
                current = ((MenuItem) current).getParent();
            } else if (current instanceof ToolItem) {
                current = ((ToolItem) current).getParent();
            } else if (current instanceof Menu) {
                Menu parentMenu = ((Menu) current).getParentMenu();
                if (parentMenu == null) {
                    current = ((Menu) current).getParent();
                } else {
                    current = parentMenu;
                }
            } else if (current instanceof ScrollBar) {
                current = ((ScrollBar) current).getParent();
            } else if (current instanceof ToolTip) {
                current = ((ToolTip) current).getParent();
            } else {
                current = null;
            }
            if (current != null) {
                if (current instanceof CoolBar) {
                    CoolBar bar = (CoolBar) current;
                    CoolItem[] items = bar.getItems();
                    for (CoolItem coolItem : items) {
                        if (coolItem != null) {
                            Control control = coolItem.getControl();
                            if (control != null && control.equals(prev)) {
                                parents.add(coolItem);
                            }
                        }
                    }
                }
                if (stopAt != null) {
                    boolean breakRequired = false;
                    for (Widget w : stopAt) {
                        if (w.equals(current)) {
                            breakRequired = true;
                            break;
                        }
                    }
                    if (breakRequired) {
                        break;
                    }
                }
                parents.add(current);
            }
        }
        return parents;
    }

    public String getSelectedTabItem(SWTUIElement swtuiElement) {
        Widget w = unwrapWidget(swtuiElement);
        if (w instanceof CTabFolder) {
            CTabFolder tf = (CTabFolder) w;
            CTabItem item = tf.getSelection();
            return item.getText();
        } else if (w instanceof TabFolder) {
            TabFolder tf = (TabFolder) w;
            TabItem[] selection = tf.getSelection();
            if (selection.length > 0) {
                return selection[0].getText();
            }
        }
        return null;
    }

    public static boolean isVisible(SWTUIElement swtuiElement) {
        Widget widget = unwrapWidget(swtuiElement);
        if (widget instanceof Control) {
            return ((Control) widget).isVisible();
        }
        return true;
    }

    public static SWTUIElement getShell(SWTUIElement element) {

        for (ISWTUIPlayerExtension ext : getExtensions()) {
            SWTUIElement result = ext.getShell(element);
            if (result != null) {
                return result;
            }
        }

        //

        Widget widget = unwrapWidget(element);

        // PartPane.control == null after disposal in case of WorkbenchUIElement
        if (widget == null || widget.isDisposed()) {
            return null;
        }

        SWTUIPlayer player = element.getPlayer();
        if (widget instanceof Control) {
            return player.wrap(((Control) widget).getShell());
        } else if (widget instanceof MenuItem) {
            return player.wrap(((MenuItem) widget).getParent().getShell());
        } else if (widget instanceof ToolItem) {
            return player.wrap(((ToolItem) widget).getParent().getShell());
        } else if (widget instanceof TreeItem) {
            return player.wrap(((TreeItem) widget).getParent().getShell());
        } else if (widget instanceof TableItem) {
            return player.wrap(((TableItem) widget).getParent().getShell());
        } else if (widget instanceof CTabItem) {
            return player.wrap(((CTabItem) widget).getParent().getShell());
        } else if (widget instanceof CoolItem) {
            return player.wrap(((CoolItem) widget).getParent().getShell());
        } else if (widget instanceof TabItem) {
            return player.wrap(((TabItem) widget).getParent().getShell());
        } else if (widget instanceof TreeColumn) {
            return player.wrap(((TreeColumn) widget).getParent().getShell());
        } else if (widget instanceof TableColumn) {
            return player.wrap(((TableColumn) widget).getParent().getShell());
        } else if (widget instanceof Item) {
            return player.wrap(((Item) widget).getDisplay().getActiveShell());
        } else if (widget instanceof ScrollBar) {
            return player.wrap(((ScrollBar) widget).getParent().getShell());
        }
        return null;
    }

    public void addMouseWidgetInfo(final Widget canvas, int x, int y) {
        TeslaEventManager.getManager().setLastWidget(canvas, x, y);
        widgetToMouseForMenus.put(canvas, new Point(x, y));
        canvas.addDisposeListener(new DisposeListener() {
            @Override
            public void widgetDisposed(DisposeEvent e) {
                widgetToMouseForMenus.remove(canvas);
                try {
                    canvas.removeDisposeListener(this);
                } catch (Throwable e2) {
                }
            }
        });
    }

    public SWTEvents getEvents() {
        return events;
    }

    public List<SWTUIElement> getParentsList(SWTUIElement swtuiElement) {
        Map<Control, SWTUIElement> references = EclipseWorkbenchProvider.getProvider().getWorkbenchReference(this);
        List<Widget> parents = collectParents(unwrapWidget(swtuiElement), references);
        List<SWTUIElement> elements = new ArrayList<SWTUIElement>();
        for (Widget widget : parents) {
            SWTUIElement e = null;
            if (references.containsKey(widget)) {
                e = references.get(widget);
            } else {
                e = wrap(widget);
            }
            if (e != null) {
                GenericElementKind kind = e.getKind();
                if (kind.is(ElementKind.Any) || kind.is(ElementKind.Unknown) || kind.is(ElementKind.Toolbar)
                        || kind.is(ElementKind.CoolBar) || kind.is(ElementKind.CBanner)
                        || kind.is(ElementKind.TabFolder) || kind.is(ElementKind.Canvas)
                        || kind.is(ElementKind.Combo)) {
                    continue;
                }
                if (isVisible(e))
                    elements.add(e);
            }
        }
        return elements;
    }

    public UIJobCollector getCollector() {
        return collector;
    }

    public SWTUIElement getParentElement(SWTUIElement uiElement) {
        List<SWTUIElement> parentsList = getParentsList(uiElement);
        if (parentsList.size() > 0) {
            return parentsList.get(0);
        }
        return null;
    }

    public BrowserManager getBrowserManager() {
        return BrowserManager.getInstance();
    }

    private void updateStyledTextPos(Widget widget) {
        if (widget instanceof StyledText) {
            // Update viewers based on textViewer about styled
            // text position. So popups will not be hided on
            // update
            StyledText t = (StyledText) widget;
            int topPixel = t.getTopPixel();
            Viewer viewer = TeslaSWTAccess.getViewer(t);
            if (viewer != null) {
                JFaceTextSupport.setLastPixels(viewer, topPixel);
            }
        }
    }

    public static byte[] captureControlImage(Control ctrl, Rectangle subImageBounds) {
        // selectionShell.setVisible(false);
        // Rectangle selBounds = selectionShell.getBounds();
        Image image = captureControlImageRaw(ctrl, subImageBounds);
        ImageData data = image.getImageData();
        ImageLoader loader = new ImageLoader();
        loader.data = new ImageData[] { data };
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        loader.save(stream, SWT.IMAGE_PNG);
        image.dispose();
        return stream.toByteArray();
    }

    public static Image captureControlImageRaw(Control ctrl, Rectangle subImageBounds) {
        ctrl.getShell().setActive();
        if (ctrl instanceof Label) {
            Image image = ((Label) ctrl).getImage();
            if (image != null) {
                return new Image(image.getDevice(), image, SWT.IMAGE_COPY);
            }
        }
        GC gc = new GC(ctrl);
        final Rectangle bounds = ctrl.getBounds();
        Image image = null;
        if (!bounds.equals(subImageBounds)) {
            image = new Image(ctrl.getDisplay(), subImageBounds.width, subImageBounds.height);
            gc.copyArea(image, subImageBounds.x, subImageBounds.y);
            gc.dispose();
        } else {
            image = new Image(ctrl.getDisplay(), bounds.width, bounds.height);
            gc.copyArea(image, 0, 0);
            ScreenshotSupport.saveImage(image.getImageData(), "");
            gc.dispose();
        }
        return image;
    }

    public static Image copyImagePart(Image img, Rectangle subImageBounds) {
        GC gc = new GC(img);
        final Rectangle bounds = img.getBounds();
        Image image = null;
        if (!bounds.equals(subImageBounds)) {
            image = new Image(img.getDevice(), subImageBounds.width, subImageBounds.height);
            gc.copyArea(image, subImageBounds.x, subImageBounds.y);
            gc.dispose();
        } else {
            gc.dispose();
            image = img;
        }
        ScreenshotSupport.saveImage(image.getImageData(), "copy_img");
        return image;
    }

    public static byte[] captureControlImage(Image img) {
        ImageData data = img.getImageData();
        ImageLoader loader = new ImageLoader();
        loader.data = new ImageData[] { data };
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        loader.save(stream, SWT.IMAGE_PNG);
        img.dispose();
        return stream.toByteArray();
    }

    public static Image prepateImageForOCR(byte[] image, int x, int y, int width, int height) {
        Display display = PlatformUI.getWorkbench().getDisplay();
        Image img = new Image(display, new ByteArrayInputStream(image));
        ScreenshotSupport.saveImage(img.getImageData(), "ocr");
        Image scaled = prepareImageForOCR(x, y, width, height, img);
        ScreenshotSupport.saveImage(scaled.getImageData(), "ocr_scaled");
        img.dispose();
        return scaled;
    }

    public static Image prepareImageForOCR(int x, int y, int width, int height, Image img) {
        int mult = 2;
        Image scaled = new Image(img.getDevice(), width * mult, height * mult);
        GC gc = new GC(scaled);
        gc.drawImage(img, x, y, width, height, 0, 0, width * mult, height * mult);
        ScreenshotSupport.saveImage(scaled.getImageData(), "ocr_part");
        gc.dispose();
        return scaled;
    }

    public static void disableMessageDialogs() {
        SWTDialogManager.setCancelMessageBoxesDisplay(true);
    }

    public static void enableMessageDialogs() {
        SWTDialogManager.setCancelMessageBoxesDisplay(false);
    }

    public synchronized static SWTUIPlayer getPlayer(Display display) {
        SWTUIPlayer p = players.get(display);
        if (p == null) {
            p = new SWTUIPlayer(display);
            players.put(display, p);
        }
        return p;
    }

    public synchronized static SWTUIPlayer getPlayer() {
        return SWTUIPlayer.getPlayer(PlatformUI.getWorkbench().getDisplay());
    }

    public synchronized static void shutdown(SWTUIPlayer internalPlayer) {
        if (internalPlayer != null) {
            players.remove(internalPlayer.getDisplay());
            internalPlayer.shutdown();
        }
    }

    public Display getDisplay() {
        return display;
    }

    public void minimize(SWTUIElement uiElement) {
        final Widget widget = unwrapWidget(uiElement);
        exec("minimize", new Runnable() {
            @Override
            public void run() {
                processTabFolderButton(widget, IWorkbenchPage.STATE_MINIMIZED);
            }
        });
    }

    public void maximize(SWTUIElement uiElement) {
        final Widget widget = unwrapWidget(uiElement);

        exec("maximize", new Runnable() {
            @Override
            public void run() {
                if (widget instanceof Shell) {
                    ((Shell) widget).setMaximized(true);
                    try {
                        ShellUtilsProvider.getShellUtils().forceActive((Shell) widget);
                    } catch (CoreException e) {
                        throw new RuntimeException(e);
                    }
                }
                processTabFolderButton(widget, IWorkbenchPage.STATE_MAXIMIZED);
            }
        });
    }

    public void restore(SWTUIElement uiElement) {
        final Widget widget = unwrapWidget(uiElement);
        exec("restore", new Runnable() {
            @Override
            public void run() {
                processTabFolderButton(widget, IWorkbenchPage.STATE_RESTORED);
            }
        });
    }

    private void processTabFolderButton(Widget widget, int buttonId) {
        EclipseWorkbenchProvider.getProvider().processTabFolderButton(widget, buttonId);
    }

    public void showTabList(SWTUIElement uiElement) {
        final Widget widget = unwrapWidget(uiElement);
        exec("showTabList", new Runnable() {
            @Override
            public void run() {
                processTabShowList(widget);
            }
        });
    }

    private void processTabShowList(Widget widget) {
        EclipseWorkbenchProvider.getProvider().processTabShowList(widget);
    }

    public void setPerspective(final String perspectiveId) {
        exec("setPerspective", new Runnable() {
            @Override
            public void run() {
                IPerspectiveDescriptor persectiveDescriptor = PlatformUI.getWorkbench().getPerspectiveRegistry()
                        .findPerspectiveWithId(perspectiveId);
                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
                        .setPerspective(persectiveDescriptor);
            }
        });
    }

    public void wakeup() {
        notifyUI(getDisplay());
    }

    public final class ExecRunnable implements Runnable {
        private final Context currentContext;
        private final Runnable runnable;
        private final String errorMethod;
        private String msg;

        private ExecRunnable(String msg, Context currentContext, Runnable runnable, String errorMethod) {
            this.msg = msg;
            this.currentContext = currentContext;
            this.runnable = runnable;
            this.errorMethod = errorMethod;
        }

        @Override
        public String toString() {
            return errorMethod;
        }

        @Override
        public void run() {
            // ReportBuilder builder = ReportManager.getBuilder();
            // if (builder != null) {
            // builder.beginTask("Running raw command:" + msg);
            // }
            collector.enable();
            try {
                runnable.run();
                // Wait for all jobs
                display.wake();
            } catch (Throwable t) {
                if (error != null)
                    error = t;
                TeslaCore.log(new TeslaExecutionFailedException(errorMethod, t));
            } finally {
                // if (builder != null) {
                // builder.endTask();
                // }
                // collector.setNeedDisable();
                // Will be called from next sleep state.
                synchronized (runnables) {
                    if (currentContext != null) {
                        List<Runnable> runs = runnables.get(currentContext);
                        runs.remove(this);
                    }
                }
            }
        }
    }

    private static class NotifyUINullRunnable implements Runnable {
        @Override
        public void run() {
        }
    };

    private static NotifyUINullRunnable notifyUINullRunnable;

    public static void notifyUI(Display display) {
        display.wake();
        display.asyncExec(notifyUINullRunnable);
    }

    public static synchronized void addExtension(ISWTUIPlayerExtension extension) {
        extensions.add(extension);
    }

    public static synchronized void removeExtension(ISWTUIPlayerExtension extension) {
        extensions.remove(extension);
    }

    public static synchronized List<ISWTUIPlayerExtension> getExtensions() {
        return new ArrayList<ISWTUIPlayerExtension>(extensions);
    }

    public boolean cleanMenus(final Q7WaitInfoRoot info) {
        final boolean result[] = { false };

        Display curDisplay = PlatformUI.getWorkbench().getDisplay();
        if (curDisplay == null || curDisplay.isDisposed()) {
            return false;
        }
        curDisplay.syncExec(new Runnable() {
            @Override
            public void run() {
                for (WeakReference<Menu> weakReference : shownMenus) {
                    Menu menu = weakReference.get();
                    if (menu == null) {
                        continue;
                    }
                    if (!menu.isDisposed()) {
                        events.sendEvent(menu, SWT.Hide);
                    }
                    Q7WaitUtils.updateInfo("menu", "hide", info);

                }
                result[0] = !shownMenus.isEmpty();
                shownMenus.clear();
                // TODO Auto-generated method stub
            }
        });
        return result[0];
    }

    public void clean() {
        getCollector().clean();
        getBrowserManager().clear();
        cleanMenus(null);
        error = null;
    }

    public boolean isCollectable(SWTUIElement element, Class<?>[] classes) {
        if (element == null)
            return false;

        if (classes == null)
            return true;

        for (ISWTUIPlayerExtension extension : getExtensions()) {
            if (extension.isCollectable(element, classes)) {
                return true;
            }
        }

        return false;
    }
}