de.innot.avreclipse.ui.propertypages.TabAVRDudeProgrammer.java Source code

Java tutorial

Introduction

Here is the source code for de.innot.avreclipse.ui.propertypages.TabAVRDudeProgrammer.java

Source

/*******************************************************************************
 * 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.propertypages;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.window.Window;
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.graphics.FontMetrics;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

import de.innot.avreclipse.core.avrdude.ProgrammerConfig;
import de.innot.avreclipse.core.properties.AVRDudeProperties;
import de.innot.avreclipse.ui.preferences.AVRDudeConfigEditor;

/**
 * The main / general AVRDude options tab.
 * <p>
 * On this tab, the following properties are edited:
 * <ul>
 * <li>Avrdude Programmer Configuration, incl. buttons to edit the current
 * config or add a new config</li>
 * <li>The JTAG BitClock</li>
 * <li>The BitBanger bit change delay</li>
 * </ul>
 * </p>
 * 
 * @author Thomas Holland
 * @since 2.2
 * 
 */
public class TabAVRDudeProgrammer extends AbstractAVRDudePropertyTab {

    // The GUI texts
    // Programmer config selection group
    private final static String GROUP_PROGCONFIG = "Programmer configuration";
    private final static String TEXT_EDITBUTTON = "Edit...";
    private final static String TEXT_NEWBUTTON = "New...";
    private final static String LABEL_CONFIG_WARNING = "The Programmer configuration previously associated with this project/configuration\n"
            + "does not exist anymore. Please select a different one.";
    private final static String LABEL_NOCONFIG = "Please select a Programmer Configuration to enable avrdude functions";

    // JTAG Bitclock group
    private final static String GROUP_BITCLOCK = "JTAG ICE BitClock";
    private final static String LABEL_BITCLOCK = "Specify the bit clock period in microseconds for the JTAG interface or the ISP clock (JTAG ICE only).\n"
            + "Set this to > 1.0 for target MCUs running with less than 4MHz on a JTAG ICE.\n"
            + "Leave the field empty to use the preset bit clock period of the selected Programmer.";
    private final static String TEXT_BITCLOCK = "JTAG ICE bitclock";
    private final static String LABEL_BITCLOCK_UNIT = "\u03bcs";

    // BitBang delay group
    private final static String GROUP_DELAY = "BitBang Programmer Bit State Change Delay";
    private final static String LABEL_DELAY = "Specify the delay in microseconds for each bit change on bitbang-type programmers.\n"
            + "Set this when the the host system is very fast, or the target runs off a slow clock\n"
            + "Leave the field empty to run the ISP connection at max speed.";
    private final static String TEXT_DELAY = "Bit state change delay";
    private final static String LABEL_DELAY_UNIT = "\u03bcs";

    // The GUI widgets
    private Combo fProgrammerCombo;
    private Label fConfigWarningIcon;
    private Label fConfigWarningMessage;

    private Text fBitClockText;

    private Text fBitBangDelayText;

    /** The Properties that this page works with */
    private AVRDudeProperties fTargetProps;

    /** Warning image used for invalid Programmer Config values */
    private static final Image IMG_WARN = PlatformUI.getWorkbench().getSharedImages()
            .getImage(ISharedImages.IMG_OBJS_WARN_TSK);

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#createControls(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createControls(Composite parent) {

        parent.setLayout(new GridLayout(1, false));

        addProgrammerConfigSection(parent);

        addBitClockSection(parent);

