de.innot.avreclipse.ui.views.avrdevice.AVRDeviceView.java Source code

Java tutorial

Introduction

Here is the source code for de.innot.avreclipse.ui.views.avrdevice.AVRDeviceView.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.views.avrdevice;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.ViewPart;

import de.innot.avreclipse.PluginIDs;
import de.innot.avreclipse.core.properties.AVRProjectProperties;
import de.innot.avreclipse.core.properties.ProjectPropertyManager;
import de.innot.avreclipse.core.util.AVRMCUidConverter;
import de.innot.avreclipse.devicedescription.ICategory;
import de.innot.avreclipse.devicedescription.IDeviceDescription;
import de.innot.avreclipse.devicedescription.IDeviceDescriptionProvider;
import de.innot.avreclipse.devicedescription.IProviderChangeListener;
import de.innot.avreclipse.devicedescription.avrio.AVRiohDeviceDescriptionProvider;

/**
 * This is the main part of the AVR Device Explorer View.
 * 
 * @author Thomas Holland
 * @version 1.1
 */

public class AVRDeviceView extends ViewPart {

    // The parent Composite of this Viewer
    private Composite fViewParent;

    private Composite fTop;
    private ComboViewer fCombo;

    private Composite fSourcesComposite;
    private final List<Label> fSourcesLabels = new ArrayList<Label>(0);
    private final List<Text> fSourcesTexts = new ArrayList<Text>(0);

    private CTabFolder fTabFolder;
    private final List<CTabItem> fTabs = new ArrayList<CTabItem>(0);
    private Map<String, List<TreeColumn>> fTreeColumns;

    private IMemento fMemento;

    private IDeviceDescriptionProvider dmprovider = null;

    private IProviderChangeListener fProviderChangeListener;
    private ISelectionListener fWorkbenchSelectionListener;

    /**
     * The constructor.
     * 
     * Nothing done here.
     */
    public AVRDeviceView() {
    }

