/*******************************************************************************
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.forms.AbstractFormPart;
import org.eclipse.ui.forms.IFormPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.widgets.ColumnLayout;
import org.eclipse.ui.forms.widgets.Section;
import de.innot.avreclipse.core.toolinfo.fuses.BitFieldDescription;
import de.innot.avreclipse.core.toolinfo.fuses.ByteValueChangeEvent;
import de.innot.avreclipse.core.toolinfo.fuses.ByteValues;
import de.innot.avreclipse.core.toolinfo.fuses.IByteValuesChangeListener;
import de.innot.avreclipse.ui.dialogs.ByteValuesEditorDialog;
/**
* /** A <code>IFormPart</code> that can edit all BitFields of a <code>ByteValues</code> source
* object.
* <p>
* This class automatically creates a <code>Composite</code> and adds it to the parent. The
* composite is given a <code>ColumnLayout</code> and then all {@link BitFieldEditorSectionPart}s
* are added. See {@link ByteValuesEditorDialog#optimizeSize()} for a discussion about the problems
* of the <code>ColumnLayout</code> in unconstrained layouts
* </p>
* <p>
* After the class has been instantiated it must be added to a <code>IManagedForm</code> to
* participate in the lifecycle management of the managed form.
*
* <pre>
* Composite parent = ...
* ByteValues bytevalues = ...
* IManagedForm managedForm = ...
*
* IFormPart part = new ByteValuesMainPart(parent, bytevalues);
* managedForm.addPart(part);
* </pre>
*
* </p>
* <p>
* This class implements the {@link IFormPart} interface to participate in the lifecycle management
* of a managed form. To set the value of all BitFields use
*
* <pre>
* ByteValues bytevalues = ...
* managedForm.setInput(bytevalues);
* </pre>
*
* The <code>ByteValues</code> passed to the managedForm is the model for this
* <code>SectionPart</code>. Unlike normal IFormParts all changes to the source ByteValues are
* applied immediately, because other BitFields or even other editors might be affected by the
* change.
* </p>
*
* @author Thomas Holland
* @since 2.3
*
*/
public class ByteValuesMainPart extends AbstractFormPart implements IByteValuesChangeListener {
/** The parent composite. */
private final Composite fParent;
/**
* List of all child <code>Sections</code>. Used to clear the parent composite for a redraw and
* to determine the widest section.
*/
private final List<Control> fControls = new ArrayList<Control>();
/**
* List of all child <code>IFormPart</code>. Used to clear to ManagedForm for a redraw.
*/
private final List<IFormPart> fPFormParts = new ArrayList<IFormPart>();
/**
* The model for this <code>IFormPart</code>. Will not be modified in this class, but in the
* {@link #commit(boolean)} method of the child <code>SectionPart</code>s.
*/
private ByteValues fByteValues;
/**
* The current MCU of the ByteValues. If this is different than {@link #fLastCleanMCU} then this
* part is stale and needs to be redrawn on the next refresh.
*/
private String fCurrentMCU = null;
/**
* Create a new <code>IFormPart</code> to handle all BitFields in a <code>ByteValues</code>.
* <p>
* The <code>ByteValues</code> model is required to determine all
* <code>BitFieldDescriptions</code> rendered in this part. This is required because
* {@link #initialize(IManagedForm)} is called before {@link #setFormInput(Object)} and this
* class needs to now the ByteValues structure to render itself.
* </p>
* <p>
* The actual content of the <code>ByteValues</code> is not used until the
* <code>setInput(ByteValues)</code> method of the ManagedForm is called.
* </p>
*
* @param parent
* the parent. Layout of the parent will be set to <code>ColumnLayout</code>.
* @param model
* the <code>ByteValues</code> to use for the first rendering of this part.
*/
public ByteValuesMainPart(Composite parent, ByteValues model) {
fParent = parent;
fByteValues = model;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
*/
@Override
public void initialize(IManagedForm form) {
super.initialize(form);
ColumnLayout layout = new ColumnLayout();
layout.horizontalSpacing = 10;
fParent.setLayout(layout);
initForm(form);
}
/**
* (Re)draw the Form.
*
* @param form
* <code>IManagedForm</code>
*/
private void initForm(IManagedForm form) {
List<BitFieldDescription> allbfds = fByteValues.getBitfieldDescriptions();
// Sort the bitfield descriptions according to their name
// The ColumnLayout will mess this up in a two column layout but this is better than
// nothing.
Collections.sort(allbfds, new Comparator<BitFieldDescription>() {
public int compare(BitFieldDescription o1, BitFieldDescription o2) {
String name1 = o1.getName();
String name2 = o2.getName();
return name1.compareTo(name2);
}
});
// Now go though all BitFieldDesriptions, create SectionParts for them, add them to the
// ManagedForm and remember them for later access.
for (BitFieldDescription bfd : allbfds) {
SectionPart part = new BitFieldEditorSectionPart(fParent, form.getToolkit(),
Section.TITLE_BAR, bfd);
form.addPart(part);
fPFormParts.add(part);
fControls.add(part.getSection());
}
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#dispose()
*/
@Override
public void dispose() {
if (fByteValues != null) {
fByteValues.removeChangeListener(this);
}
super.dispose();
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#setFormInput(java.lang.Object)
*/
@Override
public boolean setFormInput(Object input) {
if (!(input instanceof ByteValues)) {
// Input not applicable
return false;
}
fByteValues = (ByteValues) input;
fByteValues.addChangeListener(this);
refresh();
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#refresh()
*/
@Override
public void refresh() {
if (fByteValues == null) {
return; // not initialized yet
}
// clear this form if it has been drawn before for a different MCU type.
if (!fByteValues.getMCUId().equals(fCurrentMCU)) {
// Clear the form and redraw it for the new type
redraw();
fCurrentMCU = fByteValues.getMCUId();
}
super.refresh();
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#commit(boolean)
*/
@Override
public void commit(boolean onSave) {
// nothing to commit for this part
super.commit(onSave);
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#isDirty()
*/
@Override
public boolean isDirty() {
// This part is never dirty.
// Its subparts might be.
return false;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#isStale()
*/
@Override
public boolean isStale() {
// This part is stale if the ByteValues are for a different MCU than currently shown on this
// part.
if (!fByteValues.getMCUId().equals(fCurrentMCU)) {
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.toolinfo.fuses.IByteValuesChangeListener#byteValuesChanged(de.innot
* .avreclipse.core.toolinfo.fuses.ByteValueChangeEvent[])
*/
public void byteValuesChanged(ByteValueChangeEvent[] events) {
// go through all events and if any event changes the MCU then redraw this part for the new
// MCU
for (ByteValueChangeEvent event : events) {
if (event.name.equals(ByteValues.MCU_CHANGE_EVENT)) {
// Our stale state might have changed, depending on the new MCU value.
// Inform the parent ManagedForm - it will call our isStale() implementation to get
// the actual state and later refresh() if we are actually stale.
getManagedForm().staleStateChanged();
}
}
// TODO Auto-generated method stub
}
private void redraw() {
// This is (indirectly) called by the "Change MCU Type" Action.
// Clear the form and redraw it for the new type
clearForm();
initForm(getManagedForm());
fCurrentMCU = fByteValues.getMCUId();
// and inform the new BitField sections about the input model.
for (IFormPart sectionpart : fPFormParts) {
sectionpart.setFormInput(fByteValues);
}
// Update the layout
getManagedForm().reflow(true);
}
/**
* Clear the form by removing all child <code>SectionPart</code>s from the parent composite and
* all <code>IFormParts</code> from the parent ManagedForm.
* <p>
* Used to prepare this form for a redraw for a different MCU.
* </p>
*
*/
private void clearForm() {
// First remove the parts from the form
IManagedForm form = getManagedForm();
for (IFormPart part : fPFormParts) {
form.removePart(part);
part.dispose();
}
fPFormParts.clear();
// Then remove the sections from the composite
for (Control control : fControls) {
control.dispose();
}
fControls.clear();
form.reflow(true);
}
/**
* @return The width of the widest child BitField section.
*/
public int getMaxWidth() {
int maxwidth = 0;
for (Control control : fControls) {
Point cSize = control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
if (cSize.x > maxwidth) {
maxwidth = cSize.x;
}
}
return maxwidth;
}
}