/******************************************************************************* * 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 java.util.HashMap; import java.util.Map; 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.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Scale; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.IMessageManager; import org.eclipse.ui.forms.widgets.FormToolkit; 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.AVRHardwareConfigValidator; import de.innot.avreclipse.core.targets.IProgrammer; import de.innot.avreclipse.core.targets.ITargetConfigConstants; import de.innot.avreclipse.core.targets.ITargetConfiguration; import de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy; import de.innot.avreclipse.core.targets.TargetInterface; /** * FormPart to edit all settings for the current target interface. * <p> * This part is implemented as a section. * </p> * * @author Thomas Holland * @since 2.4 * */ public class SectionTargetInterface extends AbstractTCSectionPart implements ITargetConfigConstants { /** The list of target configuration attributes that this part manages. */ private final static String[] PART_ATTRS = new String[] { // // ATTR_JTAG_CLOCK, // ATTR_DAISYCHAIN_ENABLE, // ATTR_DAISYCHAIN_UB, // ATTR_DAISYCHAIN_UA, // ATTR_DAISYCHAIN_BB, // ATTR_DAISYCHAIN_BA }; /** The list of target configuration attributes that cause this part to refresh. */ private final static String[] PART_DEPENDS = new String[] { // // ATTR_PROGRAMMER_ID, // ATTR_FCPU }; /** the client area of the Section created by the superclass. */ private Composite fSectionClient; private Section fFreqSection; private Scale fFreqScale; private Label fFreqText; private Section fDaisyChainSection; /** The composite that contains the four daisy chain setting controls. */ private Composite fDaisyChainCompo; /** Map of the daisy chain attributes to their respective text controls. */ private Map<String, Text> fDaisyChainTexts = new HashMap<String, Text>(4); /** the array of possible clock frequencies for the current programmer. */ private int[] fClockValues; /* * (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 Arrays.copyOf(PART_DEPENDS, PART_DEPENDS.length); } /* * (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.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 programmerid = tcwc.getAttribute(ATTR_PROGRAMMER_ID); IProgrammer programmer = tcwc.getProgrammer(programmerid); TargetInterface newTI = programmer.getTargetInterface(); fClockValues = programmer.getTargetInterfaceClockFrequencies(); // // Clear the old section content // // First remove all old errors/warnings. // The MessageManager does not like disposed controls, so we have to remove // all messages first. // The warnings which are still valid are regenerated when the respective sections are // generated. IMessageManager mmngr = getMessageManager(); if (fFreqScale != null && !fFreqScale.isDisposed()) { mmngr.removeMessages(fFreqScale); } for (Control textcontrol : fDaisyChainTexts.values()) { if (!textcontrol.isDisposed()) { mmngr.removeMessages(textcontrol); } } // then remove all old controls from the section Control[] children = fSectionClient.getChildren(); for (Control child : children) { child.dispose(); } // Finally reflow the form. Otherwise layout artifacts may remain behind. getManagedForm().reflow(true); // // redraw the complete section. // String title = MessageFormat.format("{0} Settings", newTI.toString()); getControl().setText(title); // And rebuild the content FormToolkit toolkit = getManagedForm().getToolkit(); Section section = null; // Add the BitClock section if the target configuration has some bitclock values. // The target configuration knows which programmers have a settable bitclock and // which have not. if (fClockValues.length != 0) { section = addClockSection(fSectionClient, toolkit); section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); fFreqSection = section; } else { fFreqSection = null; } // Add the Daisy Chain section if the target interface is capable of daisy chaining. if (programmer.isDaisyChainCapable()) { section = addJTAGDaisyChainSection(fSectionClient, toolkit); section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); fDaisyChainSection = section; } else { fDaisyChainSection = null; } // If the target interface has neither settable clocks nor is daisy chain capable, then add // a small dummy text telling the user that there is nothing to set. if (section == null) { // the selected target interface has no options Label label = toolkit.createLabel(fSectionClient, "The selected progrmmer has no user changeable settings for the " + newTI.toString() + " target interface", SWT.WRAP); label.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); } } /* * (non-Javadoc) * @see * de.innot.avreclipse.ui.editors.targets.AbstractTargetConfigurationEditorPart#refreshWarnings * () */ public void refreshMessages() { validateBitClock(); validateDaisyChain(); } /** * Add the bit bang delay setting section to the parent. * <p> * The Section contains the controls for the ATTR_JTAGCLOCK 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 addClockSection(Composite parent, FormToolkit toolkit) { // // The Section // Section section = toolkit.createSection(parent, Section.TWISTIE | Section.CLIENT_INDENT); section.setText("Clock Frequency"); String desc = "The clock frequency must not be higher that 1/4 of " + "the target MCU clock frequency. The default value depends on the " + "selected tool, but is usually 1 MHz, suitable for target MCUs running " + "at 4 MHz or above."; int jtagclock = getTargetConfiguration().getIntegerAttribute(ATTR_JTAG_CLOCK); // Collapse the section if the current value is 0 (= default) to reduce clutter section.setExpanded(jtagclock != 0); // // 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)); GridLayout gl = new GridLayout(2, false); gl.horizontalSpacing = 12; content.setLayout(gl); fFreqScale = new Scale(content, SWT.HORIZONTAL); toolkit.adapt(fFreqScale, true, true); fFreqScale.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); fFreqScale.addSelectionListener(new SelectionAdapter() { /* * (non-Javadoc) * @seeorg.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events. * SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { int index = fFreqScale.getSelection(); int value = fClockValues[index]; updateBitClockValue(value); } }); // Set the scale properties. // we use an indirection: The scale does not set the Hz value directly. // instead it just selects the value from the fClockValues array. // The pageIncrements determine the number of ticks on the scale. // For up to 100 values in the bitclocks array we use 1 tick for each value. // For more than 100 values we use 1 tick for every 2 values. int units = fClockValues.length; fFreqScale.setMaximum(units - 1); fFreqScale.setMinimum(0); fFreqScale.setIncrement(1); fFreqScale.setPageIncrement(units < 100 ? 1 : 2); // // The frequency display. // fFreqText = toolkit.createLabel(content, "default", SWT.RIGHT); GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false); gd.widthHint = calcTextWidth(fFreqText, "88.888 MHz"); fFreqText.setLayoutData(gd); // Finally set the scale and the label to the current setting (or the next lower if a // different ClockValues table is used) int value = getTargetConfiguration().getIntegerAttribute(ATTR_JTAG_CLOCK); // find next lower value int lastv = 0; int index = 0; for (; index < fClockValues.length; index++) { if (fClockValues[index] <= value) { lastv = fClockValues[index]; } else { break; } } fFreqScale.setSelection(index - 1); // Update the value. This will in turn set the bitclock warning if required. updateBitClockValue(lastv); // Now tell the section about its content. section.setClient(sectionClient); return section; } /** * Updates the bitclock attribute in the target configuration and validates it. * * @param value * The new bitclock frequency. */ private void updateBitClockValue(int value) { // Set the attribute getTargetConfiguration().setIntegerAttribute(ATTR_JTAG_CLOCK, value); getManagedForm().dirtyStateChanged(); // update the frequency display label fFreqText.setText(convertFrequencyToString(value)); validateBitClock(); } /** * Convert a integer Hz value to a String. * <p> * The result has the unit appended: * <ul> * <li><code>Hz</code> for values below 1KHZ</li> * <li><code>KHz</code> for values between 1 and 1000 KHz</li> * <li><code>MHz</code> for values above 1000 KHz</li> * </ul> * As a special case the value <code>0</code> will result in "default". * </p> * * @param value * integer Hz value * @return */ private String convertFrequencyToString(int value) { String text; if (value == 0) { text = "default"; } else if (value < 1000) { text = value + " Hz"; } else if (value < 1000000) { float newvalue = value / 1000.0F; text = newvalue + " KHz"; } else { float newvalue = value / 1000000.0F; text = newvalue + " MHz"; } return text; } /** * Show or hide the 1/4th MCU frequency warning. * * @see AVRHardwareConfigValidator#checkJTAGClock(ITargetConfiguration) */ private void validateBitClock() { validate(ATTR_JTAG_CLOCK, fFreqScale); } /** * Add the JTAG daisy chain settings section to the parent. * <p> * The Section contains the controls for the ATTR_DAISYCHAIN_ENABLE and the four DAISYCHAIN_xx * attributes. * </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 addJTAGDaisyChainSection(Composite parent, FormToolkit toolkit) { // // The Section // Section section = toolkit.createSection(parent, Section.TWISTIE | Section.CLIENT_INDENT); section.setText("Daisy Chain"); String desc = "These settings are required if the target MCU is part of a JTAG daisy chain.\n" + "Set the number of devices before and after the target MCU in the chain " + "and the accumulated number of instruction bits they use. AVR devices use " + "4 instruction bits, but other JTAG devices may differ. \n" + "Note: JTAG daisy chains are only supported by some Programmers."; String enabledtext = getTargetConfiguration().getAttribute(ATTR_DAISYCHAIN_ENABLE); boolean enabled = Boolean.parseBoolean(enabledtext); // Collapse the section if Daisy chain is not enables to avoid clutter section.setExpanded(enabled); Composite sectionClient = toolkit.createComposite(section); sectionClient.setLayout(new TableWrapLayout()); // // The section description label // Label description = toolkit.createLabel(sectionClient, desc, SWT.WRAP); description.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); // // The Daisy Chain enable check button // boolean useDaisyChain = getTargetConfiguration() .getBooleanAttribute(ATTR_DAISYCHAIN_ENABLE); final Button enableButton = toolkit.createButton(sectionClient, "Enable daisy chain", SWT.CHECK); enableButton.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); enableButton.setSelection(useDaisyChain); enableButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { boolean isEnabled = enableButton.getSelection(); setEnabled(fDaisyChainCompo, isEnabled); getTargetConfiguration().setBooleanAttribute(ATTR_DAISYCHAIN_ENABLE, isEnabled); getManagedForm().dirtyStateChanged(); validateDaisyChain(); } }); // // The actual daisy chain controls, wrapped in a Composite with a 4 column GridLayout // fDaisyChainCompo = toolkit.createComposite(sectionClient); fDaisyChainCompo.setLayoutData(new TableWrapData(TableWrapData.FILL)); GridLayout layout = new GridLayout(4, false); layout.horizontalSpacing = 12; fDaisyChainCompo.setLayout(layout); createDCTextField(fDaisyChainCompo, "Devices before:", ATTR_DAISYCHAIN_UB); createDCTextField(fDaisyChainCompo, "Instruction bits before:", ATTR_DAISYCHAIN_BB); createDCTextField(fDaisyChainCompo, "Devices after:", ATTR_DAISYCHAIN_UA); createDCTextField(fDaisyChainCompo, "Instruction bits after:", ATTR_DAISYCHAIN_BA); setEnabled(fDaisyChainCompo, useDaisyChain); section.setClient(sectionClient); // Once we have created the controls we can validate the target configuration to set any // problem markers. validateDaisyChain(); return section; } /** * Create a single daisy chain settings text control with a label. * <p> * The created text control is added to the {@link #fDaisyChainTexts} map with the given * attribute as the key. * </p> * * @param parent * The parent composite (with a GridLayout) * @param labeltext * The text for the label * @param attribute * The target configuration attribute. */ private void createDCTextField(Composite parent, String labeltext, String attribute) { FormToolkit toolkit = getManagedForm().getToolkit(); final ModifyListener modifylistener = new ModifyListener() { public void modifyText(ModifyEvent e) { // Get the Attribute of the text field and its value String attr = (String) e.widget.getData(); String value = ((Text) e.widget).getText(); if (value.length() == 0) { value = "0"; } int intvalue = Integer.parseInt(value); getTargetConfiguration().setIntegerAttribute(attr, intvalue); getManagedForm().dirtyStateChanged(); validateDaisyChain(); } }; // The verify listener to restrict the input to integers final VerifyListener verifylistener = new VerifyListener() { public void verifyText(VerifyEvent event) { String text = event.text; if (!text.matches("[0-9]*")) { event.doit = false; } } }; toolkit.createLabel(parent, labeltext); int currvalue = getTargetConfiguration().getIntegerAttribute(attribute); String currvaluestring = Integer.toString(currvalue); Text text = toolkit.createText(parent, currvaluestring, SWT.RIGHT); GridData gd = new GridData(SWT.FILL, SWT.NONE, false, false); gd.widthHint = calcTextWidth(text, "8888"); text.setLayoutData(gd); text.setTextLimit(3); text.setData(attribute); // set the attribute for the modify listener text.addModifyListener(modifylistener); text.addVerifyListener(verifylistener); fDaisyChainTexts.put(attribute, text); } /** * Add or remove the error messages for the daisy chain settings. * * @see AVRHardwareConfigValidator#checkJTAGDaisyChainUnitsBefore(ITargetConfiguration) * @see AVRHardwareConfigValidator#checkJTAGDaisyChainUnitsAfter(ITargetConfiguration) * @see AVRHardwareConfigValidator#checkJTAGDaisyChainBitsBefore(ITargetConfiguration) * @see AVRHardwareConfigValidator#checkJTAGDaisyChainBitsAfter(ITargetConfiguration) */ private void validateDaisyChain() { validate(ATTR_DAISYCHAIN_BB, fDaisyChainTexts.get(ATTR_DAISYCHAIN_BB)); validate(ATTR_DAISYCHAIN_BA, fDaisyChainTexts.get(ATTR_DAISYCHAIN_BA)); validate(ATTR_DAISYCHAIN_UB, fDaisyChainTexts.get(ATTR_DAISYCHAIN_UB)); validate(ATTR_DAISYCHAIN_UA, fDaisyChainTexts.get(ATTR_DAISYCHAIN_UA)); } /* * (non-Javadoc) * @see de.innot.avreclipse.ui.editors.targets.AbstractTCSectionPart#setFocus(java.lang.String) */ @Override public boolean setFocus(String attribute) { if (attribute.equals(ATTR_JTAG_CLOCK)) { if (fFreqScale != null && !fFreqScale.isDisposed()) { fFreqScale.setFocus(); } if (fFreqSection != null && !fFreqSection.isDisposed()) { fFreqSection.setExpanded(true); } return true; } if (fDaisyChainTexts.containsKey(attribute)) { Text textcontrol = fDaisyChainTexts.get(attribute); if (textcontrol != null && !textcontrol.isDisposed()) { textcontrol.setFocus(); } if (fDaisyChainSection != null && !fDaisyChainSection.isDisposed()) { fDaisyChainSection.setExpanded(true); } return true; } return false; } }