ilg.gnuarmeclipse.packs.ui.views.PacksView.java Source code

Java tutorial

Introduction

Here is the source code for ilg.gnuarmeclipse.packs.ui.views.PacksView.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Liviu Ionescu.
 * 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:
 *     Liviu Ionescu - initial implementation.
 *******************************************************************************/

package ilg.gnuarmeclipse.packs.ui.views;

import ilg.gnuarmeclipse.core.StringUtils;
import ilg.gnuarmeclipse.packs.core.ConsoleStream;
import ilg.gnuarmeclipse.packs.core.data.DurationMonitor;
import ilg.gnuarmeclipse.packs.core.tree.Leaf;
import ilg.gnuarmeclipse.packs.core.tree.Node;
import ilg.gnuarmeclipse.packs.core.tree.NodeViewContentProvider;
import ilg.gnuarmeclipse.packs.core.tree.PackNode;
import ilg.gnuarmeclipse.packs.core.tree.Property;
import ilg.gnuarmeclipse.packs.core.tree.Selector;
import ilg.gnuarmeclipse.packs.core.tree.Type;
import ilg.gnuarmeclipse.packs.data.DataManager;
import ilg.gnuarmeclipse.packs.data.DataManagerEvent;
import ilg.gnuarmeclipse.packs.data.IDataManagerListener;
import ilg.gnuarmeclipse.packs.jobs.CopyExampleJob;
import ilg.gnuarmeclipse.packs.jobs.InstallJob;
import ilg.gnuarmeclipse.packs.jobs.RemoveJob;
import ilg.gnuarmeclipse.packs.ui.Activator;
import ilg.gnuarmeclipse.packs.ui.Messages;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.console.MessageConsoleStream;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.services.IServiceLocator;

public class PacksView extends ViewPart implements IDataManagerListener {

    /**
     * The ID of the view as specified by the extension.
     */
    public static final String ID = "ilg.gnuarmeclipse.packs.ui.views.PackagesView";

    private static final int AUTOEXPAND_LEVEL = 2;

    // ------------------------------------------------------------------------

    class ViewContentProvider extends NodeViewContentProvider {

        // public Object[] getElements(Object inputElement) {
        // Object[] children = getChildren(inputElement);
        // m_out.print("getElements() =");
        // for (Object child : children) {
        // m_out.print(" " + child.toString());
        // }
        // m_out.println();
        // return children;
        // }

    }

    // ------------------------------------------------------------------------

    class TableLabelProvider implements ITableLabelProvider {

        public Image getColumnImage(Object obj, int columnIndex) {

            switch (columnIndex) {
            case 0:
                // String imageKey;
                Leaf node = ((Leaf) obj);
                String type = node.getType();

                if (Type.VENDOR.equals(type)) {
                    // imageKey = ISharedImages.IMG_OBJ_FOLDER;
                    // return PlatformUI.getWorkbench().getSharedImages()
                    // .getImage(imageKey);
                    return Activator.getInstance().getImage("pack_folder");
                } else if (Type.PACKAGE.equals(type)) {
                    if (node.isBooleanProperty(Property.INSTALLED)) {
                        return Activator.getInstance().getImage("package_obj");
                    } else {
                        return Activator.getInstance().getImage("package_obj_grey");
                    }
                } else if (Type.VERSION.equals(type)) {
                    if (node.isBooleanProperty(Property.INSTALLED)) {
                        return Activator.getInstance().getImage("jtypeassist_co");
                    } else {
                        return Activator.getInstance().getImage("jtypeassist_co_grey");
                    }
                } else if (Type.EXAMPLE.equals(type)) {
                    return Activator.getInstance().getImage("binaries_obj");
                }
            }
            return null;
        }

        public String getColumnText(Object obj, int columnIndex) {

            Leaf node = ((Leaf) obj);

            switch (columnIndex) {
            case 0:
                String name = node.getName();
                if (node.isBooleanProperty(Property.INSTALLED)) {
                    name += " (installed)";
                } else {
                    if (node.isType(Type.VERSION)) {
                        String size = node.getProperty(Property.ARCHIVE_SIZE);
                        if (size.length() > 0) {
                            try {
                                int n = Integer.parseInt(size);
                                if (n <= 0) {
                                    name += " (n/a)";
                                } else {
                                    name += " (" + StringUtils.convertSizeToString(n) + ")";
                                }
                            } catch (NumberFormatException e) {
                                ;
                            }
                        }
                    }
                }
                return " " + name;

            case 1:
                // On Linux, the cell is multi-line, so we keep only the first
                // line of a multi-line description.
                // TODO: check if it can be set to single line.
                String description = node.getDescription();
                String[] lines = description.split("\\r?\\n"); //$NON-NLS-1$
                description = lines[0];
                return " " + description;
            }
            return null;
        }

