/******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.eclipse.ui.views; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.window.IShellProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.UIPlugin; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.IWorkbenchSiteProgressService; import com.windowtester.eclipse.ui.UiPlugin; import com.windowtester.eclipse.ui.WTUI; import com.windowtester.eclipse.ui.actions.LaunchRecorderViewAction; import com.windowtester.eclipse.ui.layout.CardLayout; import com.windowtester.eclipse.ui.session.ISession; import com.windowtester.eclipse.ui.session.ISessionMonitor; import com.windowtester.eclipse.ui.usage.ProfiledAction; import com.windowtester.eclipse.ui.usage.ProfiledDelegateAction; import com.windowtester.eclipse.ui.viewers.EventSequenceTreeViewer; import com.windowtester.eclipse.ui.views.DashboardController.IRecorderDashActionProvider; import com.windowtester.recorder.ui.EventSequenceModel; import com.windowtester.recorder.ui.IEventSequenceView; import com.windowtester.recorder.ui.IRecorderConsoleActionHandler; import com.windowtester.recorder.ui.IRecorderPanelView; import com.windowtester.recorder.ui.RecorderPanelModel; import com.windowtester.runtime.WT; import com.windowtester.runtime.swt.internal.display.DisplayExec; import com.windowtester.runtime.swt.locator.ButtonLocator; import com.windowtester.runtime.swt.locator.MenuItemLocator; import com.windowtester.ui.core.model.ISemanticEvent; /** * View that presents recorded events. */ @SuppressWarnings("restriction") public class RecorderConsoleView extends ViewPart implements IRecorderPanelView, IEventSequenceView, IRecorderDashActionProvider, IShellProvider { private static final boolean INIT_WITH_STUB_EVENTS = false; public static final String MENU_MANAGER_NAME = "consoleMenuManager"; //TODO: move me! (used to id tool item contribs to be ignored in recording (temp)) public static final String ACTION_TAG_PREFIX = "com.windowtester.util.ui.views.recorder.actions."; private static final String HELP_CONTEXT_ID = "com.windowtester.eclipse.help.recording_console"; /** * The id used to display the event tree in the console panel. */ private static final String EVENT_TREE_PANE_ID = "eventTree"; private static class ContextMenuAction extends ProfiledDelegateAction { public ContextMenuAction(Action action) { super(action, "contextMenu"); } } //the presenter private RecorderConsolePresenter presenter; //actions/buttons private Action recordAction; private Action pauseAction; private Action codegenAction; private Action deleteAction; private Action hookAction; private Action spyAction; private LaunchRecorderViewAction launchAction; private EventSequenceTreeViewer treeViewer; private EmptyRecorderConsoleControl emptyConsoleViewer;; private AssertionHookActionHandler hookHandler; private IDashboardController dashController; private Composite consoleArea; private CardLayout consoleAreaLayout; private class EmptyEventTreeStateUpdater { public void update() { DisplayExec.sync(new Runnable(){ public void run() { if (inEmptyState()) { showEmptyConsolePane(); } else { showEventTreePane(); } updateContentDescription(); } }); } private boolean inEmptyState() { return !getSessionMonitor().inSession() && getViewer().getSequence().isEmpty(); } } private final EmptyEventTreeStateUpdater emptyStateUpdater = new EmptyEventTreeStateUpdater(); public RecorderConsoleView() { //NOTE: presenter and controller set lazily } public IDashboardController getDashController() { if (dashController == null) dashController = new DashboardController(this); return dashController; } public RecorderConsolePresenter getPresenter() { if (presenter == null) presenter = new RecorderConsolePresenter(new RecorderPanelModel(), this, new EventSequenceModel(), this); return presenter; } /* (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { addToolItems(); createConsoleArea(parent); createActionBarMenu(); if (INIT_WITH_STUB_EVENTS) addStubEvents(); updateContentDescription(); hookupDynamicHelp(parent); addSessionProgressFeedback(); update(); } private void createConsoleArea(Composite parent) { emptyConsoleViewer = new EmptyRecorderConsoleControl(launchAction); consoleArea = new Composite(parent, SWT.FILL); consoleAreaLayout = new CardLayout(consoleArea); consoleArea.setLayout(consoleAreaLayout); createEventTreePane(consoleArea); createEmptyResultPane(consoleArea); consoleAreaLayout.show(emptyConsoleViewer.getId()); } private void createEmptyResultPane(Composite parent) { emptyConsoleViewer.createControl(parent); } private void createEventTreePane(Composite parent) { addTree(parent); addContextMenus(); } private void addSessionProgressFeedback() { /* $if eclipse.version >= 3.3 $ */ getSessionMonitor().addListener(new ISessionMonitor.ISessionListener() { public void started(ISession session) { getProgressService().incrementBusy(); } public void ended(ISession session) { getProgressService().decrementBusy(); } }); /* $endif $ */ } protected ISessionMonitor getSessionMonitor() { return UiPlugin.getDefault().getSessionMonitor(); } protected void showEmptyResultPage() { } private void createActionBarMenu() { IActionBars actionBars = getViewSite().getActionBars(); IMenuManager menuManager = actionBars.getMenuManager(); menuManager.add(new OpenWTPreferencesPageAction(this)); } /* (non-Javadoc) * @see org.eclipse.jface.window.IShellProvider#getShell() */ public Shell getShell() { return getSite().getShell(); } private void hookupDynamicHelp(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, HELP_CONTEXT_ID); } /* (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ public void dispose() { getPresenter().dispose(); super.dispose(); } private void addTree(Composite parent) { //this is not quite right -- this wiring of model to view should happen //in the presenter... treeViewer = new EventSequenceTreeViewer(parent, getPresenter().getSequenceModel()); Tree tree = treeViewer.getTreeViewer().getTree(); tree.setLayoutData(EVENT_TREE_PANE_ID); tree.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { //no-op } public void keyReleased(KeyEvent e) { if (e.character == WT.DEL || e.character == WT.BS) { deleteAction.run(); } } }); } IWorkbenchSiteProgressService getProgressService() { IWorkbenchSiteProgressService progressService = null; Object siteService = getSite().getAdapter(IWorkbenchSiteProgressService.class); if (siteService != null) progressService = (IWorkbenchSiteProgressService) siteService; return progressService; } //for debuggging private void addStubEvents() { getPresenter().getSequenceModel().add( FakeEventFactory.fakeSelectEvent(MenuItem.class, new MenuItemLocator("File/New"))).add( FakeEventFactory.fakeSelectEvent(Button.class, new ButtonLocator("New"))).add( FakeEventFactory.fakeSelectEvent(Button.class, new MenuItemLocator("Cancel"))).add( FakeEventFactory.fakeKeyEntry('x')).add( FakeEventFactory.fakeKeyEntry('y')).add( FakeEventFactory.fakeKeyEntry('z')).add( FakeEventFactory.fakeSelectEvent(Button.class, new MenuItemLocator("Cancel")) ); } private void addContextMenus() { final TreeViewer viewer = treeViewer.getTreeViewer(); MenuManager mm = new MenuManager(MENU_MANAGER_NAME); mm.setRemoveAllWhenShown(true); mm.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { if (viewer == null) return; Tree tree = viewer.getTree(); if (tree == null) return; final TreeItem[] selection = tree.getSelection(); if (selection.length == 0) { return; } manager.add(new ContextMenuAction(deleteAction)); // manager.add(new Action("Group") { // public void run() { // super.run(); // System.out.println("DO group!"); // // ISemanticEvent[] selected = getSelection(); // IEvent[] events = new IEvent[selected.length]; // for (int i = 0; i < selection.length; i++) { // events[i] = (IEvent)selected[i]; // } // IEventGroup group = getPresenter().group(events); // getViewer().setGroupedState(group); // // } // }); } }); mm.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); Control control = viewer.getControl(); Menu menu = mm.createContextMenu(control); control.setMenu(menu); //re-enable if we want to accept contributions // getSite().registerContextMenu(mm, viewer); } public EventSequenceTreeViewer getViewer() { return treeViewer; } private void update() { getPresenter().update(); } private void addToolItems() { addRecordItem(); addPauseItem(); //NOTE: disabled item for now (optionally enabled via pref) addSpyItem(); addHookItem(); addDeleteItem(); addToolItemSeparator(); addCodegenItem(); addToolItemSeparator(); addLaunchItem(); getViewSite().getActionBars().updateActionBars(); } private void addHookItem() { hookAction = new ProfiledAction("Add Assertion Hook...") { public void doRun() { clickAddHook(); } }; hookAction.setImageDescriptor(imageDescriptor("assertion_hook.gif")); hookAction.setDisabledImageDescriptor(imageDescriptor("assertion_hook_dis.gif")); hookAction.setId(actionTag("hook")); addToolItemAction(hookAction); } private void addCodegenItem() { codegenAction = new ProfiledAction("Generate Test...") { public void doRun() { clickCodegen(); } }; codegenAction.setImageDescriptor(imageDescriptor("codegen_gears.gif")); codegenAction.setDisabledImageDescriptor(imageDescriptor("codegen_gears_dis.gif")); codegenAction.setId(actionTag("codegen")); addToolItemAction(codegenAction); } private void addDeleteItem() { deleteAction = new ProfiledAction("Delete") { public void doRun() { clickDelete(); } }; deleteAction.setText("Delete"); ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); deleteAction.setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE)); deleteAction.setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE_DISABLED)); deleteAction.setId(ACTION_TAG_PREFIX + "delete"); addToolItemAction(deleteAction); } private void addPauseItem() { pauseAction = new ProfiledAction("Pause") { public void doRun() { clickPause(); } }; pauseAction.setImageDescriptor(imageDescriptor("pause.gif")); pauseAction.setDisabledImageDescriptor(imageDescriptor("pause_dis.gif")); pauseAction.setId(actionTag("pause")); addToolItemAction(pauseAction); } private void addRecordItem() { recordAction = new ProfiledAction("Record") { public void doRun() { clickRecord(); } }; recordAction.setImageDescriptor(imageDescriptor("start_recording.gif")); recordAction.setDisabledImageDescriptor(imageDescriptor("start_recording_dis.gif")); recordAction.setId(actionTag("record")); addToolItemAction(recordAction); } private void addSpyItem() { spyAction = new ProfiledAction("Inspect" /*, SWT.TOGGLE */) { public void doRun() { clickSpyMode(); } }; spyAction.setImageDescriptor(imageDescriptor("spy.gif")); spyAction.setDisabledImageDescriptor(imageDescriptor("spy_dis.gif")); spyAction.setId(actionTag("spy")); //TODO: enable when this is more backed --- can be enabled for the remote via a preference //addToolItemAction(spyAction); } private void addLaunchItem() { launchAction = new LaunchRecorderViewAction(imageDescriptor("manual_run_wiz.gif")); launchAction.init(getSite().getWorkbenchWindow()); launchAction.setId(actionTag("launchRecorder")); addToolItemAction(launchAction); } private String actionTag(String id) { return ACTION_TAG_PREFIX + id; } private void addToolItemAction(Action action) { IToolBarManager tm = getViewSite().getActionBars().getToolBarManager(); tm.add(action); } private void addToolItemSeparator() { IToolBarManager tm = getViewSite().getActionBars().getToolBarManager(); tm.add(new Separator()); } private ImageDescriptor imageDescriptor(String imageFilePath) { return UIPlugin.imageDescriptorFromPlugin(UiPlugin.PLUGIN_ID, "icons/full/obj16/" + imageFilePath); } /* (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ public void setFocus() { // TODO Auto-generated method stub } public void addHandler(IRecorderConsoleActionHandler listener) { getPresenter().addHandler(listener); } public void removeHandler(IRecorderConsoleActionHandler listener) { getPresenter().removeHandler(listener); } /////////////////////////////////////////////////////////////////////// // // Selection (click) actions // /////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderActions#clickPause() */ public void clickPause() { getPresenter().clickPause(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderActions#clickRecord() */ public void clickRecord() { getPresenter().clickRecord(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderActions#clickRestart() */ public void clickRestart() { getPresenter().clickRestart(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderActions#clickDelete() */ public void clickDelete() { getPresenter().clickDelete(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IEventSequenceView#clickCodegen() */ public void clickCodegen() { getPresenter().clickCodegen(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderConsoleActionHandler#clickAddHook() */ public void clickAddHook() { //TODO: and here our neat separation of concerns breaks... (can we restore it?) /* * rather than delegating to the presenter, we're hooking in dreictly here (ugh */ getAssertionHookHandler().addAssertion(); //still call presenter to make sure updates get fired getPresenter().clickAddHook(); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderConsoleActionHandler#clickSpyMode() */ public void clickSpyMode() { getPresenter().clickSpyMode(); } /////////////////////////////////////////////////////////////////////// // // UI enablement // /////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderPanelView#setDeleteEnabled(boolean) */ public void setDeleteEnabled(boolean isEnabled) { deleteAction.setEnabled(isEnabled); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderPanelView#setPauseEnabled(boolean) */ public void setPauseEnabled(boolean isEnabled) { pauseAction.setEnabled(isEnabled); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IRecorderPanelView#setRecordEnabled(boolean) */ public void setRecordEnabled(boolean isEnabled) { recordAction.setEnabled(isEnabled); } /* (non-Javadoc) * @see com.windowtester.recorder.ui.IEventSequenceView#setCodegenEnabled(boolean) */ public void setCodegenEnabled(boolean enabled) { codegenAction.setEnabled(enabled); } /** * Accessor to surface events in the tree viewer. */ public ISemanticEvent[] getEvents() { return getViewer().getSequence().getEvents(); } /** * Get the current sequence selection. */ public ISemanticEvent[] getSelection() { return getViewer().getSelection(); } public AssertionHookActionHandler getAssertionHookHandler() { if (hookHandler == null) hookHandler = new AssertionHookActionHandler(getPresenter().getSequenceModel()); return hookHandler; } int refreshed = 0; /* (non-Javadoc) * @see com.windowtester.recorder.ui.IEventSequenceView#refresh() */ public void refresh() { getViewer().refresh(); emptyStateUpdater.update(); } private void updateContentDescription() { setContentDescription(SessionSummaryLabelProvider.getSummary(this)); } private void showEventTreePane() { consoleAreaLayout.show(EVENT_TREE_PANE_ID); } private void showEmptyConsolePane() { emptyConsoleViewer.aboutToShow(); consoleAreaLayout.show(emptyConsoleViewer.getId()); } /* (non-Javadoc) * @see com.windowtester.eclipse.ui.views.DashboardController.IRecorderDashActionProvider#getActions() */ public IAction[] getActions() { List<Action> actions = new ArrayList<Action>(); actions.add(recordAction); actions.add(pauseAction); if (WTUI.isInspectorEnabled()) actions.add(spyAction); actions.add(hookAction); return (IAction[]) actions.toArray(new IAction[]{}); } }