/* MonkeyTalk - a cross-platform functional testing tool Copyright (C) 2012 Gorilla Logic, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.gorillalogic.monkeyconsole.tableview; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FilenameUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ColumnViewerEditor; import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.FocusCellHighlighter; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TableViewerEditor; import org.eclipse.jface.viewers.TableViewerFocusCellManager; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerRow; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.part.FileEditorInput; import com.gorillalogic.monkeyconsole.editors.utils.MonkeyTalkController; import com.gorillalogic.monkeyconsole.editors.utils.MonkeyTalkUtils; import com.gorillalogic.monkeyconsole.plugin.FoneMonkeyPlugin; import com.gorillalogic.monkeyconsole.preferences.PreferenceConstants; import com.gorillalogic.monkeyconsole.tableview.editors.ActionEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.ArgsEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.ComponentEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.MonkeyidEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.ShouldFailEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.ThinktimeEditingSupport; import com.gorillalogic.monkeyconsole.tableview.editors.TimeoutEditingSupport; import com.gorillalogic.monkeyconsole.tableview.labelproviders.ActionLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.ArgsLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.ComponentLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.MonkeyidLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.RowNumberLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.ShouldFailLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.ThinktimeLabelProvider; import com.gorillalogic.monkeyconsole.tableview.labelproviders.TimeoutLabelProvider; import com.gorillalogic.monkeytalk.Command; /** * This class allows you to create and edit Command objects */ public class MonkeyTalkTabularEditor extends EditorPart { // Table column names/properties public static final String ROW = "Row"; public static final String COMPONENT = "Component"; public static final String MONKEYID = "MonkeyID"; public static final String ACTION = "Action"; public static final String ARGS = "Arguments"; public static final String TIMEOUT = "Timeout (ms)"; public static final String THINKTIME = "ThinkTime (ms)"; public static final String SHOULDFAIL = "Should Fail"; public boolean commandKeyDown = false; public static final String[] PROPS = { ROW, COMPONENT, MONKEYID, ACTION, ARGS, TIMEOUT, THINKTIME, SHOULDFAIL }; // The data model, this is marked as final because the labelproviders and // editors need access to it final private java.util.List<TableRow> commands; TableViewer tv = null; Table table = null; String[] limitedComponentSet = null; MonkeyTalkController monkeyControls; int menueventsCaught = 0; public MonkeyTalkController getMonkeyControls() { return monkeyControls; } public void setMonkeyControls(MonkeyTalkController monkeyControls) { this.monkeyControls = monkeyControls; } /** * Constructs a JfaceTableExample */ public MonkeyTalkTabularEditor(MonkeyTalkController monkeyControls) { commands = new ArrayList<TableRow>(); this.monkeyControls = monkeyControls; } /** * Constructs a JfaceTableExample */ public MonkeyTalkTabularEditor(String[] limitedComponentSet, MonkeyTalkController monkeyControls) { this.limitedComponentSet = limitedComponentSet; commands = new ArrayList<TableRow>(); this.monkeyControls = monkeyControls; } /** * Creates the main window's contents * * @param parent * the main window * @return Control */ protected Control createContents(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, false)); // Add the TableViewer tv = new TableViewer(composite, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER); tv.setContentProvider(new CommandContentProvider()); tv.setInput(commands); table = tv.getTable(); table.setLayoutData(new GridData(GridData.FILL_BOTH)); TableColumnLayout layout = new TableColumnLayout(); composite.setLayout(layout); // Row Number Column TableViewerColumn row_col = createTableViewerColumn(ROW, 100, 0); row_col.setLabelProvider(new RowNumberLabelProvider(commands)); layout.setColumnData(row_col.getColumn(), new ColumnWeightData(10)); // Component Type Column TableViewerColumn component_col = createTableViewerColumn(COMPONENT, 100, 1); component_col.setLabelProvider(new ComponentLabelProvider()); component_col.setEditingSupport(new ComponentEditingSupport(tv, limitedComponentSet) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(component_col.getColumn(), new ColumnWeightData(30)); TableViewerColumn monkeyid_col = createTableViewerColumn(MONKEYID, 100, 2); monkeyid_col.setLabelProvider(new MonkeyidLabelProvider()); monkeyid_col.setEditingSupport(new MonkeyidEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(monkeyid_col.getColumn(), new ColumnWeightData(25)); TableViewerColumn action_col = createTableViewerColumn(ACTION, 100, 3); action_col.setLabelProvider(new ActionLabelProvider()); action_col.setEditingSupport(new ActionEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(action_col.getColumn(), new ColumnWeightData(30)); TableViewerColumn args_col = createTableViewerColumn(ARGS, 100, 4); args_col.setLabelProvider(new ArgsLabelProvider()); args_col.setEditingSupport(new ArgsEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(args_col.getColumn(), new ColumnWeightData(40)); TableViewerColumn timeout_col = createTableViewerColumn(TIMEOUT, 100, 5); timeout_col.setLabelProvider(new TimeoutLabelProvider()); timeout_col.setEditingSupport(new TimeoutEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(timeout_col.getColumn(), new ColumnWeightData(20)); TableViewerColumn thinktime_col = createTableViewerColumn(THINKTIME, 100, 6); thinktime_col.setLabelProvider(new ThinktimeLabelProvider()); thinktime_col.setEditingSupport(new ThinktimeEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(thinktime_col.getColumn(), new ColumnWeightData(20)); String extencion = FilenameUtils.getExtension(((FileEditorInput) this.getEditorInput()) .getPath().toFile().getName()); if (extencion.equals("mt")) { TableViewerColumn shouldfail_col = createTableViewerColumn(SHOULDFAIL, 100, 7); shouldfail_col.getColumn().setAlignment(SWT.CENTER); shouldfail_col.setLabelProvider(new ShouldFailLabelProvider(tv)); shouldfail_col.setEditingSupport(new ShouldFailEditingSupport(tv) { public void dataChanged() { MonkeyTalkTabularEditor.this.doDataChanged(); }; }); layout.setColumnData(shouldfail_col.getColumn(), new ColumnWeightData(15)); } tv.getTable().addListener(SWT.MenuDetect, new Listener() { public void handleEvent(Event event) { if (menueventsCaught != event.time) { new ContextMenu(MonkeyTalkTabularEditor.this, event).show(); menueventsCaught = event.time; } } }); table.addListener(SWT.MouseDoubleClick, new Listener() { @Override public void handleEvent(Event e) { TableItem item = table.getItem(new Point(e.x, e.y)); if (item == null) { } else { // click link case String comp = ((TableRow) item.getData()).getComponentType(); if (comp != null && (comp.equalsIgnoreCase("Script") || comp.equalsIgnoreCase("Test") || comp.equalsIgnoreCase("SetUp") || comp .equalsIgnoreCase("Teardown"))) { try { IEditorPart ieditorpart = MonkeyTalkTabularEditor.this.getEditorSite() .getPage().getActiveEditor(); String dotMt = ".mt"; if (((TableRow) item.getData()).getMonkeyId().contains(".mt") || ((TableRow) item.getData()).getMonkeyId().contains(".js")) { dotMt = ""; } IFile fileToBeOpened = ((IFileEditorInput) ieditorpart.getEditorInput()) .getFile().getProject() .getFile(((TableRow) item.getData()).getMonkeyId() + dotMt); IEditorInput editorInput = new FileEditorInput(fileToBeOpened); MonkeyTalkTabularEditor.this .getEditorSite() .getPage() .openEditor(editorInput, "com.gorillalogic.monkeyconsole.editors.FoneMonkeyTestEditor"); commandKeyDown = false; } catch (CoreException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } } } } }); table.addListener(SWT.MouseUp, new Listener() { @Override public void handleEvent(Event e) { TableItem item = table.getItem(new Point(e.x, e.y)); if (item == null) { if (e.y < (table.getItemCount() * table.getItemHeight()) + table.getItemHeight()) { appendRow(); } } else { // click link case String comp = ((TableRow) item.getData()).getComponentType(); if (comp != null && commandKeyDown && tv.getCell(new Point(e.x, e.y)).getColumnIndex() == 2 && (comp.equalsIgnoreCase("Script") || comp.equalsIgnoreCase("Test") || comp.equalsIgnoreCase("Run") || comp.equalsIgnoreCase("RunWith") || comp.equalsIgnoreCase("SetUp") || comp .equalsIgnoreCase("Teardown"))) { try { IEditorPart ieditorpart = MonkeyTalkTabularEditor.this.getEditorSite() .getPage().getActiveEditor(); IFile fileToBeOpened = ((IFileEditorInput) ieditorpart.getEditorInput()) .getFile().getProject() .getFile(tv.getCell(new Point(e.x, e.y)).getText() + ".mt"); IEditorInput editorInput = new FileEditorInput(fileToBeOpened); MonkeyTalkTabularEditor.this .getEditorSite() .getPage() .openEditor(editorInput, "com.gorillalogic.monkeyconsole.editors.FoneMonkeyTestEditor"); commandKeyDown = false; } catch (CoreException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } } } } }); // FOCUS TRAVERSAL FocusCellHighlighter highlighter = new FocusCellHighlighter(tv) { }; TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tv, highlighter) { }; ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy( tv) { protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) { return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; } }; TableViewerEditor.create(tv, focusCellManager, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION); table.setHeaderVisible(true); table.setLinesVisible(true); table.addKeyListener(new KeyListener() { @Override public void keyPressed(KeyEvent arg0) { if (arg0.keyCode == SWT.CTRL || arg0.keyCode == SWT.COMMAND) { commandKeyDown = true; } } @Override public void keyReleased(KeyEvent arg0) { if (arg0.keyCode == SWT.CTRL || arg0.keyCode == SWT.COMMAND) { commandKeyDown = false; } if (commandKeyDown && (arg0.character == 'P' || arg0.character == 'p')) { if (monkeyControls.isCurrentlyConnected() && !monkeyControls.isRecordingON() && getLimitedComponentSet() == null) monkeyControls .startReplayRange(tv.getTable().getSelectionIndex(), tv.getTable() .getSelectionIndex() + tv.getTable().getSelectionCount()); } System.out.println(arg0.keyCode); if (arg0.keyCode == SWT.DEL || arg0.keyCode == 8) { deleteRows(getTv().getTable().getSelectionIndices()); } } }); int ops = DND.DROP_COPY | DND.DROP_MOVE; Transfer[] transfers = new Transfer[] { TextTransfer.getInstance() }; tv.addDropSupport(ops, transfers, new GadgetTableDropAdapter(tv)); return composite; } private static ViewerCell getNeighbor(ViewerCell currentCell, int directionMask, boolean sameLevel) { ViewerRow row; if ((directionMask & ViewerCell.ABOVE) == ViewerCell.ABOVE) { row = currentCell.getViewerRow().getNeighbor(ViewerRow.ABOVE, sameLevel); } else if ((directionMask & ViewerCell.BELOW) == ViewerCell.BELOW) { row = currentCell.getViewerRow().getNeighbor(ViewerRow.BELOW, sameLevel); } else { row = currentCell.getViewerRow(); } if (row != null) { int columnIndex; columnIndex = getVisualIndex(row, currentCell.getColumnIndex()); int modifier = 0; if ((directionMask & ViewerCell.LEFT) == ViewerCell.LEFT) { modifier = -1; } else if ((directionMask & ViewerCell.RIGHT) == ViewerCell.RIGHT) { modifier = 1; } columnIndex += modifier; if (columnIndex >= 0 && columnIndex < row.getColumnCount()) { ViewerCell cell = getCellAtVisualIndex(row, columnIndex); if (cell != null) { while (cell != null && columnIndex < row.getColumnCount() - 1 && columnIndex > 0) { if (isVisible(cell)) { break; } columnIndex += modifier; cell = getCellAtVisualIndex(row, columnIndex); if (cell == null) { break; } } } return cell; } } return null; } // Reimplementation of ViewerCell-Methods private static int getVisualIndex(ViewerRow row, int creationIndex) { TableItem item = (TableItem) row.getItem(); int[] order = item.getParent().getColumnOrder(); for (int i = 0; i < order.length; i++) { if (order[i] == creationIndex) { return i; } } return creationIndex; } private static ViewerCell getCellAtVisualIndex(ViewerRow row, int visualIndex) { return getCell(row, getCreationIndex(row, visualIndex)); } private static boolean isVisible(ViewerCell cell) { return getWidth(cell) > 0; } private static int getWidth(ViewerCell cell) { TableItem item = (TableItem) cell.getViewerRow().getItem(); return item.getParent().getColumn(cell.getColumnIndex()).getWidth(); } private static ViewerCell getCell(ViewerRow row, int index) { return row.getCell(index); } private static int getCreationIndex(ViewerRow row, int visualIndex) { TableItem item = (TableItem) row.getItem(); if (item != null && !item.isDisposed() /* * && hasColumns() && isValidOrderIndex * (visualIndex) */) { return item.getParent().getColumnOrder()[visualIndex]; } return visualIndex; } private TableViewerColumn createTableViewerColumn(String title, int bound, final int colNumber) { final TableViewerColumn viewerColumn = new TableViewerColumn(tv, SWT.NONE); final TableColumn column = viewerColumn.getColumn(); column.setText(title); column.setWidth(bound); column.setResizable(true); column.setMoveable(true); return viewerColumn; } /** * Append a row to the bottom of the table * * @return true if the row was added, false if not */ public void appendRow() { TableRow c = new TableRow(); c.setAction(""); c.setArgsAndModifiers(""); c.setComponentType(""); c.setMonkeyId(""); commands.add(c); doDataChanged(); tv.refresh(); } /** * Append a row to the bottom of the table * * @return true if the row was added, false if not */ public void appendRow(Command c) { TableRow r = new TableRow(c); commands.add(r); doDataChanged(); tv.refresh(); table.setTopIndex(table.getItemCount() - 1); } /** * Append a row using coalescing * * @param c * @param useCooalessing */ public void appendRow(Command cr, boolean useCooalessing) { TableRow newrow = new TableRow(cr); if (useCooalessing && getCommands().size() > 0 && FoneMonkeyPlugin.getDefault().getPreferenceStore() .getString(PreferenceConstants.P_EVENTSTOCOMBINE).toLowerCase() .contains(newrow.getAction().toLowerCase())) { Command lastRow = getCommands().get(getCommands().size() - 1); if (null != lastRow && lastRow.getAction().equalsIgnoreCase(newrow.getAction()) && lastRow.getComponentType().equalsIgnoreCase(newrow.getComponentType()) && lastRow.getCommandName().equalsIgnoreCase(newrow.getCommandName()) && lastRow.getMonkeyId().equalsIgnoreCase(newrow.getMonkeyId())) { commands.set(getCommands().size() - 1, new TableRow(newrow)); doDataChanged(); } else { commands.add(newrow); doDataChanged(); } } else { commands.add(newrow); doDataChanged(); } tv.refresh(); table.setTopIndex(table.getItemCount() - 1); } /** * * @param rowToInsertAbove */ public void insertRow(int rowToInsertAbove) { TableRow c = new TableRow(); c.setAction(""); c.setArgsAndModifiers(""); c.setComponentType(""); c.setMonkeyId(""); commands.add(rowToInsertAbove, c); doDataChanged(); tv.refresh(); } /** * Delete the rows provided * * @param start * the first row to delete 0 indexed * @param end * the last row to delete 0 indexed */ public void deleteRows(int start, int end) { for (int i = end; i > start; i--) { commands.remove(i); doDataChanged(); } tv.refresh(); } /** * Delete a random assortment of rows * * @param rowsToDelete * a SORTED low to high collection of rows to be deleted */ public void deleteRows(int[] rowsToDelete) { for (int i = rowsToDelete.length - 1; i >= 0; i--) { commands.remove(rowsToDelete[i]); doDataChanged(); } tv.refresh(); } /** * Convinence function for deleting one row * * @param rowNumberthe * row to be deleted */ public void deleteRow(int rowNumber) { int[] i = new int[1]; i[0] = rowNumber; deleteRows(i); } /** * Convinence function for deleting all rows */ public void clear() { commands.removeAll(commands); doDataChanged(); tv.refresh(); } @Override public void doSave(IProgressMonitor arg0) { isDirty = false; } @Override public void doSaveAs() { isDirty = false; } @Override public void init(IEditorSite arg0, IEditorInput arg1) throws PartInitException { this.setSite(arg0); this.setInput(arg1); } @Override public void createPartControl(Composite parent) { this.createContents(parent); } boolean isDirty = false; public void doDataChanged() { isDirty = true; this.firePropertyChange(PROP_DIRTY); } @Override public boolean isDirty() { return isDirty; } public void setDirty(boolean isDirty) { this.isDirty = isDirty; this.firePropertyChange(PROP_DIRTY); } @Override public boolean isSaveAsAllowed() { // TODO Auto-generated method stub return true; } @Override public void setFocus() { // Must set focus somewhere or Project Navigator gets whacked table.setFocus(); // this.setPlaybackControlsState(); } public void deleteBlankRows(){ MonkeyTalkUtils.runOnGUI(new Runnable() { public void run() { for (int i = 0; i < commands.size(); i++) { if (commands.get(i).toString().equalsIgnoreCase("* * *")){ deleteRow(i); } } } }, getSite().getShell().getDisplay()); } public int getBlankCommandOffset(int row) { int retVal = 0; for (int i = 0; i < commands.size(); i++) { if (commands.get(i).toString().equalsIgnoreCase("* * *")){ if(i < row) retVal++; } } return retVal; } public java.util.List<Command> getCommands() { List<Command> retCommands = new ArrayList<Command>(); for (int i = 0; i < commands.size(); i++) { if (!commands.get(i).toString().equalsIgnoreCase("* * *")){ retCommands.add(commands.get(i)); } } return retCommands; } public void setCommands(java.util.List<Command> commandsparam) { commands.removeAll(commands); for (Command c : commandsparam) { commands.add(new TableRow(c)); } tv.setInput(commands); tv.refresh(); } public String getCommandsAsString() { String result = ""; for (Command c : getCommands()) { result += c.toString() + "\n"; } if (result.length() > 0) { // remove tailing newline character result = result.substring(0, result.length() - 1); } return result; } public TableViewer getTv() { return tv; } public void setTv(TableViewer tv) { this.tv = tv; } public void setSelection(int i) { tv.getTable().setSelection(i); } public void markRowAsError(int i) { // TODO Auto-generated method stub } public String[] getLimitedComponentSet() { return limitedComponentSet; } public void setLimitedComponentSet(String[] limitedComponentSet) { this.limitedComponentSet = limitedComponentSet; } }