/* CpuInstructionTextCanvasComposite.java (c) 2012-2015 Edward Swartz 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 */ package v9t9.gui.client.swt.shells.debugger; import java.util.List; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.resource.FontDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.LineStyleEvent; import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.MenuDetectEvent; import org.eclipse.swt.events.MenuDetectListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.TextStyle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.ScrollBar; import org.ejs.gui.common.FontUtils; import org.ejs.gui.common.SwtUtils; import v9t9.common.cpu.BreakpointManager; import v9t9.common.cpu.IBreakpoint; import v9t9.common.cpu.IBreakpointListener; import v9t9.common.cpu.IInstructionEffectLabelProvider.Column; import v9t9.common.machine.IMachine; /** * @author ejs * */ public class CpuInstructionTextCanvasComposite extends CpuInstructionComposite implements IBreakpointListener { private StyledText text; private int numRows; private Runnable refreshTask; private TextStyle baseTextStyle; private TextStyle bg1Style; private TextStyle bg2Style; private TextStyle bpStyle; private InstLabelProvider instLabelProvider; private Font smallerFont; private int topRow; public CpuInstructionTextCanvasComposite(Composite parent, int style, IMachine machine) { super(parent, style | SWT.V_SCROLL, machine); this.instLabelProvider = new InstLabelProvider(machine.getCpu(), machine.getCpu().createInstructionEffectLabelProvider()); GridLayoutFactory.fillDefaults().applyTo(this); text = new StyledText(this, SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL); GridDataFactory.fillDefaults().grab(true, true).applyTo(text); FontDescriptor fontDescriptor = FontUtils.getFontDescriptor(JFaceResources.getTextFont()); FontDescriptor smallerFontDescriptor = fontDescriptor.increaseHeight(-2); smallerFont = smallerFontDescriptor.createFont(getDisplay()); baseTextStyle = new TextStyle(smallerFont, null, null); bg1Style = new TextStyle(baseTextStyle); bg1Style.background = getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); bg2Style = new TextStyle(baseTextStyle); bg2Style.background = getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW); bpStyle = new TextStyle(baseTextStyle); bpStyle.background = getDisplay().getSystemColor(SWT.COLOR_RED); text.addLineStyleListener(new LineStyleListener() { @Override public void lineGetStyle(LineStyleEvent event) { StyleRange[] ranges = layoutStyles(event.lineOffset); event.styles = ranges; } }); text.addKeyListener(new KeyAdapter() { public void keyPressed(org.eclipse.swt.events.KeyEvent e) { //System.out.println(text.getParent().getVerticalBar().getThumb()); ScrollBar bar = text.getParent().getVerticalBar(); if (e.keyCode == SWT.PAGE_DOWN) { bar.setSelection(bar.getSelection() + bar.getPageIncrement()); } else if (e.keyCode == SWT.PAGE_UP) { bar.setSelection(bar.getSelection() - bar.getPageIncrement()); } else if (e.keyCode == SWT.ARROW_DOWN) { bar.setSelection(bar.getSelection() + 1); } else if (e.keyCode == SWT.ARROW_UP) { bar.setSelection(bar.getSelection() - 1); } else if (e.keyCode == SWT.END && (e.stateMask & SWT.CTRL) != 0) { bar.setSelection(bar.getMaximum()); } else if (e.keyCode == SWT.HOME && (e.stateMask & SWT.CTRL) != 0) { bar.setSelection(bar.getMinimum()); } else { return; } redrawLines(); e.doit = false; } }); text.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { redrawLines(); } }); start(); } protected StyleRange[] layoutStyles(int offset) { int rowN = topRow + text.getLineAtOffset(offset); synchronized (instHistory) { // mark breakpoints if (rowN >= 0 && rowN < instHistory.size()) { InstRow row = instHistory.get(rowN); IBreakpoint bp = machine.getExecutor().getBreakpoints().findBreakpoint( row.getInst().pc & 0xffff); if (bp != null) { StyleRange range = new StyleRange(bpStyle); range.start = offset; range.length = text.getLine(rowN - topRow).length(); return new StyleRange[] { range }; } } } // mark line style StyleRange[] ranges = new StyleRange[instLabelProvider.getColumnCount()]; boolean oddOp = true; int rangeIdx = 0; for (Column column : instLabelProvider.getColumns()) { TextStyle style; switch (column.role) { case UNKNOWN: case INSTRUCTION: case SYMBOL: default: style = baseTextStyle; break; case INPUT: case ADDRESS: style = bg1Style; break; case OUTPUT: style = bg2Style; break; case OPERAND: style = oddOp ? bg2Style : bg1Style; oddOp = !oddOp; break; } ranges[rangeIdx] = new StyleRange(style); ranges[rangeIdx].start = offset; ranges[rangeIdx].length = column.width + 2 + (rangeIdx == 0 ? 2 : 0); // implicit breakpoint column offset += ranges[rangeIdx].length; rangeIdx++; } return ranges; } /* (non-Javadoc) * @see v9t9.gui.client.swt.shells.debugger.CpuInstructionComposite#setupEvents() */ @Override public void setupEvents() { text.addListener(SWT.Resize, new Listener() { /* (non-Javadoc) * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) */ @Override public void handleEvent(Event event) { text.setFont(smallerFont); int height = text.getClientArea().height; numRows = Math.max(1, height / text.getLineHeight()); getVerticalBar().setIncrement(1); getVerticalBar().setPageIncrement(numRows); } }); text.addMenuDetectListener(new MenuDetectListener() { @Override public void menuDetected(MenuDetectEvent e) { Point pt = getDisplay().map(null, text, e.x, e.y); int rowN = pt.y / text.getLineHeight() + topRow; synchronized (instHistory) { if (rowN >= 0 && rowN < instHistory.size()) { InstRow row = instHistory.get(rowN); System.out.println(row.getInst()); final int pc = row.getInst().pc & 0xffff; Menu menu = new Menu(text); DebuggerWindow.addBreakpointActions(machine, menu, pc); SwtUtils.runMenu(null, e.x, e.y, menu); } } } }); getVerticalBar().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { redrawLines(); } }); machine.getExecutor().getBreakpoints().addListener(CpuInstructionTextCanvasComposite.this); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { machine.getFastMachineTimer().cancelTask(refreshTask); machine.getExecutor().getBreakpoints().removeListener(CpuInstructionTextCanvasComposite.this); smallerFont.dispose(); } }); } /* (non-Javadoc) * @see v9t9.gui.client.swt.shells.debugger.CpuInstructionComposite#go() */ @Override public void go() { } /* (non-Javadoc) * @see v9t9.gui.client.swt.shells.debugger.CpuInstructionComposite#refresh() */ @Override public void flush() { synchronized (instHistory) { redrawLines(); getVerticalBar().setMaximum(instHistory.size()); getVerticalBar().setSelection(instHistory.size() - numRows + 1); } } /** * */ private void redrawLines() { int rowIndex = getVerticalBar().getSelection(); synchronized (text) { synchronized (instHistory) { final BreakpointManager bpMgr = machine.getExecutor().getBreakpoints(); StringBuilder sb = new StringBuilder(); int numCols = instLabelProvider.getColumnCount(); Column[] cols = instLabelProvider.getColumns(); int visible = numRows; int start = Math.min(rowIndex, instHistory.size()); int end = Math.min(rowIndex + visible, instHistory.size()); List<InstRow> subList = instHistory.subList(start, end); topRow = rowIndex; for (InstRow row : subList) { IBreakpoint bp = bpMgr.findBreakpoint(row.getInst().pc); if (bp == null) { sb.append(" "); } else { sb.append("• "); } for (int i = 0; i < numCols; i++) { String field = fieldOf(instLabelProvider.getColumnText(row, i), cols[i].width); sb.append(' '); sb.append(field); sb.append(' '); } sb.append('\n'); } while (end - start < numRows) { for (int i = 0; i < numCols; i++) { sb.append(fieldOf("", cols[i].width + 2)); } sb.append('\n'); end++; } text.setText(sb.toString()); } } //text.redraw(); } /** * @param op3 * @param i * @return */ private String fieldOf(String str, int len) { if (str == null) str = ""; if (str.length() == len) return str; if (str.length() < len) { while (str.length() < len) { str += " "; } } if (str.length() > len) { return str.substring(0, len); } return str; } /* (non-Javadoc) * @see v9t9.common.cpu.IBreakpointListener#breakpointAdded(v9t9.common.cpu.IBreakpoint) */ @Override public void breakpointChanged(IBreakpoint bp, boolean added) { Display.getDefault().asyncExec(new Runnable() { public void run() { redrawLines(); } }); } }