        @Override
        public void addListener(ILabelProviderListener listener) {
        }

        @Override
        public void dispose() {
        }

        @Override
        public boolean isLabelProperty(Object element, String property) {
            return false;
        }

        @Override
        public void removeListener(ILabelProviderListener listener) {
        }
    }

    // ------------------------------------------------------------------------

    class NameSorter extends ViewerSorter {

        @SuppressWarnings("unchecked")
        public int compare(Viewer viewer, Object e1, Object e2) {

            Leaf n1 = (Leaf) e1;
            String name1 = n1.getName();
            String name2 = ((Leaf) e2).getName();

            if (n1.isType(Type.VERSION)) {
                // Reverse the order for versions
                return getComparator().compare(name2, name1);
            } else {
                return getComparator().compare(name1, name2);
            }
        }
    }

    // ------------------------------------------------------------------------

    private Composite fComposite;
    private TreeViewer fViewer;
    private ISelectionListener fPageSelectionListener;
    private ViewContentProvider fContentProvider;

    private Action fUpdateAction;
    private Action fInstallAction;
    private Action fRemoveAction;
    private Action fCopyExampleAction;
    private Action fExpandAll;
    private Action fCollapseAll;

    private PacksFilter fPacksFilter;
    private ViewerFilter[] fPacksFilters;
    private boolean fIsInstallEnabled;
    private boolean fIsRemoveEnabled;
    private boolean fIsCopyExampleEnabled;

    private DataManager fDataManager;
    private MessageConsoleStream fOut;

    public PacksView() {

        fOut = ConsoleStream.getConsoleOut();

        fDataManager = DataManager.getInstance();
    }

    public TreeViewer getTreeViewer() {
        return fViewer;
    }

    public void createPartControl(Composite parent) {

        // System.out.println("PacksView.createPartControl()");

        fComposite = parent;

        fPacksFilter = new PacksFilter();
        fPacksFilters = new PacksFilter[] { fPacksFilter };

        Tree tree = new Tree(parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL);
        tree.setHeaderVisible(true);
        tree.setLinesVisible(true);

        // You can only add the Layout to a container whose only child is the
        // Tree control you want the Layout applied to. Don't assign the layout
        // directly the Tree

        TreeColumnLayout layout = new TreeColumnLayout();
        parent.setLayout(layout);

        TreeColumn nameColumn = new TreeColumn(tree, SWT.NONE);
        nameColumn.setText("  Name");
        // nameColumn.setWidth(200);
        layout.setColumnData(nameColumn, new ColumnPixelData(200));

        TreeColumn descriptionColumn = new TreeColumn(tree, SWT.NONE);
        descriptionColumn.setAlignment(SWT.LEFT);
        descriptionColumn.setText(" Description");
        // descriptionColumn.setWidth(450);
        layout.setColumnData(descriptionColumn, new ColumnPixelData(450));

        fViewer = new TreeViewer(tree);
        // TODO: change to a more elaborate widget, that allows tooltips

        fContentProvider = new ViewContentProvider();

        // Register this view to the packs storage notifications
        fDataManager.addListener(this);

        fViewer.setContentProvider(fContentProvider);
        fViewer.setLabelProvider(new TableLabelProvider());
        fViewer.setSorter(new NameSorter());

        fViewer.setAutoExpandLevel(AUTOEXPAND_LEVEL);
        fViewer.setInput(getPacksTree());

        addProviders();
        addListners();
        hookPageSelection();

        makeActions();
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();
    }

    public void dispose() {

        super.dispose();

        if (fPageSelectionListener != null) {
            getSite().getPage().removePostSelectionListener(fPageSelectionListener);
        }

        fDataManager.removeListener(this);

        if (Activator.getInstance().isDebugging()) {
            System.out.println("PacksView.dispose()");
        }
    }

    private void addProviders() {

        // Register this viewer as a selection provider
        getSite().setSelectionProvider(fViewer);
    }

