blob: 66a7dd3326226fa00c1af7cad5d82891e5c2cec8 [file] [log] [blame]
/*
** Copyright 2011, The Android Open Source Project
**
** 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.android.glesv2debugger;
import com.android.glesv2debugger.DebuggerMessage.Message;
import com.android.glesv2debugger.DebuggerMessage.Message.Function;
import com.android.glesv2debugger.DebuggerMessage.Message.Prop;
import com.android.glesv2debugger.DebuggerMessage.Message.Type;
import com.android.sdklib.util.SparseArray;
import org.eclipse.jface.action.Action;
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.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
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.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
* This sample class demonstrates how to plug-in a new workbench view. The view
* shows data obtained from the model. The sample creates a dummy model on the
* fly, but a real implementation would connect to the model available either in
* this or another plug-in (e.g. the workspace). The view is connected to the
* model using a content provider.
* <p>
* The view uses a label provider to define how model objects should be
* presented in the view. Each view can present the same model objects using
* different labels and icons, if needed. Alternatively, a single label provider
* can be shared between views in order to ensure that objects of the same type
* are presented in the same way everywhere.
* <p>
*/
public class SampleView extends ViewPart implements Runnable, SelectionListener {
public static final ByteOrder targetByteOrder = ByteOrder.LITTLE_ENDIAN;
boolean running = false;
Thread thread;
MessageQueue messageQueue = new MessageQueue(this);
SparseArray<DebugContext> debugContexts = new SparseArray<DebugContext>();
/**
* The ID of the view as specified by the extension.
*/
public static final String ID = "glesv2debuggerclient.views.SampleView";
TabFolder tabFolder;
TabItem tabItemText, tabItemImage, tabItemBreakpointOption;
TabItem tabItemShaderEditor, tabContextViewer;
ListViewer viewer; // or TableViewer
Slider frameNum; // scale max cannot overlap min, so max is array size
TreeViewer contextViewer;
BreakpointOption breakpointOption;
ShaderEditor shaderEditor;
Canvas canvas;
Text text;
Action actionConnect; // connect / disconnect
Action actionAutoScroll;
Action actionFilter;
Action actionCapture;
Action actionPort;
Action actContext; // for toggling contexts
DebugContext current = null;
Point origin = new Point(0, 0); // for smooth scrolling canvas
String[] filters = null;
class ViewContentProvider extends LabelProvider implements IStructuredContentProvider,
ITableLabelProvider {
Frame frame = null;
@Override
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
frame = (Frame) newInput;
}
@Override
public void dispose() {
}
@Override
public Object[] getElements(Object parent) {
return frame.calls.toArray();
}
@Override
public String getText(Object obj) {
MessageData msgData = (MessageData) obj;
return msgData.text;
}
@Override
public Image getImage(Object obj) {
MessageData msgData = (MessageData) obj;
return msgData.image;
}
@Override
public String getColumnText(Object obj, int index) {
MessageData msgData = (MessageData) obj;
if (index >= msgData.columns.length)
return null;
return msgData.columns[index];
}
@Override
public Image getColumnImage(Object obj, int index) {
if (index > -1)
return null;
MessageData msgData = (MessageData) obj;
if (msgData.image == null)
return null;
return msgData.image;
}
}
class NameSorter extends ViewerSorter {
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
MessageData m1 = (MessageData) e1;
MessageData m2 = (MessageData) e2;
return (int) ((m1.msg.getTime() - m2.msg.getTime()) * 100);
}
}
class Filter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parentElement,
Object element) {
MessageData msgData = (MessageData) element;
if (null == filters)
return true;
for (int i = 0; i < filters.length; i++)
if (msgData.text.contains(filters[i]))
return true;
return false;
}
}
public SampleView() {
MessageParserEx messageParserEx = new MessageParserEx();
Message.Builder builder = Message.newBuilder();
messageParserEx.Parse(builder, "glUniform4fv(1,2,{0,1,2,3,4,5,6,7})");
messageParserEx
.Parse(builder,
"void glShaderSource(shader=4, count=1, string=\"dksjafhskjahourehghskjg\", length=0x0)");
}
public void CreateLeftPane(Composite parent) {
Composite composite = new Composite(parent, 0);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 1;
composite.setLayout(gridLayout);
frameNum = new Slider(composite, SWT.BORDER | SWT.HORIZONTAL);
frameNum.setMinimum(0);
frameNum.setMaximum(1);
frameNum.setSelection(0);
frameNum.addSelectionListener(this);
GridData gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.verticalAlignment = SWT.FILL;
frameNum.setLayoutData(gridData);
Table table = new Table(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI
| SWT.FULL_SELECTION);
TableLayout layout = new TableLayout();
table.setLayout(layout);
table.setLinesVisible(true);
table.setHeaderVisible(true);
String[] headings = {
"Name", "Elapsed (ms)", "Detail"
};
int[] weights = {
50, 16, 60
};
int[] widths = {
180, 90, 200
};
for (int i = 0; i < headings.length; i++) {
layout.addColumnData(new ColumnWeightData(weights[i], widths[i],
true));
TableColumn nameCol = new TableColumn(table, SWT.NONE, i);
nameCol.setText(headings[i]);
}
// viewer = new TableViewer(table);
viewer = new ListViewer(composite, SWT.DEFAULT);
viewer.getList().setFont(new Font(viewer.getList().getDisplay(), "Courier", 10, SWT.BOLD));
ViewContentProvider contentProvider = new ViewContentProvider();
viewer.setContentProvider(contentProvider);
viewer.setLabelProvider(contentProvider);
// viewer.setSorter(new NameSorter());
viewer.setFilters(new ViewerFilter[] {
new Filter()
});
gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.verticalAlignment = SWT.FILL;
gridData.grabExcessVerticalSpace = true;
viewer.getControl().setLayoutData(gridData);
}
/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
@Override
public void createPartControl(Composite parent) {
CreateLeftPane(parent);
// Create the help context id for the viewer's control
PlatformUI.getWorkbench().getHelpSystem()
.setHelp(viewer.getControl(), "GLESv2DebuggerClient.viewer");
tabFolder = new TabFolder(parent, SWT.BORDER);
text = new Text(tabFolder, SWT.NO_BACKGROUND | SWT.READ_ONLY
| SWT.V_SCROLL | SWT.H_SCROLL);
tabItemText = new TabItem(tabFolder, SWT.NONE);
tabItemText.setText("Text");
tabItemText.setControl(text);
canvas = new Canvas(tabFolder, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE
| SWT.V_SCROLL | SWT.H_SCROLL);
tabItemImage = new TabItem(tabFolder, SWT.NONE);
tabItemImage.setText("Image");
tabItemImage.setControl(canvas);
breakpointOption = new BreakpointOption(this, tabFolder);
tabItemBreakpointOption = new TabItem(tabFolder, SWT.NONE);
tabItemBreakpointOption.setText("Breakpoint Option");
tabItemBreakpointOption.setControl(breakpointOption);
shaderEditor = new ShaderEditor(this, tabFolder);
tabItemShaderEditor = new TabItem(tabFolder, SWT.NONE);
tabItemShaderEditor.setText("Shader Editor");
tabItemShaderEditor.setControl(shaderEditor);
contextViewer = new TreeViewer(tabFolder);
ContextViewProvider contextViewProvider = new ContextViewProvider(this);
contextViewer.addSelectionChangedListener(contextViewProvider);
contextViewer.setContentProvider(contextViewProvider);
contextViewer.setLabelProvider(contextViewProvider);
tabContextViewer = new TabItem(tabFolder, SWT.NONE);
tabContextViewer.setText("Context Viewer");
tabContextViewer.setControl(contextViewer.getTree());
final ScrollBar hBar = canvas.getHorizontalBar();
hBar.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
if (null == canvas.getBackgroundImage())
return;
Image image = canvas.getBackgroundImage();
int hSelection = hBar.getSelection();
int destX = -hSelection - origin.x;
Rectangle rect = image.getBounds();
canvas.scroll(destX, 0, 0, 0, rect.width, rect.height, false);
origin.x = -hSelection;
}
});
final ScrollBar vBar = canvas.getVerticalBar();
vBar.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
if (null == canvas.getBackgroundImage())
return;
Image image = canvas.getBackgroundImage();
int vSelection = vBar.getSelection();
int destY = -vSelection - origin.y;
Rectangle rect = image.getBounds();
canvas.scroll(0, destY, 0, 0, rect.width, rect.height, false);
origin.y = -vSelection;
}
});
canvas.addListener(SWT.Resize, new Listener() {
@Override
public void handleEvent(Event e) {
if (null == canvas.getBackgroundImage())
return;
Image image = canvas.getBackgroundImage();
Rectangle rect = image.getBounds();
Rectangle client = canvas.getClientArea();
hBar.setMaximum(rect.width);
vBar.setMaximum(rect.height);
hBar.setThumb(Math.min(rect.width, client.width));
vBar.setThumb(Math.min(rect.height, client.height));
int hPage = rect.width - client.width;
int vPage = rect.height - client.height;
int hSelection = hBar.getSelection();
int vSelection = vBar.getSelection();
if (hSelection >= hPage) {
if (hPage <= 0)
hSelection = 0;
origin.x = -hSelection;
}
if (vSelection >= vPage) {
if (vPage <= 0)
vSelection = 0;
origin.y = -vSelection;
}
canvas.redraw();
}
});
canvas.addListener(SWT.Paint, new Listener() {
@Override
public void handleEvent(Event e) {
if (null == canvas.getBackgroundImage())
return;
Image image = canvas.getBackgroundImage();
GC gc = e.gc;
gc.drawImage(image, origin.x, origin.y);
Rectangle rect = image.getBounds();
Rectangle client = canvas.getClientArea();
int marginWidth = client.width - rect.width;
if (marginWidth > 0) {
gc.fillRectangle(rect.width, 0, marginWidth, client.height);
}
int marginHeight = client.height - rect.height;
if (marginHeight > 0) {
gc.fillRectangle(0, rect.height, client.width, marginHeight);
}
}
});
hookContextMenu();
hookSelectionChanged();
contributeToActionBars();
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
SampleView.this.fillContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(menuMgr, viewer);
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalPullDown(IMenuManager manager) {
// manager.add(actionConnect);
// manager.add(new Separator());
// manager.add(actionDisconnect);
}
private void fillContextMenu(IMenuManager manager) {
// manager.add(actionConnect);
// manager.add(actionDisconnect);
// Other plug-ins can contribute there actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void fillLocalToolBar(final IToolBarManager manager) {
actionConnect = new Action("Connect", Action.AS_PUSH_BUTTON) {
@Override
public void run() {
ConnectDisconnect();
}
};
manager.add(actionConnect);
manager.add(new Action("Open File", Action.AS_PUSH_BUTTON)
{
@Override
public void run()
{
if (!running)
OpenFile();
}
});
final Shell shell = this.getViewSite().getShell();
actionAutoScroll = new Action("Auto Scroll", Action.AS_CHECK_BOX) {
@Override
public void run() {
}
};
actionAutoScroll.setChecked(true);
manager.add(actionAutoScroll);
actionFilter = new Action("*", Action.AS_DROP_DOWN_MENU) {
@Override
public void run() {
org.eclipse.jface.dialogs.InputDialog dialog = new org.eclipse.jface.dialogs.InputDialog(
shell, "Contains Filter",
"case sensitive substring or *",
actionFilter.getText(), null);
if (Window.OK == dialog.open()) {
actionFilter.setText(dialog.getValue());
manager.update(true);
filters = dialog.getValue().split("\\|");
if (filters.length == 1 && filters[0].equals("*"))
filters = null;
viewer.refresh();
}
}
};
manager.add(actionFilter);
actionCapture = new Action("Capture", Action.AS_CHECK_BOX) {
@Override
public void run() {
Message.Builder builder = Message.newBuilder();
builder.setContextId(0); // FIXME: proper context id
builder.setType(Type.Response);
builder.setExpectResponse(false);
builder.setFunction(Function.SETPROP);
builder.setProp(Prop.Capture);
builder.setArg0(isChecked() ? 1 : 0);
messageQueue.AddCommand(builder.build());
manager.update(true);
}
};
actionCapture.setChecked(false);
manager.add(actionCapture);
manager.add(new Action("SYSTEM_TIME_THREAD", Action.AS_DROP_DOWN_MENU)
{
@Override
public void run()
{
final String[] timeModes = {
"SYSTEM_TIME_REALTIME", "SYSTEM_TIME_MONOTONIC", "SYSTEM_TIME_PROCESS",
"SYSTEM_TIME_THREAD"
};
int i = java.util.Arrays.asList(timeModes).indexOf(this.getText());
i = (i + 1) % timeModes.length;
Message.Builder builder = Message.newBuilder();
builder.setContextId(0); // FIXME: proper context id
builder.setType(Type.Response);
builder.setExpectResponse(false);
builder.setFunction(Message.Function.SETPROP);
builder.setProp(Prop.TimeMode);
builder.setArg0(i);
messageQueue.AddCommand(builder.build());
this.setText(timeModes[i]);
manager.update(true);
}
});
actContext = new Action("Context: 0x", Action.AS_DROP_DOWN_MENU) {
@Override
public void run() {
if (debugContexts.size() < 2)
return;
final String idStr = this.getText().substring(
"Context: 0x".length());
if (idStr.length() == 0)
return;
final int contextId = Integer.parseInt(idStr, 16);
int index = debugContexts.indexOfKey(contextId);
index = (index + 1) % debugContexts.size();
ChangeContext(debugContexts.valueAt(index));
}
};
manager.add(actContext);
actionPort = new Action("5039", Action.AS_DROP_DOWN_MENU)
{
@Override
public void run() {
InputDialog dialog = new InputDialog(shell, "Port", "Debugger port",
actionPort.getText(), null);
if (Window.OK == dialog.open()) {
actionPort.setText(dialog.getValue());
manager.update(true);
}
}
};
manager.add(actionPort);
manager.add(new Action("CodeGen Frame", Action.AS_PUSH_BUTTON)
{
@Override
public void run()
{
if (current != null)
new CodeGen().CodeGenFrame((Frame) viewer.getInput());
}
});
manager.add(new Action("CodeGen Frames", Action.AS_PUSH_BUTTON)
{
@Override
public void run()
{
if (current != null)
new CodeGen().CodeGenFrames(current, frameNum.getSelection() + 1);
}
});
}
private void OpenFile() {
FileDialog dialog = new FileDialog(getSite().getShell(), SWT.OPEN);
dialog.setText("Open");
dialog.setFilterExtensions(new String[] {
"*.gles2dbg"
});
String filePath = dialog.open();
if (filePath == null)
return;
FileInputStream file = null;
try {
file = new FileInputStream(filePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
running = true;
messageQueue.Start(targetByteOrder, file);
thread = new Thread(this);
thread.start();
actionConnect.setText("Disconnect");
getViewSite().getActionBars().getToolBarManager().update(true);
}
private void ConnectDisconnect() {
if (!running) {
running = true;
messageQueue.Start(targetByteOrder, null);
thread = new Thread(this);
thread.start();
actionConnect.setText("Disconnect");
} else {
running = false;
messageQueue.Stop();
actionConnect.setText("Connect");
}
this.getSite().getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
getViewSite().getActionBars().getToolBarManager().update(true);
}
});
}
void MessageDataSelected(final MessageData msgData) {
if (null == msgData)
return;
if (frameNum.getSelection() == frameNum.getMaximum())
return; // scale max cannot overlap min, so max is array size
final Frame frame = current.frames.get(frameNum.getSelection());
final Context context = current.ComputeContext(frame, msgData);
contextViewer.setInput(context);
if (null != msgData.image) {
canvas.setBackgroundImage(msgData.image);
tabFolder.setSelection(tabItemImage);
canvas.redraw();
} else if (null != msgData.shader) {
text.setText(msgData.shader);
tabFolder.setSelection(tabItemText);
} else if (null != msgData.data) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < msgData.data.length; i++) {
builder.append(String.format("%.3g", msgData.data[i]));
if (i % (4 * msgData.maxAttrib) == (4 * msgData.maxAttrib - 1))
builder.append('\n');
else if (i % 4 == 3)
builder.append(" -");
if (i < msgData.data.length - 1)
builder.append(' ');
}
text.setText(builder.toString());
tabFolder.setSelection(tabItemText);
}
}
private void hookSelectionChanged() {
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
StructuredSelection selection = (StructuredSelection) event
.getSelection();
if (null == selection)
return;
MessageData msgData = (MessageData) selection.getFirstElement();
MessageDataSelected(msgData);
}
});
}
public void showError(final Exception e) {
viewer.getControl().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
MessageDialog.openError(viewer.getControl().getShell(),
"GL ES 2.0 Debugger Client", e.getMessage());
}
});
}
/**
* Passing the focus request to the viewer's control.
*/
@Override
public void setFocus() {
viewer.getControl().setFocus();
}
@Override
public void run() {
FileWriter file = null;
PrintWriter writer = null;
try {
file = new FileWriter("GLES2Debugger.log", true);
writer = new PrintWriter(file);
writer.write("\n\n");
writer.write(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance()
.getTime()));
writer.write("\n\n");
} catch (IOException e1) {
showError(e1);
}
int newMessages = 0;
boolean shaderEditorUpdate = false, currentUpdate = false;
while (running) {
final Message oriMsg = messageQueue.RemoveCompleteMessage(0);
if (oriMsg == null && !messageQueue.IsRunning())
break;
if (newMessages > 60 || (newMessages > 0 && null == oriMsg)) {
newMessages = 0;
if (currentUpdate || current == null)
getSite().getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (current == null)
ChangeContext(debugContexts.valueAt(0));
else if (frameNum.getSelection() == current.frames.size() - 1)
{
viewer.refresh(false);
if (actionAutoScroll.isChecked())
viewer.getList().setSelection(
viewer.getList().getItemCount() - 1);
}
frameNum.setMaximum(current.frames.size());
}
});
currentUpdate = false;
if (shaderEditorUpdate)
this.getSite().getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
shaderEditor.Update();
}
});
shaderEditorUpdate = false;
}
if (null == oriMsg) {
try {
Thread.sleep(1);
continue;
} catch (InterruptedException e) {
showError(e);
}
}
DebugContext debugContext = debugContexts.get(oriMsg.getContextId());
if (debugContext == null) {
debugContext = new DebugContext(oriMsg.getContextId());
debugContexts.put(oriMsg.getContextId(), debugContext);
}
final MessageData msgData = debugContext.ProcessMessage(oriMsg);
if (current == debugContext) {
currentUpdate = true;
}
shaderEditorUpdate |= debugContext.currentContext.serverShader.uiUpdate;
debugContext.currentContext.serverShader.uiUpdate = false;
if (null != writer) {
writer.write(msgData.text + "\n");
if (msgData.msg.getFunction() == Function.eglSwapBuffers) {
writer.write("\n-------\n");
writer.flush();
}
}
newMessages++;
}
if (running)
ConnectDisconnect(); // error occurred, disconnect
if (null != writer) {
writer.flush();
writer.close();
}
}
/** can be called from non-UI thread */
void ChangeContext(final DebugContext newContext) {
getSite().getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
current = newContext;
frameNum.setMaximum(current.frames.size());
frameNum.setSelection(0);
viewer.setInput(current.frames.get(frameNum.getSelection()));
shaderEditor.Update();
actContext.setText("Context: 0x" + Integer.toHexString(current.contextId));
getViewSite().getActionBars().getToolBarManager().update(true);
}
});
}
@Override
public void widgetSelected(SelectionEvent e) {
if (e.widget != frameNum)
assert false;
if (current == null)
return;
if (frameNum.getSelection() == current.frames.size())
return; // scale maximum cannot overlap minimum
Frame frame = current.frames.get(frameNum.getSelection());
viewer.setInput(frame);
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
}