blob: fe8ea086b56a46294551e42db49624244f7eb89a [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.ui.playback.util;
import com.intellij.ide.UiActivityMonitor;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.AsyncResult;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SimpleTimer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.Consumer;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.util.*;
public class WindowSystemPlaybackCall {
public static AsyncResult<String> printFocus(final PlaybackContext context) {
final AsyncResult result = new AsyncResult<String>();
getUiReady(context).doWhenProcessed(new Runnable() {
@Override
public void run() {
final LinkedHashMap<String, String> focusInfo = getFocusInfo();
if (focusInfo == null) {
result.setRejected("No component focused");
return;
}
StringBuffer text = new StringBuffer();
for (Iterator<String> iterator = focusInfo.keySet().iterator(); iterator.hasNext(); ) {
String key = iterator.next();
text.append(key + "=" + focusInfo.get(key));
if (iterator.hasNext()) {
text.append("|");
}
}
result.setDone(text.toString());
}
});
return result;
}
public static AsyncResult<String> waitForDialog(final PlaybackContext context, final String title) {
final AsyncResult<String> result = new AsyncResult<String>();
final Ref<AWTEventListener> listener = new Ref<AWTEventListener>();
listener.set(new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (event.getID() == WindowEvent.WINDOW_ACTIVATED) {
final Window wnd = ((WindowEvent)event).getWindow();
if (wnd instanceof JDialog) {
if (title.equals(((JDialog)wnd).getTitle())) {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener.get());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getUiReady(context).notify(result);
}
});
}
}
}
}
});
Toolkit.getDefaultToolkit().addAWTEventListener(listener.get(), WindowEvent.WINDOW_EVENT_MASK);
SimpleTimer.getInstance().setUp(new Runnable() {
@Override
public void run() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener.get());
if (!result.isProcessed()) {
result.setRejected("Timed out waiting for window: " + title);
}
}
}, Registry.intValue("actionSystem.commandProcessingTimeout"));
return result;
}
public static AsyncResult<String> checkFocus(final PlaybackContext context, String expected) {
final AsyncResult<String> result = new AsyncResult<String>();
final Map<String, String> expectedMap = new LinkedHashMap<String, String>();
if (expected.length() > 0) {
final String[] keyValue = expected.split("\\|");
for (String each : keyValue) {
final String[] eachPair = each.split("=");
if (eachPair.length != 2) {
result.setRejected("Syntax error, must be |-separated pairs key=value");
return result;
}
expectedMap.put(eachPair[0], eachPair[1]);
}
}
getUiReady(context).doWhenDone(new Runnable() {
@Override
public void run() {
try {
doAssert(expectedMap, result, context);
}
catch (AssertionError error) {
result.setRejected("Assertion failed: " + error.getMessage());
}
}
});
return result;
}
public static AsyncResult<String> waitForToolWindow(final PlaybackContext context, final String id) {
final AsyncResult<String> result = new AsyncResult<String>();
findProject().doWhenDone(new Consumer<Project>() {
@Override
public void consume(Project project) {
ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(id);
if (toolWindow == null) {
result.setRejected("Cannot find tool window with id: " + id);
return;
}
toolWindow.getReady(context).doWhenDone(result.createSetDoneRunnable()).doWhenRejected(new Runnable() {
@Override
public void run() {
result.setRejected("Cannot activate tool window with id:" + id);
}
});
}
}).doWhenRejected(new Runnable() {
@Override
public void run() {
result.setRejected("Cannot retrieve open project");
}
});
return result;
}
public static AsyncResult<Project> findProject() {
final AsyncResult<Project> project = new AsyncResult<Project>();
final IdeFocusManager fm = IdeFocusManager.getGlobalInstance();
fm.doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
Component parent = UIUtil.findUltimateParent(fm.getFocusOwner());
if (parent instanceof IdeFrame) {
IdeFrame frame = (IdeFrame)parent;
if (frame.getProject() != null) {
project.setDone(frame.getProject());
return;
}
}
project.setRejected();
}
});
return project;
}
public static AsyncResult<String> contextMenu(final PlaybackContext context, final String path) {
final AsyncResult<String> result = new AsyncResult<String>();
final IdeFocusManager fm = IdeFocusManager.getGlobalInstance();
fm.doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
Component owner = fm.getFocusOwner();
if (owner == null) {
result.setRejected("No component focused");
return;
}
ActionManager am = ActionManager.getInstance();
AnAction showPopupMenu = am.getAction("ShowPopupMenu");
if (showPopupMenu == null) {
result.setRejected("Cannot find action: ShowPopupMenu");
return;
}
am.tryToExecute(showPopupMenu, new MouseEvent(owner, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, 0, 0, 1, true), null,
null, false).doWhenDone(
new Runnable() {
@Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
MenuElement[] selectedPath = MenuSelectionManager.defaultManager().getSelectedPath();
if (selectedPath.length == 0) {
result.setRejected("Failed to find active popup menu");
return;
}
selectNext(context, path.split("\\|"), 0, selectedPath[0].getSubElements(), result);
}
});
}
}).doWhenRejected(new Runnable() {
@Override
public void run() {
result.setRejected("Cannot invoke popup menu from the ShowPopupMenu action, action call rejected");
}
});
}
});
return result;
}
private static void selectNext(final PlaybackContext context, final String[] toSelect, final int toSelectIndex, MenuElement[] menuElements, final AsyncResult<String> result) {
if (menuElements == null || menuElements.length == 0) {
result.setDone();
}
if (toSelectIndex > toSelect.length - 1) {
result.setDone();
return;
}
String target = toSelect[toSelectIndex];
for (final MenuElement each : menuElements) {
if (each.getComponent() instanceof AbstractButton) {
final AbstractButton eachButton = (AbstractButton)each.getComponent();
if (eachButton.getText() != null && eachButton.getText().startsWith(target)) {
activateItem(context, each).doWhenDone(new Consumer<MenuElement[]>() {
@Override
public void consume(MenuElement[] menuElements) {
selectNext(context, toSelect, toSelectIndex + 1, menuElements, result);
}
}).doWhenRejected(new Runnable() {
@Override
public void run() {
result.setRejected("Cannot activate menu element: " + eachButton.getText());
return;
}
});
return;
}
}
else {
result.setRejected("Unknown class for context menu item: " + each.getComponent());
return;
}
}
result.setRejected("Failed to find menu item: " + target);
}
private static AsyncResult<MenuElement[]> activateItem(final PlaybackContext context, final MenuElement element) {
final AsyncResult<MenuElement[]> result = new AsyncResult<MenuElement[]>();
final AbstractButton c = (AbstractButton)element.getComponent();
final Runnable pressRunnable = new Runnable() {
@Override
public void run() {
Robot robot = context.getRobot();
Point location = c.getLocationOnScreen();
Dimension size = c.getSize();
Point point = new Point(location.x + size.width / 2, location.y + size.height / 2);
robot.mouseMove(point.x, point.y);
robot.delay(90);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.delay(90);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.delay(90);
context.flushAwtAndRunInEdt(new Runnable() {
@Override
public void run() {
context.flushAwtAndRunInEdt(new Runnable() {
@Override
public void run() {
MenuElement[] subElements = element.getSubElements();
if (subElements == null || subElements.length == 0) {
result.setDone();
}
else {
MenuElement[] menuElements = subElements[0].getSubElements();
result.setDone(menuElements);
}
}
});
}
});
}
};
if (c.isShowing()) {
context.runPooledThread(pressRunnable);
} else {
context.delayAndRunInEdt(new Runnable() {
@Override
public void run() {
if (c.isShowing()) {
context.runPooledThread(pressRunnable);
} else {
result.setRejected();
}
}
}, 1000);
}
return result;
}
public static ActionCallback getUiReady(final PlaybackContext context) {
final ActionCallback result = new ActionCallback();
context.flushAwtAndRunInEdt(new Runnable() {
@Override
public void run() {
UiActivityMonitor.getInstance().getBusy().getReady(context).notify(result);
}
});
return result;
}
private static void doAssert(Map<String, String> expected, AsyncResult<String> result, PlaybackContext context) throws AssertionError {
final LinkedHashMap<String, String> actual = getFocusInfo();
if (actual == null) {
result.setRejected("No component focused");
return;
}
Set testedKeys = new LinkedHashSet<String>();
for (String eachKey : expected.keySet()) {
testedKeys.add(eachKey);
final String actualValue = actual.get(eachKey);
final String expectedValue = expected.get(eachKey);
if (!expectedValue.equals(actualValue)) {
result.setRejected(eachKey + " expected: " + expectedValue + " but was: " + actualValue);
return;
}
}
Map<String, String> untested = new HashMap<String, String>();
for (String eachKey : actual.keySet()) {
if (testedKeys.contains(eachKey)) continue;
untested.put(eachKey, actual.get(eachKey));
}
StringBuffer untestedText = new StringBuffer();
for (String each : untested.keySet()) {
if (untestedText.length() > 0) {
untestedText.append(",");
}
untestedText.append(each).append("=").append(untested.get(each));
}
result.setDone();
if (untestedText.length() > 0) {
context.message("Untested focus info: " + untestedText.toString(), context.getCurrentLine());
}
}
private static LinkedHashMap<String, String> getFocusInfo() {
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (owner == null) {
return null;
}
Component eachParent = owner;
final LinkedHashMap<String, String> actual = new LinkedHashMap<String, String>();
while (eachParent != null) {
if (eachParent instanceof Queryable) {
((Queryable)eachParent).putInfo(actual);
}
eachParent = eachParent.getParent();
}
return actual;
}
public static AsyncResult<String> flushUi(PlaybackContext context) {
AsyncResult<String> result = new AsyncResult<String>();
getUiReady(context).notify(result);
return result;
}
}