        addBitBangDelaySection(parent);

    }

    /**
     * Add the Programmer Configuration selection <code>Combo</code> and the
     * "Edit", "New" Buttons.
     * 
     * @param parent
     *            <code>Composite</code>
     */
    private void addProgrammerConfigSection(Composite parent) {

        Group configgroup = setupGroup(parent, GROUP_PROGCONFIG, 3, SWT.NONE);
        configgroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));

        fProgrammerCombo = new Combo(configgroup, SWT.READ_ONLY);
        fProgrammerCombo.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
        fProgrammerCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                String selectedname = fProgrammerCombo.getItem(fProgrammerCombo.getSelectionIndex());
                String selectedid = getProgrammerConfigId(selectedname);
                fTargetProps.setProgrammerId(selectedid);
                showProgrammerWarning("", false);
                updateAVRDudePreview(fTargetProps);
            }
        });
        // Init the combo with the list of available programmer configurations
        loadProgrammerConfigs();

        // Edit... Button
        Button editButton = setupButton(configgroup, TEXT_EDITBUTTON, 1, SWT.NONE);
        editButton.setBackground(parent.getBackground());
        editButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                editButtonAction(false);
            }
        });

        // New... Button
        Button newButton = setupButton(configgroup, TEXT_NEWBUTTON, 1, SWT.NONE);
        newButton.setBackground(parent.getBackground());
        newButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                editButtonAction(true);
            }
        });

        // The Warning icon / message composite
        Composite warningComposite = new Composite(configgroup, SWT.NONE);
        warningComposite.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 3, 1));
        GridLayout gl = new GridLayout(2, false);
        gl.marginHeight = 0;
        gl.marginWidth = 0;
        gl.verticalSpacing = 0;
        gl.horizontalSpacing = 0;
        warningComposite.setLayout(gl);

        fConfigWarningIcon = new Label(warningComposite, SWT.LEFT);
        fConfigWarningIcon.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
        fConfigWarningIcon.setImage(IMG_WARN);

        fConfigWarningMessage = new Label(warningComposite, SWT.LEFT);
        fConfigWarningMessage.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        fConfigWarningMessage.setText("two-line\ndummy");

        // By default make the warning invisible
        // updateData() will make it visible when required
        fConfigWarningIcon.setVisible(false);
        fConfigWarningMessage.setVisible(false);

    }

    /**
     * The JTAG bitclock section.
     * <p>
     * The primary control in this section is a text field, that accepts only
     * floating point numbers.
     * </p>
     * 
     * @param parent
     *            <code>Composite</code>
     */
    private void addBitClockSection(Composite parent) {

        // TODO this could be replaced by a combo to select standard values.
        // Also this could be implemented as a frequency selector like in AVR
        // Studio
        // However, investigations into the avrdude source code indicate, that
        // the different programmer backends interpret this number differently.
        // Especially the stk500v2 backend interprets this number relative to the
        // clock frequency of the stk500 programmer.

        Group group = new Group(parent, SWT.NONE);
        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        group.setLayout(new GridLayout(3, false));
        group.setText(GROUP_BITCLOCK);

        Label label = new Label(group, SWT.WRAP);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
        label.setText(LABEL_BITCLOCK);

        setupLabel(group, TEXT_BITCLOCK, 1, SWT.NONE);

        fBitClockText = new Text(group, SWT.BORDER | SWT.RIGHT);
        GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
        FontMetrics fm = getFontMetrics(parent);
        gd.widthHint = Dialog.convertWidthInCharsToPixels(fm, 12);
        fBitClockText.setLayoutData(gd);
        fBitClockText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                fTargetProps.setBitclock(fBitClockText.getText());
                updateAVRDudePreview(fTargetProps);
            }
        });
        fBitClockText.addVerifyListener(new VerifyListener() {
            public void verifyText(VerifyEvent event) {
                // Accept only digits and -at most- one dot '.'
                int dotcount = 0;
                if (fBitClockText.getText().contains(".")) {
                    dotcount++;
                }
                String text = event.text;
                for (int i = 0; i < text.length(); i++) {
                    char ch = text.charAt(i);
                    if (ch == '.') {
                        dotcount++;
                        if (dotcount > 1) {
                            event.doit = false;
                            return;
                        }
                    } else if (!('0' <= ch && ch <= '9')) {
                        event.doit = false;
                        return;
                    }
                }
            }
        });

        // Label with the units (microseconds)
        setupLabel(group, LABEL_BITCLOCK_UNIT, 1, SWT.FILL);
    }

    /**
     * The BitBang bit change delay section.
     * <p>
     * The primary control in this section is a text field, that accepts only
     * integers.
     * </p>
     * 
     * @param parent
     *            <code>Composite</code>
     */
    private void addBitBangDelaySection(Composite parent) {

        Group group = new Group(parent, SWT.NONE);
        group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        group.setLayout(new GridLayout(3, false));
        group.setText(GROUP_DELAY);

        Label label = new Label(group, SWT.WRAP);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
        label.setText(LABEL_DELAY);

        setupLabel(group, TEXT_DELAY, 1, SWT.NONE);

        fBitBangDelayText = new Text(group, SWT.BORDER | SWT.RIGHT);
        GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
        FontMetrics fm = getFontMetrics(parent);
        gd.widthHint = Dialog.convertWidthInCharsToPixels(fm, 12);
        fBitBangDelayText.setLayoutData(gd);
        fBitBangDelayText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                fTargetProps.setBitBangDelay(fBitBangDelayText.getText());
                updateAVRDudePreview(fTargetProps);
            }
        });
        fBitBangDelayText.addVerifyListener(new VerifyListener() {
            public void verifyText(VerifyEvent event) {
                // Accept only digits
                String text = event.text;
                for (int i = 0; i < text.length(); i++) {
                    char ch = text.charAt(i);
                    if (!('0' <= ch && ch <= '9')) {
                        event.doit = false;
                        return;
                    }
                }
            }
        });

        // Label with the units (microseconds)
        setupLabel(group, LABEL_DELAY_UNIT, 1, SWT.FILL);
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performApply(de.innot.avreclipse.core.preferences.AVRProjectProperties)
     */
    @Override
    protected void performApply(AVRDudeProperties dstprops) {

        // Save all new / modified programmer configurations
        saveProgrammerConfigs();

        // Copy the currently selected values of this tab to the given, fresh
        // Properties.
        // The caller of this method will handle the actual saving
        dstprops.setProgrammerId(fTargetProps.getProgrammerId());
        dstprops.setBitclock(fTargetProps.getBitclock());
        dstprops.setBitBangDelay(fTargetProps.getBitBangDelay());
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performDefaults()
     */
    @Override
    protected void performDefaults() {

        // Reset the list of Programmer Configurations
        loadProgrammerConfigs();

        // The other defaults related stuff is done in the performCopy() method,
        // which is called later by the superclass.
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performDefaults(de.innot.avreclipse.core.preferences.AVRProjectProperties)
     */
    @Override
    protected void performCopy(AVRDudeProperties srcprops) {

        // Reload the items on this page
        fTargetProps.setProgrammerId(srcprops.getProgrammerId());
        fTargetProps.setBitclock(srcprops.getBitclock());
        fTargetProps.setBitBangDelay(srcprops.getBitBangDelay());
        updateData(fTargetProps);
    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#updateData(de.innot.avreclipse.core.preferences.AVRProjectProperties)
     */
    @Override
    protected void updateData(AVRDudeProperties props) {

        fTargetProps = props;

        // Set the selection of the Programmercombo
        // If the programmerid of the target properties does not exist,
        // show a warning and select the first item (without copying it into the
        // Target Properties)
        String programmerid = fTargetProps.getProgrammerId();
        if (programmerid.length() == 0) {
            // No Programmer has been set yet
            // Deselect the combo and show a Message
            fProgrammerCombo.deselect(fProgrammerCombo.getSelectionIndex());
            showProgrammerWarning(LABEL_NOCONFIG, false);
        } else {
            // Programmer id exists. Now test if it is still valid
            if (!isValidId(programmerid)) {
                // id is not valid. Deselect Combo and show a Warning
                fProgrammerCombo.deselect(fProgrammerCombo.getSelectionIndex());
                showProgrammerWarning(LABEL_CONFIG_WARNING, true);
            } else {
                // everything is good. Select the id in the combo
                String programmername = getProgrammerConfigName(programmerid);
                int index = fProgrammerCombo.indexOf(programmername);
                fProgrammerCombo.select(index);
                showProgrammerWarning("", false);
            }
        }

        fBitClockText.setText(fTargetProps.getBitclock());
        fBitBangDelayText.setText(fTargetProps.getBitBangDelay());

    }

    /*
     * (non-Javadoc)
     * 
     * @see de.innot.avreclipse.ui.propertypages.AbstractAVRDudePropertyTab#doProgConfigsChanged(java.lang.String[],
     *      int)
     */
    @Override
    protected void doProgConfigsChanged(String[] configs, int newindex) {

        fProgrammerCombo.setItems(configs);

        // make the combo show all available items (no scrollbar)
        fProgrammerCombo.setVisibleItemCount(configs.length);

        if (newindex != -1) {
            fProgrammerCombo.select(newindex);
        } else {
            fProgrammerCombo.deselect(fProgrammerCombo.getSelectionIndex());
        }
    };

    /**
     * Adds a new configuration or edits the currently selected Programmer
     * Configuration.
     * <p>
     * Called when either the new or the edit button has been clicked.
     * </p>
     * 
     * @see AVRDudeConfigEditor
     */
    private void editButtonAction(boolean createnew) {
        ProgrammerConfig oldconfig = null;

        // Create a list of all currently available configurations
        // This is used by the editor to avoid name clashes
        // (a configuration name needs to be unique)
        String[] allcfgs = fProgrammerCombo.getItems();
        Set<String> allconfignames = new HashSet<String>(allcfgs.length);
        for (String cfg : allcfgs) {
            allconfignames.add(cfg);
        }

        if (createnew) { // new config
            // Create a new configuration with a default name
            // (with a trailing running number if required),
            // a sample Description text and stk500v2 as programmer
            // (because I happen to have one)
            // All other options remain at the default (empty)
            String basename = "New Configuration";
            String defaultname = basename;
            int i = 1;
            while (allconfignames.contains(defaultname)) {
                defaultname = basename + " (" + i++ + ")";
            }
            oldconfig = fCfgManager.createNewConfig();
            oldconfig.setName(defaultname);
        } else { // edit existing config
            // Get the ProgrammerConfig from the Combo
            String configname = allcfgs[fProgrammerCombo.getSelectionIndex()];
            String configid = getProgrammerConfigId(configname);
            oldconfig = getProgrammerConfig(configid);
        }

        // Open the Config Editor.
        // If the OK Button was selected, the modified Config is fetched from
        // the Dialog and the the superclass is informed about the addition /
        // modification.
        AVRDudeConfigEditor dialog = new AVRDudeConfigEditor(fProgrammerCombo.getShell(), oldconfig,
                allconfignames);
        if (dialog.open() == Window.OK) {
            // OK Button selected:
            ProgrammerConfig newconfig = dialog.getResult();
            fTargetProps.setProgrammer(newconfig);

            addProgrammerConfig(newconfig);
            updateData(fTargetProps.getParent());
        }
    }

    /**
     * Show the supplied Warning in the Programmer config group.
     * 
     * @param text
     *            Message to display.
     * @param warning
     *            <code>true</code> to make the warning visible,
     *            <code>false</code> to hide it.
     */
    private void showProgrammerWarning(String text, boolean warning) {
        fConfigWarningIcon.setVisible(warning);
        fConfigWarningMessage.setText(text);
        fConfigWarningMessage.pack();
        fConfigWarningMessage.setVisible(true);
    }

}