    private void addListners() {

        fViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(SelectionChangedEvent event) {

                IStructuredSelection selection = (IStructuredSelection) event.getSelection();

                updateButtonsEnableStatus(selection);

                // System.out.println("Packs Selected: " + selection.toList());
            }
        });
    }

    private void hookPageSelection() {

        fPageSelectionListener = new ISelectionListener() {

            @Override
            public void selectionChanged(IWorkbenchPart part, ISelection selection) {

                if ((part instanceof DevicesView) || (part instanceof BoardsView)
                        || (part instanceof KeywordsView)) {
                    friendViewSelectionChanged(part, selection);
                }
            }
        };
        getSite().getPage().addPostSelectionListener(fPageSelectionListener);
    }

    // Called when selection in the _part_View change
    protected void friendViewSelectionChanged(IWorkbenchPart part, ISelection selection) {

        if (selection.isEmpty()) {

            // System.out.println("Packs: resetFilters()");
            fViewer.expandToLevel(AUTOEXPAND_LEVEL);
            fViewer.resetFilters();

            return;
        }

        // System.out.println("Packs: " + part + " selection=" + selection);

        IStructuredSelection structuredSelection = (IStructuredSelection) selection;

        String selectorType = "";
        if (part instanceof DevicesView) {
            selectorType = Selector.DEVICEFAMILY_TYPE;
        } else if (part instanceof BoardsView) {
            selectorType = Selector.BOARD_TYPE;
        } else if (part instanceof KeywordsView) {
            selectorType = Selector.KEYWORD_TYPE;
        }

        fPacksFilter.setSelection(selectorType, structuredSelection);

        fViewer.expandToLevel(AUTOEXPAND_LEVEL);
        fViewer.setFilters(fPacksFilters);

        fViewer.expandToLevel(AUTOEXPAND_LEVEL);
        fViewer.setSelection(null);
    }

    public void updateButtonsEnableStatus(IStructuredSelection selection) {

        if (selection == null || selection.isEmpty()) {
            // System.out.println("Empty Selection");
            return;
        }

        if (((Leaf) selection.getFirstElement()).isType(Type.NONE)) {
            return;
        }

        fIsInstallEnabled = false;
        fIsRemoveEnabled = false;
        fIsCopyExampleEnabled = false;

        for (Object obj : selection.toArray()) {
            Leaf node = (Leaf) obj;
            String type = node.getType();

            boolean isInstalled = false;
            if (node.isBooleanProperty(Property.INSTALLED)) {
                isInstalled = true;
            }

            // Check if the selection contain any package or
            // version not installed
            if (Type.PACKAGE.equals(type)) {
                if (!isInstalled) {
                    fIsInstallEnabled = true;
                }
            }
            if (Type.VERSION.equals(type)) {
                int size = 0;
                try {
                    size = Integer.valueOf(node.getProperty(Property.ARCHIVE_SIZE, "0"));
                } catch (NumberFormatException e) {
                    ;
                }
                if (!isInstalled && size > 0) {
                    fIsInstallEnabled = true;
                }
                if (isInstalled) {
                    fIsRemoveEnabled = true;
                }
            }
            if ((Type.EXAMPLE.equals(type))) {
                fIsCopyExampleEnabled = true;
            }
        }
        fInstallAction.setEnabled(fIsInstallEnabled);
        fRemoveAction.setEnabled(fIsRemoveEnabled);
    }

    private void hookContextMenu() {

        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                PacksView.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(fViewer.getControl());
        fViewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, fViewer);
    }

    private void contributeToActionBars() {

        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }

    // Top down arrow
    private void fillLocalPullDown(IMenuManager manager) {

        manager.add(fExpandAll);
        manager.add(fCollapseAll);
        manager.add(new Separator());
        manager.add(fInstallAction);
        manager.add(fRemoveAction);
        manager.add(new Separator());
        manager.add(fUpdateAction);

        // manager.add(action1);
        // manager.add(new Separator());
        // manager.add(action2);
    }

    // Right click actions
    private void fillContextMenu(IMenuManager manager) {

        if (fIsInstallEnabled) {
            manager.add(fInstallAction);
        }

        if (fIsRemoveEnabled) {
            manager.add(fRemoveAction);
        }

        if (fIsCopyExampleEnabled) {
            manager.add(fCopyExampleAction);
        }

        // manager.add(new Separator());

        // Other plug-ins can contribute there actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    // Top tool bar buttons
    private void fillLocalToolBar(IToolBarManager manager) {

        manager.add(fExpandAll);
        manager.add(fCollapseAll);
        manager.add(new Separator());
        manager.add(fInstallAction);
        manager.add(fRemoveAction);
        manager.add(new Separator());
        manager.add(fUpdateAction);
    }

    private void makeActions() {

        fUpdateAction = new Action() {

            public void run() {

                // Obtain IServiceLocator implementer, e.g. from
                // PlatformUI.getWorkbench():
                // IServiceLocator serviceLocator = PlatformUI.getWorkbench();
                // or a site from within a editor or view:
                IServiceLocator serviceLocator = getSite();

                ICommandService commandService = (ICommandService) serviceLocator.getService(ICommandService.class);

                try {
                    // Lookup commmand with its ID
                    Command command = commandService.getCommand("ilg.gnuarmeclipse.packs.commands.updateCommand");

                    // Optionally pass a ExecutionEvent instance, default
                    // no-param arg creates blank event
                    command.executeWithChecks(new ExecutionEvent());

                } catch (Exception e) {

                    Activator.log(e);
                }
            }
        };
        fUpdateAction.setText(Messages.PacksView_UpdateAction_text);
        fUpdateAction.setToolTipText(Messages.PacksView_UpdateAction_toolTipText);
        fUpdateAction.setImageDescriptor(
                Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/refresh_nav.gif"));

        // -----
        fInstallAction = new Action() {

            public void run() {

                // System.out.println("m_installAction.run();");

                TreeSelection selection = (TreeSelection) fViewer.getSelection();
                if (Activator.getInstance().isDebugging()) {
                    System.out.println(selection);
                }

                Job job = new InstallJob("Install Packs", selection);
                job.schedule();
            }
        };
        fInstallAction.setText(Messages.PacksView_InstallAction_text);
        fInstallAction.setToolTipText(Messages.PacksView_InstallAction_toolTipText);
        fInstallAction.setImageDescriptor(
                Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/package_mode.png"));
        fInstallAction.setEnabled(false);

        // -----
        fRemoveAction = new Action() {

            public void run() {

                // System.out.println("m_removeAction.run();");

                TreeSelection selection = (TreeSelection) fViewer.getSelection();
                // System.out.println(selection);

                Job job = new RemoveJob("Remove Packs", selection);
                job.schedule();
            }
        };
        fRemoveAction.setText(Messages.PacksView_RemoveAction_text);
        fRemoveAction.setToolTipText(Messages.PacksView_RemoveAction_toolTipText);
        fRemoveAction.setImageDescriptor(
                Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/removeall.png"));
        fRemoveAction.setEnabled(false);

        // -----
        fCopyExampleAction = new Action() {

            public void run() {

                TreeSelection selection = (TreeSelection) fViewer.getSelection();
                if (!selection.isEmpty()) {

                    CopyExampleDialog dlg = new CopyExampleDialog(fComposite.getShell(), selection);
                    if (dlg.open() == Dialog.OK) {
                        String out[] = dlg.getData();

                        if (checkCopyDestinationFolders(selection, out)) {
                            Job job = new CopyExampleJob("Copy example", selection, out);
                            job.schedule();
                        }
                    }
                }
            }
        };
        fCopyExampleAction.setText("Copy to folder");

        // -----
        fExpandAll = new Action() {

            public void run() {
                fViewer.expandAll();
            }
        };

        fExpandAll.setText(Messages.PacksView_ExpandAll_text);
        fExpandAll.setToolTipText(Messages.PacksView_ExpandAll_toolTipText);
        fExpandAll.setImageDescriptor(
                Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/expandall.png"));

        fCollapseAll = new Action() {

            public void run() {
                fViewer.collapseAll();
            }
        };

        fCollapseAll.setText(Messages.PacksView_CollapseAll_text);
        fCollapseAll.setToolTipText(Messages.PacksView_CollapseAll_toolTipText);
        fCollapseAll.setImageDescriptor(
                Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/collapseall.png"));
    }

    private void hookDoubleClickAction() {
        ; // None
    }

    /**
     * Passing the focus request to the viewer's control.
     */
    public void setFocus() {
        fViewer.getControl().setFocus();
    }

    public void refresh() {
        fViewer.refresh();
    }

    public void refresh(Object obj) {

        if (obj instanceof Collection<?>) {
            for (Object node : (Collection<?>) obj) {
                fViewer.refresh(node);
            }
        } else {
            fViewer.refresh(obj);
        }

        // Setting the selection will force the outline update
        fViewer.setSelection(fViewer.getSelection());

        // Return focus to this view
        setFocus();

        if (Activator.getInstance().isDebugging()) {
            System.out.println("PacksView.refresh() " + obj);
        }
    }

    public void update(Object obj) {

        if (obj instanceof Collection<?>) {
            for (Object node : (Collection<?>) obj) {
                fViewer.update(node, null);
            }
        } else {
            fViewer.update(obj, null);
        }
        if (Activator.getInstance().isDebugging()) {
            System.out.println("PacksView.updated() " + obj);
        }
    }

    public String toString() {
        return "PacksView";
    }

    // ------------------------------------------------------------------------

    private boolean checkCopyDestinationFolders(TreeSelection selection, String[] param) {

        IPath m_destFolderPath = new Path(param[0]);

        boolean isNonEmpty = false;
        for (Object sel : selection.toList()) {

            PackNode exampleNode = (PackNode) sel;

            Leaf outlineExampleNode = exampleNode.getOutline().findChild(Type.EXAMPLE);

            String exampleRelativeFolder = outlineExampleNode.getProperty(Node.FOLDER_PROPERTY);

            File destFolder = m_destFolderPath.append(exampleRelativeFolder).toFile();

            if (destFolder.isDirectory() && (destFolder.listFiles().length > 0)) {
                isNonEmpty = true;
                break;
            }

        }

        if (isNonEmpty) {

            String msg = "One of the destination folders is not empty.";
            msg += "\nDo you agree to delete the previous content?";

            String[] buttons = new String[] { "OK", "Cancel" };
            MessageDialog dlg = new MessageDialog(fComposite.getShell(), null, null, msg, MessageDialog.ERROR,
                    buttons, 0);
            if (dlg.open() == 0) {
                return true; // OK
            }

            return false;
        }
        return true;
    }

    // ------------------------------------------------------------------------

    // Warning, this code runs on the notifier thread, hopefully will not
    // interfere with GUI actions.

    @Override
    public void packsChanged(DataManagerEvent event) {

        String type = event.getType();
        // System.out.println("PacksView.packsChanged(), type=\"" + type +
        // "\".");

        if (DataManagerEvent.Type.NEW_INPUT.equals(type)) {

            // Run the refresh on the GUI thread
            Display.getDefault().asyncExec(new Runnable() {

                @Override
                public void run() {

                    // m_out.println("PacksView NEW_INPUT");

                    ((TreeViewer) fViewer).setAutoExpandLevel(AUTOEXPAND_LEVEL);
                    fViewer.setInput(getPacksTree());
                }
            });

            // } else if (DataManagerEvent.Type.REFRESH_ALL.equals(type)) {
            //
            // // Run the refresh on the GUI thread
            // Display.getDefault().asyncExec(new Runnable() {
            //
            // @Override
            // public void run() {
            //
            // // m_out.println("PacksView REFRESH_ALL");
            //
            // fViewer.refresh();
            // }
            // });
            //
        } else if (DataManagerEvent.Type.UPDATE_VERSIONS.equals(type)) {

            @SuppressWarnings("unchecked")
            final List<PackNode> updatedList = (List<PackNode>) event.getPayload();

            final Map<String, Node> parentsMap = new HashMap<String, Node>();
            for (PackNode versionNode : updatedList) {
                String vendorName = versionNode.getProperty(Property.VENDOR_NAME);
                String packName = versionNode.getProperty(Property.PACK_NAME);
                String versionName = versionNode.getProperty(Property.VERSION_NAME);

                Node modelNode = fDataManager.findPackVersion(vendorName, packName, versionName);
                updateVersioNode(versionNode, modelNode);

                String key = fDataManager.makeMapKey(vendorName, packName);

                Node parent = versionNode.getParent();
                if (!parentsMap.containsKey(key)) {
                    parentsMap.put(key, parent);
                }
            }

            for (Node packNode : parentsMap.values()) {

                // Compute if the parent has any installed child
                boolean hasInstalledChildren = false;
                for (Leaf child : packNode.getChildren()) {
                    if (child.isBooleanProperty(Property.INSTALLED)) {
                        hasInstalledChildren = true;
                        break;
                    }
                }

                packNode.setBooleanProperty(Property.INSTALLED, hasInstalledChildren);
            }

            // Run the refresh on the GUI thread
            Display.getDefault().asyncExec(new Runnable() {

                @Override
                public void run() {

                    // Refresh pack node, this will update all version
                    // and examples below them
                    // refresh(parentsMap.values());
                    fViewer.refresh();

                    updateButtonsEnableStatus((IStructuredSelection) fViewer.getSelection());
                }
            });
        }
    }

    // ------------------------------------------------------------------------

    // Get view data from storage.
    // Return a hierarchy of vendor/packages/versions/examples nodes.
    private Node getPacksTree() {

        // Node packsTree = fStorage.getPacksTree();
        final Node packsTree = DataManager.getInstance().getRepositoriesTree();

        final Node packsRoot = new Node(Type.ROOT);
        packsRoot.setName("Packs");

        if (packsTree.hasChildren()) {

            (new DurationMonitor()).displayTimeAndRun(new Runnable() {

                public void run() {

                    fOut.println("Collecting packs...");

                    int count = 0;
                    try {
                        count = getPacksRecursive(packsTree, null, packsRoot);
                    } catch (Exception e) {
                        Activator.log(e);
                    }
                    if (packsRoot.hasChildren()) {
                        fOut.println("Found " + count + " package version(s), from "
                                + packsRoot.getChildren().size() + " vendor(s).");
                    } else {
                        fOut.println("Found none.");
                    }
                }
            });
        }

        if (!packsRoot.hasChildren()) {

            Node empty = Node.addNewChild(packsRoot, Type.NONE);
            empty.setName("(no packages)");
            empty.setDescription("Press the Refresh button to register all available packages.");
        }

        return packsRoot;
    }

    // Identify outline & external nodes and collect devices from inside.
    private int getPacksRecursive(Leaf modelNode, PackNode parentPackNode, Node root) {

        int count = 0;

        if (modelNode.isType(Type.PACKAGE)) {
            parentPackNode = (PackNode) modelNode;
        }

        if (modelNode.isType(Type.VERSION)) {

            count += addVersion((PackNode) modelNode, parentPackNode, root);

        } else if (modelNode instanceof Node && modelNode.hasChildren()) {

            for (Leaf child : ((Node) modelNode).getChildren()) {

                // Recurse down.
                count += getPacksRecursive(child, parentPackNode, root);
            }
        }

        return count;
    }

    private int addVersion(PackNode modelNode, PackNode parentPackNode, Node tree) {

        int count = 0;
        String vendorName = modelNode.getProperty(Property.VENDOR_NAME);
        String packName = modelNode.getProperty(Property.PACK_NAME);
        String versionName = modelNode.getProperty(Property.VERSION_NAME);
        String description = modelNode.getDescription();

        Node vendorNode = Node.addUniqueChild(tree, Type.VENDOR, vendorName);

        PackNode packNode = PackNode.addUniqueChild(vendorNode, Type.PACKAGE, packName);

        if (parentPackNode != null) {
            packNode.setDescription(parentPackNode.getDescription());
        }

        // Copy properties like INSTALLED.
        packNode.copyProperties(parentPackNode);
        packNode.putProperty(Property.VENDOR_NAME, vendorName);

        // Copy selectors.
        packNode.copySelectorsRef(parentPackNode);

        PackNode versionNode = PackNode.addUniqueChild(packNode, Type.VERSION, versionName);

        versionNode.setDescription(description);

        // Copy properties to the view node.
        versionNode.copyProperties(modelNode);

        updateVersioNode(versionNode, modelNode);

        // To save space, the brief or full outlines are not prepared
        // for all nodes, but only for nodes needed by selections.

        // If there will be error conditions preventing this to work,
        // the count should not be incremented.
        count++;

        return count;
    }

    private void updateVersioNode(PackNode versionNode, Node modelNode) {

        if (versionNode.isBooleanProperty(Property.INSTALLED)) {

            assert (modelNode != null);

            // For the installed nodes, add examples as children.
            Node outlineNode = (Node) modelNode.findChild(Type.OUTLINE);
            if (outlineNode != null && outlineNode.hasChildren()) {

                for (Leaf child : outlineNode.getChildren()) {

                    if (child.isType(Type.EXAMPLE)) {

                        // New node with child personality
                        // (must be PackNode to accommodate outline).
                        Node node = PackNode.addNewChild(versionNode, child);

                        // Pass "example.name" to view
                        node.copyProperties(child);
                    }
                }
            }
        } else {

            // For the removed nodes, remove all children.
            versionNode.removeChildren();
        }

        // Clear outline.
        versionNode.setOutline(null);
    }

    // ------------------------------------------------------------------------

}