/*******************************************************************************
* Copyright (c) 2009, 2016 Wind River Systems, Inc. and others.
* 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.commands;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
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.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.tcf.internal.debug.launch.TCFLaunchDelegate;
import org.eclipse.tcf.internal.debug.model.TCFLaunch;
import org.eclipse.tcf.internal.debug.ui.Activator;
import org.eclipse.tcf.internal.debug.ui.ImageCache;
import org.eclipse.tcf.internal.debug.ui.model.TCFChildren;
import org.eclipse.tcf.internal.debug.ui.model.TCFModel;
import org.eclipse.tcf.internal.debug.ui.model.TCFNode;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext.SignalMask;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.services.IProcesses;
import org.eclipse.tcf.util.TCFDataCache;
import org.eclipse.tcf.util.TCFTask;
class SignalsDialog extends Dialog {
private static final String SETTINGS_SECTION = SignalsDialog.class.getCanonicalName();
private static final int
SIZING_TABLE_WIDTH = 800,
SIZING_TABLE_HEIGHT = 300;
private static final String[] column_names = {
"Code",
"Name",
"Description",
"Don't stop",
"Don't pass",
"Pending"
};
private static class Signal extends SignalMask {
Signal(Map<String,Object> m) {
props = m;
}
Signal(SignalMask m) {
props = m.getProperties();
dont_stop = m.isDontStop();
dont_pass = m.isDontPass();
pending = m.isPending();
}
void setDontStop(boolean b) {
dont_stop = b;
}
void setDontPass(boolean b) {
dont_pass = b;
}
void setPending(boolean b) {
pending = b;
}
}
private Table signal_table;
private TableViewer table_viewer;
private Map<Number,Signal> org_signals;
private Signal[] cur_signals;
private final TCFModel model;
private final IChannel channel;
private final TCFNode selection;
private TCFNodeExecContext node;
private final IStructuredContentProvider content_provider = new IStructuredContentProvider() {
public Object[] getElements(Object input) {
return cur_signals;
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
};
private static class SignalLabelProvider extends LabelProvider implements ITableLabelProvider {
final Image img_rs = ImageCache.getImage("icons/full/elcl16/resume_co.gif");
final Image img_dl = ImageCache.getImage("icons/full/elcl16/rem_co.gif");
final Image img_en = ImageCache.getImage("icons/full/elcl16/enabled_co.gif");
final Image img_ds = ImageCache.getImage("icons/full/elcl16/disabled_co.gif");
public Image getColumnImage(Object element, int column) {
SignalMask s = (SignalMask)element;
switch (column) {
case 3:
return s.isDontStop() ? img_rs : img_ds;
case 4:
return s.isDontPass() ? img_dl : img_ds;
case 5:
return s.isPending() ? img_en : img_ds;
}
return null;
}
public String getColumnText(Object element, int column) {
SignalMask s = (SignalMask)element;
switch (column) {
case 0:
long n = s.getCode().longValue();
if (n < 256) return Long.toString(n);
String q = Long.toHexString(n);
if (q.length() < 8) q = "00000000".substring(q.length()) + q;
return "0x" + q;
case 1:
return (String)s.getProperties().get(IProcesses.SIG_NAME);
case 2:
return (String)s.getProperties().get(IProcesses.SIG_DESCRIPTION);
}
return "";
}
public String getText(Object element) {
return element.toString();
}
}
SignalsDialog(Shell parent, TCFNode node) {
super(parent);
model = node.getModel();
channel = node.getChannel();
selection = node;
}
@Override
protected boolean isResizable() {
return true;
}
@Override
protected IDialogSettings getDialogBoundsSettings() {
IDialogSettings settings = Activator.getDefault().getDialogSettings();
IDialogSettings section = settings.getSection(SETTINGS_SECTION);
if (section != null) return section;
return settings.addNewSection(SETTINGS_SECTION);
}
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("Signals");
shell.setImage(ImageCache.getImage(ImageCache.IMG_SIGNALS));
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite)super.createDialogArea(parent);
createSignalTable(composite);
composite.setSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
return composite;
}
private void createSignalTable(Composite parent) {
Font font = parent.getFont();
Label props_label = new Label(parent, SWT.WRAP);
props_label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
props_label.setFont(font);
props_label.setText("&Signals:");
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
composite.setFont(font);
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true, 2, 1));
signal_table = new Table(composite,
SWT.SINGLE | SWT.BORDER |
SWT.H_SCROLL | SWT.V_SCROLL);
signal_table.setFont(font);
GridData data = new GridData(GridData.FILL_BOTH);
data.widthHint = SIZING_TABLE_WIDTH;
data.heightHint = SIZING_TABLE_HEIGHT;
signal_table.setLayoutData(data);
int w = SIZING_TABLE_WIDTH / (column_names.length + 5);
for (int i = 0; i < column_names.length; i++) {
final TableColumn column = new TableColumn(signal_table, SWT.LEAD, i);
column.setMoveable(false);
column.setText(column_names[i]);
switch (i) {
case 0:
column.setWidth(w * 2);
break;
case 1:
case 2:
column.setWidth(w * 3);
break;
default:
column.setWidth(w);
break;
}
}
signal_table.setHeaderVisible(true);
signal_table.setLinesVisible(true);
signal_table.addMouseListener(new MouseListener() {
public void mouseDoubleClick(MouseEvent e) {
}
public void mouseDown(MouseEvent e) {
int count = signal_table.getColumnCount();
for (int row = 0; row < signal_table.getItemCount(); row++) {
for (int col = 0; col < count; col++) {
TableItem item = signal_table.getItem(row);
if (item.getBounds(col).contains(e.x, e.y)) {
if (row < 0 || row >= cur_signals.length) break;
Signal s = cur_signals[row];
switch (col) {
case 3:
s.setDontStop(!s.isDontStop());
break;
case 4:
s.setDontPass(!s.isDontPass());
break;
case 5:
if (node == null) break;
if (s.isPending()) {
// Cannot clear a signal that is already generated
Signal x = org_signals.get(s.getIndex());
if (x != null && x.isPending()) break;
}
s.setPending(!s.isPending());
break;
}
table_viewer.refresh(s);
break;
}
}
}
}
public void mouseUp(MouseEvent e) {
}
});
table_viewer = new TableViewer(signal_table);
table_viewer.setUseHashlookup(true);
table_viewer.setColumnProperties(column_names);
cur_signals = new TCFTask<Signal[]>(channel) {
public void run() {
TCFNode n = selection;
while (n != null && !(n instanceof TCFNodeExecContext)) n = n.getParent();
node = (TCFNodeExecContext)n;
if (node == null) {
TCFLaunch launch = model.getLaunch();
Collection<Map<String,Object>> sigs = launch.getSignalList();
if (sigs == null) {
done(new Signal[0]);
}
else {
int i = 0;
Set<Integer> no_stop = new HashSet<Integer>();
Set<Integer> no_pass = new HashSet<Integer>();
Signal[] arr = new Signal[sigs.size()];
try {
ILaunchConfiguration cfg = launch.getLaunchConfiguration();
no_stop = TCFLaunchDelegate.readSigSet(cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_STOP, ""));
no_pass = TCFLaunchDelegate.readSigSet(cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_PASS, ""));
}
catch (Exception x) {
Activator.log("Invalid launch cofiguration attribute", x);
}
for (Map<String,Object> m : sigs) {
Signal s = arr[i++] = new Signal(m);
Number num = s.getIndex();
int j = num == null ? 0 : 1 << num.intValue();
s.setDontStop(no_stop.contains(j));
s.setDontPass(no_pass.contains(j));
}
done(arr);
}
}
else {
TCFDataCache<SignalMask[]> dc = node.getSignalMask();
if (!dc.validate(this)) return;
if (dc.getError() != null) {
error(dc.getError());
}
else if (dc.getData() == null) {
done(new Signal[0]);
}
else {
int i = 0;
Signal[] arr = new Signal[dc.getData().length];
for (SignalMask m : dc.getData()) arr[i++] = new Signal(m);
done(arr);
}
}
}
}.getE();
org_signals = new HashMap<Number,Signal>();
for (Signal m : cur_signals) org_signals.put(m.getIndex(), new Signal(m));
table_viewer.setContentProvider(content_provider);
table_viewer.setLabelProvider(new SignalLabelProvider());
table_viewer.setInput(this);
}
@Override
protected void okPressed() {
try {
boolean set_mask = false;
Set<Integer> dont_stop_set = new HashSet<Integer>();
Set<Integer> dont_pass_set = new HashSet<Integer>();
final LinkedList<Number> send_list = new LinkedList<Number>();
for (Signal s : cur_signals) {
Number index = s.getIndex();
Signal x = org_signals.get(index);
if (!set_mask) set_mask = x == null || x.isDontStop() != s.isDontStop() || x.isDontPass() != s.isDontPass();
if (s.isDontStop()) dont_stop_set.add(index.intValue());
if (s.isDontPass()) dont_pass_set.add(index.intValue());
if ((x == null || !x.isPending()) && s.isPending()) send_list.add(s.getCode());
}
if (set_mask) {
TCFLaunch launch = model.getLaunch();
ILaunchConfigurationWorkingCopy cfg = launch.getLaunchConfiguration().getWorkingCopy();
cfg.setAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_STOP, TCFLaunchDelegate.writeSigSet(dont_stop_set));
cfg.setAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_PASS, TCFLaunchDelegate.writeSigSet(dont_pass_set));
cfg.doSave();
final Set<Integer> dont_stop = dont_stop_set;
final Set<Integer> dont_pass = dont_pass_set;
new TCFTask<Boolean>(channel) {
final HashSet<IToken> cmds = new HashSet<IToken>();
final LinkedList<TCFNodeExecContext> nodes = new LinkedList<TCFNodeExecContext>();
TCFDataCache<?> pending;
public void run() {
nodes.clear();
pending = null;
addNodes(model.getRootNode().getChildren());
if (pending != null) {
pending.wait(this);
}
else {
while (nodes.size() > 0) {
TCFNodeExecContext exe = nodes.removeFirst();
exe.getSignalMask().reset();
IProcesses prs = channel.getRemoteService(IProcesses.class);
cmds.add(prs.setSignalMask(exe.getID(), dont_stop, dont_pass, new IProcesses.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
cmds.remove(token);
if (isDone()) return;
if (error != null) error(error);
if (cmds.size() == 0) done(Boolean.TRUE);
}
}));
}
}
}
private void addNodes(TCFChildren children) {
if (!children.validate()) {
pending = children;
}
else {
Map<String,TCFNode> map = children.getData();
if (map != null) {
for (TCFNode n : map.values()) {
TCFNodeExecContext exe = (TCFNodeExecContext)n;
addNodes(exe.getChildren());
nodes.add(exe);
}
}
}
}
}.getE();
}
if (send_list.size() > 0 && node != null) {
new TCFTask<Boolean>(channel) {
public void run() {
node.getSignalMask().reset();
final IProcesses prs = channel.getRemoteService(IProcesses.class);
prs.signal(node.getID(), send_list.removeFirst().longValue(), new IProcesses.DoneCommand() {
public void doneCommand(IToken token, Exception error) {
if (error != null) {
error(error);
}
else if (send_list.isEmpty()) {
done(Boolean.TRUE);
}
else {
node.getSignalMask().reset();
prs.signal(node.getID(), send_list.removeFirst().longValue(), this);
}
}
});
}
}.getE();
}
}
catch (Throwable x) {
model.showMessageBox("Cannot update signals state", x);
return;
}
super.okPressed();
}
}