    /*
     * (non-Javadoc) Method declared on IViewPart.
     */
    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        // Initialize the SuperClass and store the passed memento for use by
        // the individual methods.
        super.init(site, memento);
        fMemento = memento;
    }

    @Override
    public void saveState(IMemento memento) {
        // Save the current state of the viewer
        super.saveState(memento);

        // TODO: Save the Column Layout for each category

    }

    /**
     * Create, layout and initialize the controls of this viewer
     */
    @Override
    public void createPartControl(Composite parent) {

        fViewParent = parent;

        // All listeners that are need to unregistered on dispose()
        fProviderChangeListener = new ProviderChangeListener();
        fWorkbenchSelectionListener = new WorkbenchSelectionListener();

        // Get the default AVRiohDeviceDescriptionProvider
        // TODO: once more than one DeviceDescriptionProvider exist,
        // this has to be changed.
        dmprovider = AVRiohDeviceDescriptionProvider.getDefault();

        if (dmprovider != null)
            // setup ourself as a change listener for the
            // DeviceDescriptionProvider
            dmprovider.addProviderChangeListener(fProviderChangeListener); // ProviderChangeListener

        fViewParent.setLayout(new GridLayout());

        // TODO: TreeColumn Map is dependent on devicemodelprovider
        fTreeColumns = new HashMap<String, List<TreeColumn>>();

        // Layout the top part, which consists of the MCU Type selection Combo
        // and a composite of the sources Labels and Texts
        // The Combo gets 33% of the space, the sources composite the other 67%
        GridLayout gl = new GridLayout(3, true);
        gl.marginHeight = 0;
        gl.marginWidth = 0;

        fTop = new Composite(fViewParent, SWT.NONE);
        fTop.setLayout(gl);
        fTop.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));

        fCombo = new ComboViewer(fTop, SWT.READ_ONLY | SWT.DROP_DOWN);
        fCombo.getControl().setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
        fCombo.setContentProvider(new DeviceListContentProvider());
        fCombo.setLabelProvider(new ComboLabelProvider());

        // register the combo as a Selection Provider
        getSite().setSelectionProvider(fCombo);

        fSourcesComposite = new Composite(fTop, SWT.NONE);
        fSourcesComposite.setLayout(new RowLayout());
        fSourcesComposite.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1));
        new Label(fSourcesComposite, SWT.NONE).setText("source:");

        // The bottom part consists of a TabFolder Control which will take all
        // space not used by the top part.
        fTabFolder = new CTabFolder(fViewParent, SWT.BORDER);
        fTabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        fCombo.addSelectionChangedListener(new ComboSelectionChangedListener());
        fTabFolder.addSelectionListener(new TabFolderSelectionListener());

        // This will -in turn- cause all the data sub-widgets to be
        // initialized and displayed
        providerChanged();

        // Activate the Workbench selection listener
        getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(fWorkbenchSelectionListener);

    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
     */
    @Override
    public void setFocus() {
        // Passing the focus request to the treeviewer of the selected tab
        CTabItem item = fTabFolder.getSelection();
        if (item != null) {
            TreeViewer tv = (TreeViewer) item.getData();
            tv.getControl().setFocus();
        }
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.ui.part.WorkbenchPart#dispose()
     */
    @Override
    public void dispose() {
        // remove the listeners from their objects
        dmprovider.removeProviderChangeListener(fProviderChangeListener);
        getSite().getWorkbenchWindow().getSelectionService()
                .removePostSelectionListener(fWorkbenchSelectionListener);
        super.dispose();
    }

    /**
     * Update the controls which make up the sources composite.
     * <p>
     * The sources are shown in a "breadcrumb" fashion, with a ">>" between the names of the source
     * files.
     * </p>
     * <p>
     * This will handle arbitrary numbers of sources. While this is not really required today with
     * maximum two sources, it is ready in case the structure of the include files is changed.
     * </p
     */
    private void updateSourcelist(Composite parent, IDeviceDescription device) {

        Text txt = null;
        Label lbl = null;
        List<String> sources = device.getSourcesList();

        // Strategy: iterate over all elements of the sources list, and try to
        // get a Text control from the existing text controls. If this fails
        // with an Exception a new Text control is created and added to the
        // internal list fSourcesTexts. After each Text control a Label with
        // ">>" is added, but the last of these labels is hidden again.
        //
        // After the list has been iterated, any Text and Label Controls that
        // are left over in the list will be disposed.
        int txtcounter = 0;
        for (String path : sources) {
            try {
                txt = fSourcesTexts.get(txtcounter);
                lbl = fSourcesLabels.get(txtcounter);
            } catch (IndexOutOfBoundsException ioobe) {
                txt = new Text(parent, SWT.NONE);
                txt.setEditable(false);
                // make it look like a link (grey background, dark blue color
                txt.setBackground(parent.getBackground());
                txt.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE));
                txt.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
                Listener srclistener = new SourceSelectionMouseHandler();
                txt.addListener(SWT.MouseEnter, srclistener);
                txt.addListener(SWT.MouseExit, srclistener);
                txt.addListener(SWT.MouseUp, srclistener);
                fSourcesTexts.add(txt);

                // create a ">>" label
                lbl = new Label(parent, SWT.NONE);
                lbl.setText(">>");
                fSourcesLabels.add(lbl);
            }
            txt.setText(path);
            lbl.setVisible(true);
            txtcounter++;
        }

        // hide the last label
        fSourcesLabels.get(txtcounter - 1).setVisible(false);

        // dispose and remove any remaining Texts / Labels
        // The loop will end with an Exception once the last list element has
        // been removed and we try to read the same index again.
        try {
            while (true) {
                txt = fSourcesTexts.get(txtcounter);
                txt.dispose();
                fSourcesTexts.remove(txtcounter); // all remaining items
                // are moved up

                lbl = fSourcesLabels.get(txtcounter);
                lbl.dispose();
                fSourcesLabels.remove(txtcounter);
            }
        } catch (IndexOutOfBoundsException ioobe) {
            // do nothing, as this exception is expected
        }

        // Redraw the parent
        parent.pack(true);
    }

    /**
     * Update all tabs.
     * <p>
     * One tab for each Category of the device is created. Each tab also gets a
     * <code>TreeViewer</code> associated to it, accessible via the <code>CTabItem.getData()</code>
     * method.
     * </p>
     * <p>
     * If a new tab has the same name as the previously selected tab, it will be selected as well
     * </p>
     */
    private void updateTabs(CTabFolder parent, IDeviceDescription device) {

        TreeViewer tv = null;
        CTabItem cti = null;
        String activetabname = null;

        // Remember the name of the active tab
        if (parent.getSelection() != null) {
            activetabname = parent.getSelection().getText();
        }

        List<ICategory> categories = device.getCategories();

        // Strategy: iterate over all elements of the category list, and try to
        // get a CTabItem control from the existing CTabItems. If this fails
        // with an Exception a new CTabItem control is created and added to the
        // internal list fTabs. Also a TreeViewer is created and linked to the
        // tab.
        // After the list has been iterated, any CTabItems that
        // are left over in the list will be disposed.
        int cticounter = 0;
        for (ICategory cat : categories) {
            try {
                cti = fTabs.get(cticounter);
            } catch (IndexOutOfBoundsException ioobe) {
                // Tab did not exist: create a new CTabItem and an associated
                // TreeViewer
                cti = new CTabItem(parent, SWT.NONE);
                tv = createTreeView(parent, cat);
                cti.setData(tv);
                cti.setControl(tv.getControl());
                fTabs.add(cti);
            }
            cti.setText(cat.getName());
            tv = (TreeViewer) cti.getData();
            updateTreeView(tv, cat);
            tv.setInput(cat);

            // Check if this tab should be made active
            if (cat.getName().equals(activetabname)) {
                parent.setSelection(cti);
            }
            cticounter++;
        }

        // dispose and remove any remaining CTabItems and associated TreeViewers
        // The loop will end with an Exception once the last list element has
        // been removed and we try to read the same index again.
        try {
            while (true) {
                cti = fTabs.get(cticounter);
                cti.getControl().dispose();
                cti.dispose();
                fTabs.remove(cticounter); // all remaining items are moved up
            }
        } catch (IndexOutOfBoundsException ioobe) {
            // do nothing, as this exception is expected
        }

        // If no Tab is active then activate the first one
        if (parent.getSelectionIndex() == -1) {
            parent.setSelection(0);
        }

    }

    /**
     * Create a new TreeViewer for the given Category.
     * 
     * It is up to the caller to dispose the <code>TreeViewer</code> when it is no longer needed.
     * 
     * @return A newly created TreeViewer.
     */
    private TreeViewer createTreeView(Composite parent, ICategory category) {

        TreeViewer tv = new TreeViewer(parent);
        tv.setContentProvider(new DeviceModelContentProvider());
        tv.setLabelProvider(new EntryLabelProvider());
        tv.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
        tv.getTree().setHeaderVisible(true);
        tv.getTree().setLinesVisible(true);

        // TODO: add an Action on the header for sorting

        tv.setInput(category);

        return tv;
    }

    /**
     * Updates a TreeViewer to a new Category.
     * <p>
     * If the Category with this name does not yet exist the required <code>TreeColumns</code> will
     * be created and initialized to their default values. If a Category with this name has already
     * existed (even from a previous device>, then the <code>TreeColumns</code> will be reused.
     * Therefore any changes by the user are preserved.
     * </p>
     * 
     * @param parent
     * @param cat
     *            The Category which the parent will use as Content
     */
    private void updateTreeView(TreeViewer parent, ICategory cat) {

        List<TreeColumn> columnlist;
        String catname = cat.getName();

        // have a somewhat reasonable layout regardless of font size.
        // Yes, I know that height is not the same as width, but with
        // proportional fonts its the next best thing.
        FontData curfd[] = parent.getTree().getFont().getFontData();
        int approxfontwidth = curfd[0].getHeight();

        // See if a column layout for this Category already exists
        columnlist = fTreeColumns.get(catname);
        if (columnlist == null) {
            // No: create the columns for this category name
            columnlist = new ArrayList<TreeColumn>(cat.getColumnCount());

            // TODO: pass more information from the Category, like alignment,
            // icon etc.
            String[] labels = cat.getColumnLabels();
            int[] widths = cat.getColumnDefaultWidths();
            for (int i = 0; i < labels.length; i++) {
                TreeColumn tc = new TreeColumn(parent.getTree(), SWT.LEFT);
                tc.setWidth(widths[i] * approxfontwidth);
                tc.setResizable(true);
                tc.setMoveable(true);
                tc.setText(labels[i]);
                columnlist.add(tc);
            }
            fTreeColumns.put(catname, columnlist);
        } else {
            // Yes:
            // TODO: restore the TreeColumns Layout from the memento
        }
    }

    /**
     * Show an Error message.
     * 
     * @param message
     */
    private void showMessage(String message) {
        MessageDialog.openError(fViewParent.getShell(), "AVR Device Explorer Message", message);
    }

    // called whenever the provider has changed
    private void providerChanged() {

        // pass the current AVRiohDeviceDescriptionProvider to the
        // DeviceListContentProvider
        fCombo.setInput(dmprovider);

        String show = null;
        if (fMemento != null)
            show = fMemento.getString("combovalue");
        if (show == null || "".equals(show)) {
            show = (String) fCombo.getElementAt(0);
        }
        if (show != null) {
            // This next step will cause a SelectionChangeEvent which in turn
            // will load the sources, tabs and treeviewers
            fCombo.setSelection(new StructuredSelection(show), true);
        }

    }

    // When a different MCU Type is selected do the following:
    // - get the new device from the provider
    // - update the sources text elements
    // - Update the tabs of the TabFolder (which will update the treeviewers)
    // - Set the focus to the TreeViewer of the active tab.
    private class ComboSelectionChangedListener implements ISelectionChangedListener {

        public void selectionChanged(SelectionChangedEvent event) {
            String devicename = (String) ((StructuredSelection) event.getSelection()).getFirstElement();
            if (devicename == null) {
                // The new provider does not know the previously selected mcu
                if (fMemento != null) {
                    String oldmcu = fMemento.getString("combovalue");
                    if (oldmcu != null) {
                        String msg = MessageFormat.format("The previously selected mcu [{0}] is not known", oldmcu);
                        showMessage(msg);
                    }
                }

                return;
            }
            if (fMemento != null) {
                // persist the selected mcu
                fMemento.putString("combovalue", devicename);
            }
            String deviceid = AVRMCUidConverter.name2id(devicename);
            IDeviceDescription device = dmprovider.getDeviceDescription(deviceid);
            if (device == null) {
                showMessage(dmprovider.getErrorMessage());
            } else {
                updateSourcelist(fSourcesComposite, device);
                updateTabs(fTabFolder, device);
                // setFocus();
            }
        }
    }

    // When a different tab is selected do the following:
    // - refresh the associated TreeViewer
    // - set the focus to this tabs TreeViewer
    private static class TabFolderSelectionListener implements SelectionListener {

        public void widgetDefaultSelected(SelectionEvent e) {
            // not called for a CTabFolder
        }

        public void widgetSelected(SelectionEvent e) {
            CTabItem ti = (CTabItem) e.item;
            ((TreeViewer) ti.getData()).refresh();
            ((TreeViewer) ti.getData()).getControl().setFocus();
        }
    }

    /**
     * Handle Mouse Events for the source file text widgets.
     * 
     * Three events are handled:
     * <ul>
     * <li><code>MouseEnter</code>: Change background color to show the user that this widget can be
     * clicked on.</li>
     * <li><code>MouseExit</code>: Restore the background color.</li>
     * <li><code>MouseUp</code>: Open the sourcefile associated with the selected widget in an
     * Editor.</li>
     * </ul>
     */
    private class SourceSelectionMouseHandler implements Listener {

        public void handleEvent(Event event) {
            Text txt = (Text) event.widget;
            switch (event.type) {
            case SWT.MouseEnter:
                // change background color to some lighter color
                txt.setBackground(txt.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
                break;
            case SWT.MouseExit:
                // change background color back to normal
                txt.setBackground(txt.getParent().getBackground());
                break;
            case SWT.MouseUp:
                // Open the selected source file
                // get the basepath from the AVRiohDeviceDescriptionProvider,
                // append the content of the selected text widget, get a
                // IFileStore for this file and open an Editor for this
                // file in the active Workbench page.
                // No error checking as this file should exist
                IPath srcfile = dmprovider.getBasePath();
                srcfile = srcfile.append(txt.getText());
                IFileStore fileStore = EFS.getLocalFileSystem().getStore(srcfile);
                if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
                    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
                    try {
                        IDE.openEditorOnFileStore(page, fileStore);
                        // TODO: if any row in the treeview has been selected,
                        // try to scroll the Editor to the associated
                        // definition.
                    } catch (PartInitException e) {
                        // what can cause this?
                        e.printStackTrace();
                    }
                }
                break;
            } // switch
        } // handleevent
    }

    /**
     * Handle Provider Change Events.
     * <p>
     * This is called from the current DeviceDescriptionProvider whenever its internal data has
     * changed. For example, when the user changes the path to the source data.
     * </p>
     */
    private class ProviderChangeListener implements IProviderChangeListener {

        public void providerChange() {
            // We don't know which Threat this comes from.
            // Assume its not the SWT Threat and act accordingly
            fViewParent.getDisplay().asyncExec(new Runnable() {
                public void run() {
                    providerChanged();
                }
            }); // Runnable
        }
    }

    /**
     * Handle Selection Change Events.
     * <p>
     * This is called by the workbench selection services to inform this viewer, that something has
     * been selected on the workbench. If something with an AVR MCU type has been selected
     * (Project), then the viewer will show the description of the associated mcu.
     * </p>
     */
    private class WorkbenchSelectionListener implements ISelectionListener {

        public void selectionChanged(IWorkbenchPart part, final ISelection selection) {
            // we ignore our own selections
            if (part == AVRDeviceView.this) {
                return;
            }

            // To minimize the GUI impact run the rest of this method in a Job
            Job selectionjob = new Job("AVR Device Explorer") {

                @Override
                protected IStatus run(IProgressMonitor monitor) {

                    String newid = null;
                    try {

                        monitor.beginTask("Selection Change", 3);
                        Set<String> devicelist = dmprovider.getMCUList();
                        if (devicelist == null) {
                            // The provider has no mcus -> return immediately
                            return Status.OK_STATUS;
                        }
                        monitor.worked(1);

                        // see if the selection is something that has an avr mcu
                        // id
                        if (selection instanceof IStructuredSelection) {
                            // First: Projects
                            IStructuredSelection ss = (IStructuredSelection) selection;
                            newid = getMCUId(ss);
                        } else if (selection instanceof ITextSelection) {
                            // Second: Selected Text
                            String text = ((ITextSelection) selection).getText();
                            // Test if the text is an MCU name/id
                            if (devicelist.contains(AVRMCUidConverter.name2id(text))) {
                                // yes: use it
                                newid = AVRMCUidConverter.name2id(text);
                            }
                        }
                        monitor.worked(1);
                        if (newid != null) {
                            // Test if it is a valid mcu id. Only set valid mcu
                            // ids
                            // as otherwise an error message would be displayed
                            if (!devicelist.contains(AVRMCUidConverter.name2id(newid))) {
                                return Status.OK_STATUS;
                            }

                            IDeviceDescription device = dmprovider.getDeviceDescription(newid);
                            if (device == null) {
                                // do nothing
                                return Status.OK_STATUS;
                            }
                            // Next cause a SelectionChange Event in the UI
                            // Thread, which handles the rest of the change.
                            // (Only if the control still exists)
                            final IStructuredSelection newselection = new StructuredSelection(
                                    AVRMCUidConverter.id2name(newid));
                            if ((fViewParent != null) && (!fViewParent.isDisposed())) {
                                fViewParent.getDisplay().asyncExec(new Runnable() {
                                    public void run() {
                                        if (fCombo != null && !fCombo.getControl().isDisposed()) {
                                            fCombo.setSelection(newselection, true);
                                        }
                                    }
                                }); // Runnable
                            }
                        }
                        monitor.worked(1);
                    } catch (IOException e) {
                        // could not get a MCU list from the provider
                        // nothing to be done about this but to fail silently.
                    } finally {
                        monitor.done();
                    }
                    return Status.OK_STATUS;
                }
            };
            selectionjob.setSystem(true);
            selectionjob.setPriority(Job.SHORT);
            selectionjob.schedule();
        }

        /**
         * Get the mcu id from the given structured selection.
         * <p>
         * If the first element of the selection is an AVR project, the mcu type is taken from the
         * properties of the active build configuration.
         * </p>
         * <p>
         * If the selection did not contain a valid mcu type <code>null</code> is returned
         * 
         * @param selection
         *            <code>IStructuredSelection</code> from the Eclipse Selection Services
         * @return String with the mcu id or <code>null</null> if no mcu id was found.
         */
        private String getMCUId(IStructuredSelection selection) {

            Object item = selection.getFirstElement();
            if (item == null) {
                return null;
            }

            IProject project = null;

            // See if the given is an IProject (directly or via IAdaptable)
            if (item instanceof IProject) {
                project = (IProject) item;
            } else if (item instanceof IResource) {
                project = ((IResource) item).getProject();
            } else if (item instanceof IAdaptable) {
                IAdaptable adaptable = (IAdaptable) item;
                project = (IProject) adaptable.getAdapter(IProject.class);
                if (project == null) {
                    // Try ICProject -> IProject
                    ICProject cproject = (ICProject) adaptable.getAdapter(ICProject.class);
                    if (cproject == null) {
                        // Try ICElement -> ICProject -> IProject
                        ICElement celement = (ICElement) adaptable.getAdapter(ICElement.class);
                        if (celement != null) {
                            cproject = celement.getCProject();
                        }
                    }
                    if (cproject != null) {
                        project = cproject.getProject();
                    }
                }
            }

            if (project != null) {
                try {
                    IProjectNature nature = project.getNature(PluginIDs.NATURE_ID);
                    if (nature != null) {
                        // This is an AVR Project
                        // Get the AVR properties for the active build
                        // configuration and fetch the mcu id from it.
                        ProjectPropertyManager projprops = ProjectPropertyManager.getPropertyManager(project);
                        AVRProjectProperties props = projprops.getActiveProperties();
                        return props.getMCUId();
                    }
                } catch (CoreException e) {
                    return null;
                }
            } else if (item instanceof String) {
                String mcuname = (String) item;
                String mcuid = AVRMCUidConverter.name2id(mcuname);
                return mcuid;
            }

            // Selection does not contain a mcuid
            return null;
        }

    }
}