/******************************************************************************* * 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 org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; 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.Group; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.progress.UIJob; import de.innot.avreclipse.core.avrdude.AVRDudeException; import de.innot.avreclipse.core.avrdude.AVRDudeSchedulingRule; import de.innot.avreclipse.core.properties.AVRDudeProperties; import de.innot.avreclipse.core.toolinfo.AVRDude; import de.innot.avreclipse.ui.dialogs.AVRDudeErrorDialogJob; /** * The AVRDude Other Actions Tab page. * <p> * On this tab, the following properties are edited: * <ul> * <li>Enable Flash memory erase cycle counter, incl. the non-property related options to load / * write the current cycle counter value to / from a connected device</li> * <li>The "other options" field for avrdude options not covered by the plugin</li> * </ul> * </p> * * @author Thomas Holland * @since 2.3 * */ public class TabAVRDudeOther extends AbstractAVRDudePropertyTab { // The GUI texts // Flash erase cycle counter check private final static String GROUP_COUNTER = "Flash memory erase cycle counter (-y)"; private final static String LABEL_COUNTER = "Enable this to have avrdude count the number of flash erase cycles.\n" + "Note: the value is stored in the last four bytes of the EEPROM,\n" + "so do not enable this if your application needs the last four bytes of the EEPROM."; private final static String TEXT_COUNTER = "Enable erase cycle counter"; private final static String TEXT_READBUTTON = "Load from MCU"; private final static String TEXT_READBUTTON_BUSY = "Loading..."; private final static String TEXT_WRITEBUTTON = "Write to MCU"; private final static String TEXT_WRITEBUTTON_BUSY = "Writing..."; // Other options group private final static String GROUP_OTHER = "Other options"; private final static String LABEL_OTHER = "Use this field to add any avdude option not covered by the plugin."; // The GUI widgets private Button fCounterCheck; private Composite fCounterOptionsComposite; private Text fCounterValue; private Button fReadButton; private Button fWriteButton; private Text fOtherOptionsText; /** The Properties that this page works with */ private AVRDudeProperties fTargetProps; /* * (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)); addUseCycleCounterSection(parent); addOtherOptionsSection(parent); } /** * Add the Other Options Text field. * * @param parent * <code>Composite</code> */ private void addOtherOptionsSection(Composite parent) { Group group = new Group(parent, SWT.NONE); group.setText(GROUP_OTHER); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); group.setLayout(new GridLayout(1, false)); setupLabel(group, LABEL_OTHER, 1, SWT.FILL); fOtherOptionsText = setupText(group, 1, SWT.FILL); fOtherOptionsText.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false)); fOtherOptionsText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String newvalue = fOtherOptionsText.getText(); fTargetProps.setOtherOptions(newvalue); updateAVRDudePreview(fTargetProps); } }); } /** * Add the GUI section for the Flash memory cycle counter settings. * <p> * This section contains not only the actual checkbox, but also some controls to read/write the * current cycle counter. These controls are not directly related to the properties, but it * seems like a good idea to put them here. * </p> * * @param parent * <code>Composite</code> */ private void addUseCycleCounterSection(Composite parent) { Group countergroup = setupGroup(parent, GROUP_COUNTER, 1, SWT.NONE); countergroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); setupLabel(countergroup, LABEL_COUNTER, 2, SWT.NONE); fCounterCheck = setupCheck(countergroup, TEXT_COUNTER, 2, SWT.NONE); // Cycle Counter load / write control composite fCounterOptionsComposite = new Composite(countergroup, SWT.NONE); fCounterOptionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1)); fCounterOptionsComposite.setLayout(new GridLayout(4, false)); setupLabel(fCounterOptionsComposite, "Current Erase Cycle Counter", 1, SWT.NONE); fCounterValue = new Text(fCounterOptionsComposite, SWT.BORDER | SWT.RIGHT); fCounterValue.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); fCounterValue.setTextLimit(5); // Max. 65535 fCounterValue.addVerifyListener(new VerifyListener() { // only allow digits as cycle counter values public void verifyText(VerifyEvent event) { String text = event.text; if (!text.matches("[0-9]*")) { event.doit = false; } } }); // Read Button fReadButton = setupButton(fCounterOptionsComposite, TEXT_READBUTTON, 1, SWT.NONE); fReadButton.setBackground(parent.getBackground()); fReadButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { readCycleCounter(); } }); // Write Button fWriteButton = setupButton(fCounterOptionsComposite, TEXT_WRITEBUTTON, 1, SWT.NONE); fWriteButton.setBackground(parent.getBackground()); fWriteButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { writeCycleCounter(); } }); // Disable by default. The updateData() method will enable/disable // according to the actual property settings. setEnabled(fCounterOptionsComposite, false); } /* * (non-Javadoc) * * @see org.eclipse.cdt.ui.newui.AbstractCPropertyTab#checkPressed(org.eclipse.swt.events.SelectionEvent) */ @Override protected void checkPressed(SelectionEvent e) { // This is called for all checkbuttons / tributtons which have been set // up with the setupXXX() calls Control source = (Control) e.widget; if (source.equals(fCounterCheck)) { // Use Cycle Counter Checkbox has been selected // Write the new value to the target properties // and enable / disable the Counter Options composite accordingly boolean newvalue = fCounterCheck.getSelection(); fTargetProps.setUseCounter(newvalue); setEnabled(fCounterOptionsComposite, newvalue); } updateAVRDudePreview(fTargetProps); } /* * (non-Javadoc) * * @see de.innot.avreclipse.ui.propertypages.AbstractAVRPropertyTab#performApply(de.innot.avreclipse.core.preferences.AVRProjectProperties) */ @Override protected void performApply(AVRDudeProperties dstprops) { if (fTargetProps == null) { // updataData() has not been called and this tab has no (modified) // settings yet. return; } // Copy the currently selected values of this tab to the given, fresh // Properties. // The caller of this method will handle the actual saving dstprops.setUseCounter(fTargetProps.getUseCounter()); dstprops.setOtherOptions(fTargetProps.getOtherOptions()); } /* * (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.setUseCounter(srcprops.getUseCounter()); fTargetProps.setOtherOptions(srcprops.getOtherOptions()); 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; // Update the GUI widgets on this Tab. boolean usecounter = fTargetProps.getUseCounter(); fCounterCheck.setSelection(usecounter); setEnabled(fCounterOptionsComposite, usecounter); fOtherOptionsText.setText(fTargetProps.getOtherOptions()); } /** * Read the current cycles value from the currently attached device. * <p> * The actual read is done in a separate Thread to reduce the impact on the GUI. * </p> */ private void readCycleCounter() { // Disable the Load Button. It is re-enabled by the load job when it finishes. fReadButton.setEnabled(false); fReadButton.setText(TEXT_READBUTTON_BUSY); // The Job that does the actual loading. Job readJob = new CycleJob(fReadButton, false, 0, TEXT_READBUTTON); // now set the Job properties and start it readJob.setRule(new AVRDudeSchedulingRule(fTargetProps.getProgrammer())); readJob.setPriority(Job.SHORT); readJob.setUser(true); readJob.schedule(); } /** * Write the selected cycles value to the currently attached device. * <p> * The actual load is done in a separate Thread to reduce the impact on the GUI. * </p> */ private void writeCycleCounter() { // Disable the Load Button. It is re-enabled by the load job when it finishes. fWriteButton.setEnabled(false); fWriteButton.setText(TEXT_WRITEBUTTON_BUSY); int newvalue = Integer.parseInt(fCounterValue.getText()); // 65534 is the largest value accepted (65535 (0xffff) is interpreted by avrdude as an unset // cycle counter). if (newvalue >= 0xfffe) { newvalue = 0xfffe; } // The Job that does the actual loading. Job readJob = new CycleJob(fWriteButton, true, newvalue, TEXT_WRITEBUTTON); // now set the Job properties and start it readJob.setRule(new AVRDudeSchedulingRule(fTargetProps.getProgrammer())); readJob.setPriority(Job.SHORT); readJob.setUser(true); readJob.schedule(); } /** * Private class to read or write the erase cycle counter. */ private class CycleJob extends Job { private final Button fButton; private final String fButtonText; private final boolean fWrite; private final int fNewCounterValue; /** * @param button * The button that was selected. Required to re-enable the button once the job * has finished. * @param write * <code>true</code> if the <code>newcounter</code> value will be written to * the device. if <code>false</code> only the current MCU counter is read. * @param newcounter * New erase cycle counter value (ignored if <code>write</code> is * <code>false</code>. * @param buttontext * The normal text for the source button. Required to restore the text once the * job has finished. */ public CycleJob(Button button, boolean write, int newcounter, String buttontext) { super("Accessing Erase Cycle Counter"); fButton = button; fWrite = write; fNewCounterValue = newcounter; fButtonText = buttontext; } @Override protected IStatus run(IProgressMonitor monitor) { try { monitor.beginTask("Starting AVRDude", 100); final int loadedvalue; if (fWrite) { loadedvalue = AVRDude.getDefault().setEraseCycleCounter( fTargetProps.getProgrammer(), fNewCounterValue, new SubProgressMonitor(monitor, 95)); } else { loadedvalue = AVRDude.getDefault().getEraseCycleCounter( fTargetProps.getProgrammer(), new SubProgressMonitor(monitor, 95)); } // and update the user interface if (!fButton.isDisposed()) { fButton.getDisplay().syncExec(new Runnable() { public void run() { if (loadedvalue >= 0) { fCounterValue.setText(Integer.toString(loadedvalue)); } else fCounterValue.setText("0"); } }); } monitor.worked(5); } catch (AVRDudeException ade) { // Show an Error message and exit if (!fButton.isDisposed()) { UIJob messagejob = new AVRDudeErrorDialogJob(fButton.getDisplay(), ade, fTargetProps.getProgrammerId()); messagejob.setPriority(Job.INTERACTIVE); messagejob.schedule(); try { messagejob.join(); // block until the dialog is closed. } catch (InterruptedException e) { // Don't care if the dialog is interrupted from outside. } } } catch (SWTException swte) { // The display has been disposed, so the user is not // interested in the results from this job return Status.CANCEL_STATUS; } finally { monitor.done(); // Re-Enable the Button if (!fButton.isDisposed()) { fButton.getDisplay().syncExec(new Runnable() { public void run() { // Re-Enable the Button fButton.setEnabled(true); fButton.setText(fButtonText); } }); } } return Status.OK_STATUS; } }; }