/******************************************************************************* * 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.preferences; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.preference.FieldEditor; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Widget; import org.osgi.service.prefs.BackingStoreException; import de.innot.avreclipse.AVRPlugin; import de.innot.avreclipse.core.avrdude.ProgrammerConfig; import de.innot.avreclipse.core.avrdude.ProgrammerConfigManager; /** * A special Field Editor to edit the list of AVRDude programmer configurations. * <p> * This editor has a Table of all Programmer Configurations, which can be edited, removed and added. * </p> * <p> * It does not work on a PreferenceStore, because the list of all configurations can only be * gathered directly from the Preferences. It does however support the Apply, Cancel and partially * the Defaults actions of a FieldEditorPreferencePage. * </p> * <p> * All modifications of programmer configurations are only persisted when the OK or Apply actions * occur. * </p> * * @author Thomas Holland * @since 2.2 * */ public class ProgConfigListFieldEditor extends FieldEditor { /** The Table Control */ private Table fTableControl; /** The button box Composite containing the Add, Remove and Edit buttons */ private Composite fButtonComposite; // The GUI Widgets private Button fAddButton; private Button fRemoveButton; private Button fEditButton; /** * The list of removed configurations. They will be removed in the {@link #doStore()} method */ private List<ProgrammerConfig> fRemovedConfigs; private final ProgrammerConfigManager fCfgManager = ProgrammerConfigManager.getDefault(); /** * Creates a AVRDude Programmers Configuration List field editor. * <p> * Because this field editor does not work on a PreferenceStore, it does not need to be passed * to the constructor. * </p> * * @param labelText * the label text of the field editor * @param parent * the parent of the field editor's control */ public ProgConfigListFieldEditor(String label, Composite parent) { super(); super.setLabelText(label); createControl(parent); } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int) */ @Override protected void adjustForNumColumns(int numColumns) { // The Table gets all but one column, the ButtonBox one column. Control control = getLabelControl(); ((GridData) control.getLayoutData()).horizontalSpan = numColumns; ((GridData) fTableControl.getLayoutData()).horizontalSpan = numColumns - 1; } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid(org.eclipse.swt.widgets.Composite, * int) */ @Override protected void doFillIntoGrid(Composite parent, int numColumns) { // Add the three controls: // Label, Table and Buttonbox Control control = getLabelControl(parent); GridData gd = new GridData(); gd.horizontalSpan = numColumns; control.setLayoutData(gd); fTableControl = getTableControl(parent); gd = new GridData(SWT.FILL, SWT.FILL, true, true, numColumns - 1, 1); fTableControl.setLayoutData(gd); fButtonComposite = getButtonBoxComposite(parent); gd = new GridData(); gd.verticalAlignment = GridData.BEGINNING; fButtonComposite.setLayoutData(gd); } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#doLoad() */ @Override protected void doLoad() { if (fTableControl != null) { Set<String> allconfigids = fCfgManager.getAllConfigIDs(); for (String configid : allconfigids) { if (configid.length() > 0) { ProgrammerConfig config = fCfgManager.getConfig(configid); TableItem item = new TableItem(fTableControl, SWT.NONE); item.setText(new String[] { config.getName(), config.getDescription() }); item.setData(config); } } } // init the list of removed configurations fRemovedConfigs = new ArrayList<ProgrammerConfig>(); } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault() */ @Override protected void doLoadDefault() { // No defaults supported for the List of Programmer Configurations // however we just reload the existing configurations. (loosing all // modifications) fTableControl.removeAll(); doLoad(); } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#doStore() */ @Override protected void doStore() { // Save all configs still in the table, then remove all configs marked // for removal TableItem[] allitems = fTableControl.getItems(); // save all current configs to the persistent store for (TableItem item : allitems) { ProgrammerConfig config = (ProgrammerConfig) item.getData(); try { fCfgManager.saveConfig(config); } catch (BackingStoreException e) { IStatus status = new Status(Status.ERROR, AVRPlugin.PLUGIN_ID, "Can't save Programmer Configuration [" + config.getName() + "] to the preference storage area", e); AVRPlugin.getDefault().log(status); ErrorDialog.openError(fTableControl.getShell(), "Programmer Configuration Error", null, status); } } // now delete the Configs marked as removed for (ProgrammerConfig config : fRemovedConfigs) { try { fCfgManager.deleteConfig(config); } catch (BackingStoreException e) { IStatus status = new Status(Status.ERROR, AVRPlugin.PLUGIN_ID, "Can't delete Programmer Configuration [" + config.getName() + "] from the preference storage area", e); AVRPlugin.getDefault().log(status); ErrorDialog.openError(fTableControl.getShell(), "Programmer Configuration Error", null, status); } } fRemovedConfigs.clear(); } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls() */ @Override public int getNumberOfControls() { // Two: List and Buttons Composite return 2; } /* * (non-Javadoc) * * @see org.eclipse.jface.preference.FieldEditor#setFocus() */ @Override public void setFocus() { if (fTableControl != null) { fTableControl.setFocus(); } } /** * Returns this field editors Table control. * * @param parent * the parent control * @return the list control */ public Table getTableControl(Composite parent) { if (fTableControl == null) { // Create the Table control, add two columns (name and description) // and set up the required listeners fTableControl = new Table(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); fTableControl.setFont(parent.getFont()); fTableControl.setLinesVisible(true); fTableControl.setHeaderVisible(true); fTableControl.addSelectionListener(new SelectionAdapter() { /* * (non-Javadoc) * * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { Widget widget = e.widget; if (widget == fTableControl) { selectionChanged(); } } }); fTableControl.addDisposeListener(new DisposeListener() { /* * (non-Javadoc) * * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent event) { fTableControl = null; } }); TableColumn column = new TableColumn(fTableControl, SWT.NONE); column.setText("Configuration"); column.setWidth(100); column = new TableColumn(fTableControl, SWT.NONE); column.setText("Description"); column.setWidth(200); } else { // fTableControl != null checkParent(fTableControl, parent); } return fTableControl; } /** * Returns this field editor's button box containing the Add, Remove and Edit buttons. * * @param parent * the parent control * @return the button box */ public Composite getButtonBoxComposite(Composite parent) { if (fButtonComposite == null) { fButtonComposite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(1, false); layout.marginWidth = 0; fButtonComposite.setLayout(layout); createButtons(fButtonComposite); fButtonComposite.addDisposeListener(new DisposeListener() { /* * (non-Javadoc) * * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent event) { fAddButton = null; fRemoveButton = null; fEditButton = null; } }); } else { checkParent(fButtonComposite, parent); } selectionChanged(); return fButtonComposite; } /** * Creates the Add, Remove and Edit buttons in the given button box. * * @param box * the box for the buttons */ private void createButtons(Composite box) { fAddButton = createPushButton(box, "Add..."); fEditButton = createPushButton(box, "Edit..."); fRemoveButton = createPushButton(box, "Remove"); } /** * Helper method to create a push button. * * @param parent * the parent control * @param label * the button's label text * @return Button */ private Button createPushButton(Composite parent, String label) { Button button = new Button(parent, SWT.PUSH); button.setText(label); button.setFont(parent.getFont()); GridData data = new GridData(GridData.FILL_HORIZONTAL); int widthHint = convertHorizontalDLUsToPixels(button, IDialogConstants.BUTTON_WIDTH); data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); button.setLayoutData(data); button.addSelectionListener(new SelectionAdapter() { /* * (non-Javadoc) * * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { Widget widget = e.widget; if (widget == fAddButton) { editButtonAction(true); } else if (widget == fRemoveButton) { removeButtonAction(); } else if (widget == fEditButton) { editButtonAction(false); } } }); return button; } /** * Enable / Disable Buttons as required. * <p> * The Remove and Edit Buttons are only enabled, when an item is selected in the Table. * </p> * <p> * Called after each change of the Table. * </p> */ private void selectionChanged() { int index = fTableControl.getSelectionIndex(); fRemoveButton.setEnabled(index >= 0); fEditButton.setEnabled(index >= 0); // TODO Is this necessary? fTableControl.redraw(); } /** * Remove the selected configuration. * <p> * The config is stored in the <code>fRemovedConfigs</code> list. All removed are only * physically removed in the {@link #doStore()} method. * </p> * <p> * Called when the remove button has been clicked. * </p> */ private void removeButtonAction() { setPresentsDefaultValue(false); int index = fTableControl.getSelectionIndex(); if (index >= 0) { TableItem ti = fTableControl.getItem(index); fRemovedConfigs.add((ProgrammerConfig) ti.getData()); fTableControl.remove(index); selectionChanged(); } } /** * Adds a new configuration or edit the currently selected config. * <p> * Called when either the add or the edit button has been clicked. * </p> */ private void editButtonAction(boolean createnew) { setPresentsDefaultValue(false); ProgrammerConfig config = null; TableItem ti = 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) Set<String> allconfigs = new HashSet<String>(); TableItem[] allitems = fTableControl.getItems(); for (TableItem item : allitems) { allconfigs.add(item.getText(0)); } if (createnew) { // new config // Create a new configuration with a default name // (with a trailing running number if required), String basename = "New Configuration"; String defaultname = basename; int i = 1; while (allconfigs.contains(defaultname)) { defaultname = basename + " (" + i++ + ")"; } config = fCfgManager.createNewConfig(); config.setName(defaultname); } else { // edit existing config // Get the ProgrammerConfig from the selected TableItem ti = fTableControl.getItem(fTableControl.getSelectionIndex()); config = (ProgrammerConfig) ti.getData(); } // Open the Config Editor. // If the OK Button was selected, the modified Config is fetched from // the Dialog and the relevant TableItem is updated. AVRDudeConfigEditor dialog = new AVRDudeConfigEditor(fTableControl.getShell(), config, allconfigs); if (dialog.open() == Window.OK) { // OK Button selected: ProgrammerConfig newconfig = dialog.getResult(); if (createnew) { ti = new TableItem(fTableControl, SWT.NONE); } // Change the TableItem if (ti != null) { ti.setText(new String[] { newconfig.getName(), newconfig.getDescription() }); ti.setData(newconfig); } selectionChanged(); } } }