/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) 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:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.ui.editors.targets;
import java.text.MessageFormat;
import java.util.Arrays;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import de.innot.avreclipse.core.targets.HostInterface;
import de.innot.avreclipse.core.targets.ITargetConfigConstants;
import de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy;
/**
* FormPart to edit all settings for the current host interface.
*
* @author Thomas Holland
* @since 2.4
*
*/
public class SectionHostInterface extends AbstractTCSectionPart implements ITargetConfigConstants {
/** Used as the default string for Port / Baudrate settings. */
private final static String DEFAULT = "default";
/** List of common baudrates incl. DEFAULT */
private final static String[] BAUDRATES = new String[] { //
//
DEFAULT, //
"1200", //
"2400", //
"4800", //
"9600", //
"19200", //
"38400", //
"57600", //
"115200", //
"230400" };
/** The list of target configuration attributes that this part manages. */
private final static String[] PART_ATTRS = new String[] { //
//
ATTR_PROGRAMMER_PORT, //
ATTR_PROGRAMMER_BAUD, //
ATTR_BITBANGDELAY, //
ATTR_PAR_EXITSPEC, //
ATTR_USB_DELAY };
/** The list of target configuration attributes that cause this part to refresh. */
private final static String[] PART_DEPENDS = new String[] { ATTR_HOSTINTERFACE };
/** the client area of the Section created by the superclass. */
private Composite fSectionClient;
/** The current exit spec for the /Reset line. */
private String fExitSpecReset;
/** The current exit spec for the Vcc line. */
private String fExitSpecVcc;
/*
* (non-Javadoc)
* @see de.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#getTitle()
*/
@Override
protected String getTitle() {
// This is just a placeholder dummy.
// The real name will be set in the refreshSectionContent() method.
return "Host Interface";
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#getDescription()
*/
@Override
protected String getDescription() {
return null; // TODO: add a description
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#getPartAttributes
* ()
*/
@Override
public String[] getPartAttributes() {
return Arrays.copyOf(PART_ATTRS, PART_ATTRS.length);
}
/*
* (non-Javadoc)
* @seede.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#
* getDependentAttributes()
*/
@Override
protected String[] getDependentAttributes() {
return PART_DEPENDS;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#getSectionStyle
* ()
*/
@Override
protected int getSectionStyle() {
return Section.TWISTIE | Section.SHORT_TITLE_BAR | Section.EXPANDED | Section.CLIENT_INDENT;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
*/
@Override
public void createSectionContent(Composite parent, FormToolkit toolkit) {
TableWrapLayout layout = new TableWrapLayout();
layout.numColumns = 2;
layout.horizontalSpacing = 12;
parent.setLayout(layout);
fSectionClient = parent;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#refresh()
*/
@Override
public void refreshSectionContent() {
final ITargetConfigurationWorkingCopy tcwc = getTargetConfiguration();
// Get the required information from the target configuration
String newHItxt = tcwc.getAttribute(ATTR_HOSTINTERFACE);
HostInterface newHI = HostInterface.valueOf(newHItxt);
//
// Clear the old section content
//
// remove all previous controls from the section
Control[] children = fSectionClient.getChildren();
for (Control child : children) {
child.dispose();
}
fSectionClient.layout(true, true);
// Finally reflow the form. Otherwise layout artifacts may remain behind.
getManagedForm().reflow(true);
//
// redraw the complete section.
//
String title = MessageFormat.format("{0} Settings", newHI.toString());
getControl().setText(title);
// rebuild the content
FormToolkit toolkit = getManagedForm().getToolkit();
Composite portCompo = toolkit.createComposite(fSectionClient);
portCompo
.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP, 1, 2));
portCompo.setLayout(new GridLayout(2, false));
// All host interfaces have a port name.
addPortCombo(portCompo, toolkit, newHI);
Control section;
// Add the host interface specific sections
switch (newHI) {
case SERIAL:
addBaudRateCombo(portCompo, toolkit);
break;
case SERIAL_BB:
section = addBitBangDelaySection(fSectionClient, toolkit);
section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP,
1, 2));
break;
case PARALLEL:
section = addBitBangDelaySection(fSectionClient, toolkit);
section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP,
1, 2));
section = addExitSpecsSection(fSectionClient, toolkit);
section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP,
1, 2));
break;
case USB:
section = addUSBDelaySection(fSectionClient, toolkit);
section
.setLayoutData(new TableWrapData(TableWrapData.FILL, TableWrapData.TOP, 1,
2));
break;
}
}
/**
* Add the port name settings controls to the parent.
* <p>
* This consists of a Label and a Text control. After creation the control is set to the current
* port name.
* </p>
* <p>
* The parent is expected to have a <code>GridLayout</code> with 2 columns.
* </p>
*
* @param parent
* Composite to which the port name settings controls are added.
* @param toolkit
* FormToolkit to use for the new controls.
* @param hi
* The current {@link HostInterface}.
*/
private void addPortCombo(Composite parent, FormToolkit toolkit, HostInterface hi) {
final ITargetConfigurationWorkingCopy tcwc = getTargetConfiguration();
//
// The Label
//
Label label = toolkit.createLabel(parent, "Portname:");
GridData gd = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
gd.widthHint = calcTextWidth(label, "Baudrate:");
label.setLayoutData(gd);
//
// The Text control
//
// TODO: replace this with a combo that has some preset values.
// For the PAR and SER ports this should be a list of previously entered names.
// For the USB port this could be a list of autodetected devices.
final Text combo = new Text(parent, SWT.BORDER);
toolkit.adapt(combo, true, true);
gd = new GridData(SWT.FILL, SWT.NONE, false, false);
gd.widthHint = 200;
combo.setLayoutData(gd);
combo
.setToolTipText("The host system port the programmer is attached to, e.g. '/dev/ttyS0' or 'com1'.\n"
+ "Leave empty to use the default port (may not work for usb devices)");
combo.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
String port = combo.getText();
if (DEFAULT.equals(port)) {
port = "";
}
tcwc.setAttribute(ATTR_PROGRAMMER_PORT, port);
getManagedForm().dirtyStateChanged();
}
});
// Set the current value for the port.
// We check if the current value makes sense for the port.
// If yes, then we leave the value like it is.
// If no, then replace the current value with a default value
// ("default" for SER/PAR ports, "usb" for USB ports)
String port = tcwc.getAttribute(ATTR_PROGRAMMER_PORT);
combo.setText(port);
switch (hi) {
case SERIAL:
case SERIAL_BB:
if (port.contains("com") // Windows
|| port.contains("cua") // FreeBSD
|| port.contains("tty") // Linux & MacOSX
|| port.contains("term") // Solaris)
) {
// leave as is
break;
}
// the previous setting probably does not represent a serial port
combo.setText(DEFAULT);
tcwc.setAttribute(ATTR_PROGRAMMER_PORT, "");
break;
case PARALLEL:
if (port.contains("lpt") // Windows
|| port.contains("ppi") // FreeBSD
|| port.contains("parport") // Linux & MacOSX
|| port.contains("printers") // Solaris)
) {
// leave as is
break;
}
// the previous setting probablydoes not represent a parallel port
combo.setText(DEFAULT);
tcwc.setAttribute(ATTR_PROGRAMMER_PORT, "");
break;
case USB:
if (port.contains("usb")) {
// leave as is
break;
}
// the previous setting probably does not represent a usb port
combo.setText("usb");
tcwc.setAttribute(ATTR_PROGRAMMER_PORT, "usb");
break;
default:
// Unsupported HostInterface -- ignore (this is here to make the FindBugs happy)
combo.setText("unknown");
}
}
/**
* Add the baud rate settings controls to the parent.
* <p>
* This consists of a Label and a Combo control. The combo has a list of common baudrates as
* well as a "default" setting. After creation the control is set to the current baud rate
* value.
* </p>
* <p>
* The parent is expected to have a <code>GridLayout</code> with 2 columns.
* </p>
*
* @param parent
* Composite to which the setting controls are added.
* @param toolkit
* FormToolkit to use for the new controls.
*/
private void addBaudRateCombo(Composite parent, FormToolkit toolkit) {
final ITargetConfigurationWorkingCopy tcwc = getTargetConfiguration();
//
// The Label
//
Label label = toolkit.createLabel(parent, "Baudrate:");
GridData gd = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
gd.widthHint = calcTextWidth(label, "Baudrate:");
label.setLayoutData(gd);
//
// The Combo
//
final Combo combo = new Combo(parent, SWT.NONE);
toolkit.adapt(combo, true, true);
gd = new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
combo.setLayoutData(gd);
combo
.setToolTipText("Override the RS-232 connection baud rate specified in the respective programmer's entry of the configuration file.\nLeave empty to use the default");
combo.setItems(BAUDRATES);
combo.setVisibleItemCount(BAUDRATES.length);
combo.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
String baudrate = combo.getText();
if (DEFAULT.equals(baudrate)) {
baudrate = "";
}
tcwc.setAttribute(ATTR_PROGRAMMER_BAUD, baudrate);
getManagedForm().dirtyStateChanged();
}
});
// The verify listener to restrict the input to integers
combo.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent event) {
String text = event.text;
if (!text.matches("[0-9]*") && !text.equals(DEFAULT)) {
event.doit = false;
}
}
});
// Set the current value for the port
String baudrate = tcwc.getAttribute(ATTR_PROGRAMMER_BAUD);
if (baudrate.length() == 0) {
baudrate = DEFAULT;
}
combo.setText(baudrate);
}
/**
* Add the bit bang delay setting section to the parent.
* <p>
* The Section contains the controls for the ATTR_BITBANGDELAY attribute.
* </p>
* <p>
* It is up to the caller to set the appropriate layout data on the returned
* <code>Section</code> control.
* </p>
*
* @param parent
* Composite to which the section is added.
* @param toolkit
* FormToolkit to use for the new controls.
*/
private Section addBitBangDelaySection(Composite parent, FormToolkit toolkit) {
final ITargetConfigurationWorkingCopy tcwc = getTargetConfiguration();
//
// The Section
//
Section section = toolkit.createSection(parent, Section.TWISTIE | Section.CLIENT_INDENT);
section.setText("Bitbang delay");
final String desc = "For bitbang-type programmers, delay for the set number of microseconds between each bit state change. \n"
+ "If the host system is very fast, or the target runs off a slow clock "
+ "(like a 32 kHz crystal, or the 128 kHz internal RC oscillator), this "
+ "can become necessary to satisfy the requirement that the ISP clock "
+ "frequency must not be higher than 1/4 of the CPU clock frequency.";
String delay = tcwc.getAttribute(ATTR_BITBANGDELAY);
if (delay.length() == 0) {
// If there has been no value then collapse this section
section.setExpanded(false);
} else {
section.setExpanded(true);
}
//
// The Section content
//
Composite sectionClient = toolkit.createComposite(section);
sectionClient.setLayout(new TableWrapLayout());
//
// The description Label
//
Label description = toolkit.createLabel(sectionClient, desc, SWT.WRAP);
description.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
//
// The actual controls, wrapped in a Composite with a 3 column GridLayout
//
Composite content = toolkit.createComposite(sectionClient);
content.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
content.setLayout(new GridLayout(3, false));
toolkit.createLabel(content, "delay:");
//
// The Text control
//
final Text text = toolkit.createText(content, delay, SWT.RIGHT);
GridData gd = new GridData(SWT.FILL, SWT.NONE, false, false);
gd.widthHint = calcTextWidth(text, "8888888888");
text.setLayoutData(gd);
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
String port = text.getText();
tcwc.setAttribute(ATTR_BITBANGDELAY, port);
getManagedForm().dirtyStateChanged();
}
});
// The verify listener to restrict the input to integers
text.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent event) {
String text = event.text;
if (!text.matches("[0-9]*")) {
event.doit = false;
}
}
});
toolkit.createLabel(content, "�s");
section.setClient(sectionClient);
return section;
}
/**
* Add the bit bang delay setting section to the parent.
* <p>
* The Section contains the controls for the ATTR_PAR_EXITSPEC attribute.
* </p>
* <p>
* It is up to the caller to set the appropriate layout data on the returned
* <code>Section</code> control.
* </p>
*
* @param parent
* Composite to which the section is added.
* @param toolkit
* FormToolkit to use for the new controls.
*/
private Section addExitSpecsSection(Composite parent, FormToolkit toolkit) {
final ITargetConfigurationWorkingCopy tcwc = getTargetConfiguration();
//
// The Section
//
Section section = toolkit.createSection(parent, Section.TWISTIE | Section.CLIENT_INDENT);
section.setText("Parallel Port Exit Specs");
final String desc = "By default, AVRDUDE leaves the parallel port "
+ "in the same state on exit as it has been found at startup. "
+ "These options modify the state of the `/RESET' and `Vcc' lines "
+ "the parallel port is left at.\nSee the avrdude manual for more details.";
String exitspecs = tcwc.getAttribute(ATTR_PAR_EXITSPEC);
if (exitspecs.length() == 0) {
// If there has been no value then collapse this section
section.setExpanded(false);
} else {
section.setExpanded(true);
}
// Parse the exitspec string and set the
// fExitSpecReset and fExitSpecVcc globals.
// Not very elegant but robust.
if (exitspecs.contains("noreset")) {
fExitSpecReset = "noreset";
} else if (exitspecs.contains("reset")) {
fExitSpecReset = "reset";
} else {
fExitSpecReset = "";
}
if (exitspecs.contains("novcc")) {
fExitSpecVcc = "novcc";
} else if (exitspecs.contains("vcc")) {
fExitSpecVcc = "vcc";
} else {
fExitSpecVcc = "";
}
//
// The Section content
//
Composite sectionClient = toolkit.createComposite(section);
sectionClient.setLayout(new TableWrapLayout());
//
// The description Label
//
Label description = toolkit.createLabel(sectionClient, desc, SWT.WRAP);
description.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
//
// The actual controls, wrapped in a Composite with a 2 column GridLayout
//
Composite content = toolkit.createComposite(sectionClient);
content.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
content.setLayout(new GridLayout(2, true));
//
// The /Reset group
//
Group resetGroup = new Group(content, SWT.NONE);
toolkit.adapt(resetGroup);
resetGroup.setText("/Reset Line");
resetGroup.setLayout(new RowLayout(SWT.VERTICAL));
SelectionListener resetlistener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String value = (String) e.widget.getData();
fExitSpecReset = value;
updateExitSpec();
}
};
// Create the 3 buttons in the reset group
Button button = toolkit.createButton(resetGroup, "Restore to previous state", SWT.RADIO);
button.setData("");
button.addSelectionListener(resetlistener);
if ("".equals(fExitSpecReset)) {
button.setSelection(true);
}
button = toolkit.createButton(resetGroup, "Activate (low) on exit", SWT.RADIO);
button.setData("reset");
button.addSelectionListener(resetlistener);
if ("reset".equals(fExitSpecReset)) {
button.setSelection(true);
}
button = toolkit.createButton(resetGroup, "Deactivate (high) on exit", SWT.RADIO);
button.setData("noreset");
button.addSelectionListener(resetlistener);
if ("noreset".equals(fExitSpecReset)) {
button.setSelection(true);
}
//
// The Vcc group
//
Group vccGroup = new Group(content, SWT.NONE);
toolkit.adapt(vccGroup);
vccGroup.setText("Vcc Lines");
vccGroup.setLayout(new RowLayout(SWT.VERTICAL));
SelectionListener vcclistener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String value = (String) e.widget.getData();
fExitSpecVcc = value;
updateExitSpec();
}
};
// Create the 3 buttons in the vcc group
button = toolkit.createButton(vccGroup, "Restore to previous state", SWT.RADIO);
button.setData("");
button.addSelectionListener(vcclistener);
if ("".equals(fExitSpecVcc)) {
button.setSelection(true);
}
button = toolkit.createButton(vccGroup, "Activate (high) on exit", SWT.RADIO);
button.setData("vcc");
button.addSelectionListener(vcclistener);
if ("vcc".equals(fExitSpecVcc)) {
button.setSelection(true);
}
button = toolkit.createButton(vccGroup, "Deactivate (low) on exit", SWT.RADIO);
button.setData("novcc");
button.addSelectionListener(vcclistener);
if ("novcc".equals(fExitSpecVcc)) {
button.setSelection(true);
}
section.setClient(sectionClient);
return section;
}
/**
* Update the exit spec attribute in the target configuration.
* <p>
* This method takes the values from {@link #fExitSpecReset} and {@link #fExitSpecVcc}, combines
* them into a string (usable for the AVRDude -E option) and sets the string in the target
* configuration.
* </p>
*/
private void updateExitSpec() {
StringBuilder sb = new StringBuilder(16);
if (fExitSpecReset.length() != 0) {
sb.append(fExitSpecReset);
if (fExitSpecVcc.length() != 0) {
sb.append(",");
}
}
if (fExitSpecVcc.length() != 0) {
sb.append(fExitSpecVcc);
}
getTargetConfiguration().setAttribute(ATTR_PAR_EXITSPEC, sb.toString());
getManagedForm().dirtyStateChanged();
}
/**
* Add the bit bang delay setting section to the parent.
* <p>
* The Section contains the controls for the ATTR_USB_DELAY attribute.
* </p>
* <p>
* It is up to the caller to set the appropriate layout data on the returned
* <code>Section</code> control.
* </p>
*
* @param parent
* Composite to which the section is added.
* @param toolkit
* FormToolkit to use for the new controls.
*/
private Section addUSBDelaySection(Composite parent, FormToolkit toolkit) {
//
// The Section
//
Section section = toolkit.createSection(parent, Section.TWISTIE | Section.CLIENT_INDENT);
section.setText("USB access delay");
final String desc = "Some USB devices need a certain time to release the USB bus. "
+ "As the AVR plugin sometimes accesses these devices in short succession "
+ "a delay can be specified between two accesses. Set the delay when you "
+ "get error messages that the selected usb port can't be opened.";
Composite sectionClient = toolkit.createComposite(section);
sectionClient.setLayout(new TableWrapLayout());
Label description = toolkit.createLabel(sectionClient, desc, SWT.WRAP);
description.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
String delay = getTargetConfiguration().getAttribute(ATTR_USB_DELAY);
if (delay.length() == 0) {
// If there has been no value then collapse this section
section.setExpanded(false);
} else {
section.setExpanded(true);
}
//
// The Section content
//
Composite content = toolkit.createComposite(sectionClient);
content.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
content.setLayout(new GridLayout(4, false));
//
// The actual controls, wrapped in a Composite with a 3 column GridLayout
//
toolkit.createLabel(content, "delay:");
final Text text = toolkit.createText(content, delay, SWT.RIGHT);
GridData gd = new GridData(SWT.FILL, SWT.NONE, false, false);
gd.widthHint = calcTextWidth(text, "8888888888");
text.setLayoutData(gd);
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
String port = text.getText();
getTargetConfiguration().setAttribute(ATTR_USB_DELAY, port);
getManagedForm().dirtyStateChanged();
}
});
// The verify listener to restrict the input to integers
text.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent event) {
String text = event.text;
if (!text.matches("[0-9]*")) {
event.doit = false;
}
}
});
toolkit.createLabel(content, "ms");
//
// The "Test" button
//
Hyperlink testlink = toolkit.createHyperlink(content, "Test delay", SWT.NONE);
testlink.setUnderlined(true);
testlink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));
// TODO: implement the actual test
section.setClient(sectionClient);
return section;
}
}