de.walware.statet.r.internal.ui.pkgmanager.PkgTab.java Source code

Java tutorial

Introduction

Here is the source code for de.walware.statet.r.internal.ui.pkgmanager.PkgTab.java

Source

/*=============================================================================#
 # Copyright (c) 2012-2015 Stephan Wahlbrink (WalWare.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:
 #     Stephan Wahlbrink - initial API and implementation
 #=============================================================================*/

package de.walware.statet.r.internal.ui.pkgmanager;

import static de.walware.statet.r.internal.ui.pkgmanager.RPkgManagerDialog.NO_INPUT;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.ibm.icu.text.DateFormat;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.databinding.observable.set.WritableSet;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
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.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItemParameter;

import de.walware.ecommons.databinding.jface.DataBindingSupport;
import de.walware.ecommons.ui.actions.HandlerContributionItem;
import de.walware.ecommons.ui.components.DropDownButton;
import de.walware.ecommons.ui.components.History;
import de.walware.ecommons.ui.components.SearchText;
import de.walware.ecommons.ui.content.IElementFilter;
import de.walware.ecommons.ui.content.ObservableSetBinding;
import de.walware.ecommons.ui.content.SearchTextBinding;
import de.walware.ecommons.ui.content.SetElementFilter;
import de.walware.ecommons.ui.content.TableFilterController;
import de.walware.ecommons.ui.content.TextElementFilter;
import de.walware.ecommons.ui.util.AutoCheckController;
import de.walware.ecommons.ui.util.LayoutUtil;
import de.walware.ecommons.ui.util.NestedServices;
import de.walware.ecommons.ui.util.ViewerUtil.CheckboxTableComposite;
import de.walware.ecommons.ui.util.ViewerUtil.TableComposite;
import de.walware.ecommons.ui.util.ViewerUtil.TreeComposite;

import de.walware.rj.renv.IRPkg;

import de.walware.statet.r.core.pkgmanager.IRLibPaths;
import de.walware.statet.r.core.pkgmanager.IRLibPaths.Entry;
import de.walware.statet.r.core.pkgmanager.IRPkgData;
import de.walware.statet.r.core.pkgmanager.IRPkgInfo;
import de.walware.statet.r.core.pkgmanager.IRPkgInfoAndData;
import de.walware.statet.r.core.pkgmanager.IRPkgList;
import de.walware.statet.r.core.pkgmanager.IRPkgManager;
import de.walware.statet.r.core.pkgmanager.IRPkgSet;
import de.walware.statet.r.core.pkgmanager.IRView;
import de.walware.statet.r.core.pkgmanager.RPkgAction;
import de.walware.statet.r.core.pkgmanager.RPkgResolver;
import de.walware.statet.r.core.pkgmanager.RPkgUtil;
import de.walware.statet.r.core.pkgmanager.RRepo;
import de.walware.statet.r.core.renv.IRLibraryLocation;
import de.walware.statet.r.ui.REnvLabelProvider;
import de.walware.statet.r.ui.RUI;

public class PkgTab extends Composite {

    private static final int VERSION_CHARS = 8;

    private static final int AVAIL = 0;
    private static final int INST = 1;

    private class InstalledFilter implements IElementFilter, SelectionListener {

        private static final int INSTALLED = 0x1;
        private static final int NOT_INSTALLED = 0x2;

        private class Final implements IFinalFilter {

            private final int fState;

            public Final(final int state) {
                fState = state;
            }

            @Override
            public boolean select(final Object element) {
                final boolean installed = fPkgSet.getInstalled().containsByName((String) element);
                if ((fState & INSTALLED) != 0) {
                    return installed;
                } else {
                    return !installed;
                }
            }

            @Override
            public boolean isSubOf(final IFinalFilter other) {
                return (other == this || other == null
                        || ((other instanceof Final) && fState == ((Final) other).fState));
            }

            @Override
            public boolean isEqualTo(final IFinalFilter other) {
                return (other == this || ((other instanceof Final) && fState == ((Final) other).fState));
            }

        }

        private volatile int fState;

        private Final fFilter;

        public InstalledFilter() {
            fFilterInstButton.addSelectionListener(this);
            fFilterNotInstButton.addSelectionListener(this);
        }

        @Override
        public void widgetSelected(final SelectionEvent e) {
            if (e.getSource() == fFilterInstButton) {
                fFilterNotInstButton.setSelection(false);
            } else if (e.getSource() == fFilterNotInstButton) {
                fFilterInstButton.setSelection(false);
            }
            int state;
            if (fFilterInstButton.getSelection()) {
                state = INSTALLED;
            } else if (fFilterNotInstButton.getSelection()) {
                state = NOT_INSTALLED;
            } else {
                state = 0;
            }
            if (fState != state) {
                fState = state;
                fFilterController.refresh(true);
            }
        }

        @Override
        public IFinalFilter getFinal(final boolean newData) {
            final int state = fState;
            if (state == 0) {
                fFilter = null;
            } else if (fFilter == null || fFilter.fState != state) {
                fFilter = new Final(state);
            }
            return fFilter;
        }

        @Override
        public void widgetDefaultSelected(final SelectionEvent e) {
        }

    }

    private final RPkgManagerDialog fDialog;
    private final IRPkgManager.Ext fRPkgManager;

    private final TabItem fTab;

    private IRPkgSet.Ext fPkgSet;

    private String fSelectedPkgName;
    private int fSelectedPkgVersionGroup;
    private IRPkgData fSelectedPkgVersion;
    private final Map<String, String> fSelectedPkgVersions = new HashMap<String, String>();

    private SearchText fFilterText;
    private Button fFilterInstButton;
    private Button fFilterNotInstButton;
    private CheckboxTableViewer fFilterPriorityTable;
    private WritableSet fFilterPrioritySet;
    private StackLayout fFilterViewsStack;
    private Link fFilterViewsMessage;
    private CheckboxTableViewer fFilterRViewsTable;
    private WritableSet fFilterRViewsSet;
    private TableFilterController fFilterController;

    private TableComposite fPkgTable;

    private NestedServices fServiceLocator;

    private IHandler2 fRefreshHandler;
    private final History<String> fPkgHistory = new de.walware.ecommons.ui.components.History<String>() {
        @Override
        protected void select(final String entry) {
            showPkg(entry);
        };
    };
    private ToolBarManager fToolBar;

    private TreeComposite fDetailTable;
    private Label fDetailLicense;
    private TreeViewer fDetailDepTable;
    private TreeViewer fDetailRevTable;

    private Button fInstallButton;
    private DropDownButton fUpdateButton;
    private Button fUninstallButton;
    private Button fLoadButton;

    PkgTab(final RPkgManagerDialog dialog, final TabItem tab, final Composite parent,
            final IRPkgManager.Ext rPkgManager) {
        super(parent, SWT.NONE);

        fDialog = dialog;
        fTab = tab;
        fRPkgManager = rPkgManager;

        setLayout(LayoutUtil.createTabGrid(3));
        createContent(this);
        updateButtons();
    }

    private void createContent(final Composite parent) {
        final Composite filterCol = createFilter(parent);
        filterCol.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));

        final Composite tableCol = createTable(parent);
        tableCol.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        final Composite detailCol = createDetail(parent);
        detailCol.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    }

    private Composite createFilter(final Composite parent) {
        final Group composite = new Group(parent, SWT.NONE);
        composite.setLayout(LayoutUtil.createGroupGrid(1));
        composite.setText("Filter");

        {
            final SearchText text = new SearchText(composite);
            fFilterText = text;
            final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
            gd.widthHint = LayoutUtil.hintWidth(text.getTextControl(), 20);
            text.setToolTipText("Name");
            text.setLayoutData(gd);
        }

        {
            //         Label label = new Label(composite, SWT.NONE);
            //         label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
            //         label.setText("State:");
            LayoutUtil.addSmallFiller(composite, false);

            {
                final Button button = new Button(composite, SWT.CHECK);
                fFilterInstButton = button;
                button.setText("Installed");
                button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            }
            //      {   Button button = new Button(composite, SWT.CHECK);
            //         fPkgFilterInstUptButton = button;
            //         button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            //         button.setText("With Update");
            //      }
            {
                final Button button = new Button(composite, SWT.CHECK);
                fFilterNotInstButton = button;
                button.setText("Not Installed");
                button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
            }
        }
        {
            final Label label = new Label(composite, SWT.NONE);
            label.setText("Priorities:");
            label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

            final CheckboxTableViewer viewer = new CheckboxTableViewer(
                    new Table(composite, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.NO_SCROLL));
            fFilterPriorityTable = viewer;
            viewer.setContentProvider(new ArrayContentProvider());
            viewer.setLabelProvider(new LabelProvider());

            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
            gd.heightHint = LayoutUtil.hintHeight(viewer.getTable(), IRPkgSet.Ext.DEFAULT_PRIORITIES.size(), false);
            viewer.getControl().setLayoutData(gd);
        }

        {
            final Label label = new Label(composite, SWT.NONE);
            label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
            label.setText("Task Views:");

            final Composite views = new Composite(composite, SWT.NONE);
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
            views.setLayoutData(gd);
            fFilterViewsStack = new StackLayout();
            views.setLayout(fFilterViewsStack);

            final Link link = new Link(views, SWT.MULTI);
            fFilterViewsMessage = link;
            link.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    showPkg(e.text);
                }
            });
            link.setText("");

            final CheckboxTableComposite table = new CheckboxTableComposite(views,
                    SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
            fFilterRViewsTable = table.viewer;
            table.viewer.setContentProvider(new ArrayContentProvider());
            ColumnViewerToolTipSupport.enableFor(table.viewer, ToolTip.NO_RECREATE);

            final TableViewerColumn column = table.addColumn("", SWT.LEFT, new ColumnWeightData(100, false));
            column.setLabelProvider(new RViewLabelProvider());

            gd.widthHint = LayoutUtil.hintWidth(table.table, 20);
            fFilterViewsStack.topControl = table;
        }

        return composite;
    }

    private Composite createTable(final Composite parent) {
        final Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(LayoutUtil.createCompositeGrid(2));

        {
            final Label label = new Label(composite, SWT.NONE);
            label.setText("Packages:");
            label.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
        }
        {
            fToolBar = new ToolBarManager(SWT.HORIZONTAL | SWT.FLAT);
            final ToolBar toolBar = fToolBar.createControl(composite);
            toolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
        }

        final TableComposite viewer = new TableComposite(composite,
                SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL);
        fPkgTable = viewer;
        viewer.viewer.setUseHashlookup(true);
        viewer.table.setHeaderVisible(true);
        {
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
            gd.heightHint = LayoutUtil.hintHeight(viewer.table, 15);
            gd.widthHint = fDialog.hintWidthInChars(40);
            viewer.setLayoutData(gd);
        }
        ColumnViewerToolTipSupport.enableFor(viewer.viewer, ToolTip.NO_RECREATE);
        {
            final TableViewerColumn column = viewer.addColumn("Name", SWT.LEFT, new ColumnWeightData(50));
            column.setLabelProvider(new CellLabelProvider() {
                @Override
                public void update(final ViewerCell cell) {
                    final String name = (String) cell.getElement();
                    cell.setImage(RUI.getImage((fPkgSet.getInstalled().containsByName(name)) ? RUI.IMG_OBJ_R_PACKAGE
                            : RUI.IMG_OBJ_R_PACKAGE_NOTA));
                    cell.setText(name);
                }

                @Override
                public String getToolTipText(final Object element) {
                    final String name = (String) element;
                    final IRPkgInfoAndData v = fPkgSet.getInstalled().getFirstByName(name);
                    if (v != null) {
                        return v.getTitle();
                    }
                    return null;
                }
            });
        }

        return composite;
    }

    private Composite createDetail(final Composite parent) {
        final Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(LayoutUtil.createCompositeGrid(2));

        createDetailTable(composite);
        createDetailButtons(composite);
        createDetailInfo(composite);

        return composite;
    }

    private void createDetailTable(final Composite parent) {
        {
            final Label label = new Label(parent, SWT.NONE);
            label.setText("&Versions:");
            label.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
        }
        { // for layout
            final Label label = new Label(parent, SWT.NONE);
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false);
            gd.heightHint = fToolBar.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
            label.setLayoutData(gd);
        }

        final TreeComposite composite = new TreeComposite(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
        fDetailTable = composite;
        {
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
            gd.heightHint = LayoutUtil.hintHeight(composite.tree, 6);
            gd.widthHint = fDialog.hintWidthInChars(40);
            composite.setLayoutData(gd);
        }

        composite.viewer.setContentProvider(new DetailGroup.ContentProvider(2) {
            {
                fGroups[AVAIL] = new DetailGroup(AVAIL, "Available");
                fGroups[INST] = new DetailGroup(INST, "Installed");
            }

            @Override
            public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
                if (newInput instanceof String) {
                    final String name = (String) newInput;
                    fGroups[AVAIL].setList(fPkgSet.getAvailable().getByName(name));
                    fGroups[INST].setList(fPkgSet.getInstalled().getByName(name));
                } else {
                    fGroups[AVAIL].clearList();
                    fGroups[INST].clearList();
                }
            }
        });
        composite.viewer.setComparer(new IElementComparer() {
            @Override
            public int hashCode(final Object element) {
                if (element instanceof IRPkgData) {
                    return element.hashCode() + ((IRPkgData) element).getRepoId().hashCode();
                }
                return element.hashCode();
            }

            @Override
            public boolean equals(final Object a, final Object b) {
                if (a == b) {
                    return true;
                }
                if (!a.equals(b)) {
                    return false;
                }
                if (a instanceof IRPkgData) {
                    return ((IRPkgData) a).getRepoId().equals(((IRPkgData) b).getRepoId());
                }
                return false;
            }
        });
        composite.viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);

        {
            final TreeViewerColumn column = composite.addColumn("Repository/Library", SWT.LEFT,
                    new ColumnWeightData(50, fDialog.hintWidthInChars(20), true));
            column.setLabelProvider(new CellLabelProvider() {
                @Override
                public void update(final ViewerCell cell) {
                    final Object element = cell.getElement();
                    if (element instanceof DetailGroup) {
                        cell.setText(((DetailGroup) element).getLabel());
                        return;
                    } else if (element instanceof IRPkgInfo) {
                        final IRLibraryLocation location = ((IRPkgInfo) element).getLibraryLocation();
                        cell.setText(REnvLabelProvider.getSafeLabel(location));
                        return;
                    } else if (element instanceof IRPkgData) {
                        final IRPkgData pkg = (IRPkgData) element;
                        if (pkg.getRepoId() != null) {
                            final RRepo repo = RPkgUtil.getRepoById(fDialog.fRepoTab.getAvailableRepos(),
                                    pkg.getRepoId());
                            if (repo != null) {
                                cell.setText(repo.getName());
                                return;
                            }
                        }
                        cell.setText("-");
                        return;
                    }
                    throw new IllegalStateException();
                }
            });
        }
        {
            final TreeViewerColumn column = composite.addColumn("Version", SWT.LEFT,
                    new ColumnPixelData(fDialog.hintWidthInChars(VERSION_CHARS), true, true));
            column.setLabelProvider(new CellLabelProvider() {
                @Override
                public void update(final ViewerCell cell) {
                    final Object element = cell.getElement();
                    if (element instanceof IRPkgData) {
                        cell.setText(((IRPkgData) element).getVersion().toString());
                        return;
                    }
                    cell.setText(""); //$NON-NLS-1$
                }

                @Override
                public String getToolTipText(final Object element) {
                    return (element instanceof IRPkgData) ? getDetailToolTipText((IRPkgData) element) : null;
                }
            });
        }

        ColumnViewerToolTipSupport.enableFor(composite.viewer);

        composite.viewer.setInput(RPkgManagerDialog.NO_INPUT);
    }

    private String getDetailToolTipText(final IRPkgData pkgData) {
        final StringBuilder sb = new StringBuilder(pkgData.getName());
        sb.append("\nVersion: ").append(pkgData.getVersion());
        if (pkgData instanceof IRPkgInfoAndData) {
            final IRPkgInfoAndData pkgDescr = (IRPkgInfoAndData) pkgData;
            sb.append("\nBuilt: ").append(((IRPkgInfoAndData) pkgData).getBuilt());
            sb.append("\nInstalled: ")
                    .append((pkgDescr.getInstallStamp() != 0)
                            ? DateFormat.getDateTimeInstance().format(pkgDescr.getInstallStamp())
                            : "-");
            final RRepo repo = fRPkgManager.getRepo(pkgDescr.getRepoId());
            if (repo != null) {
                sb.append("\nFrom: ").append(repo.getName());
            }
        }
        return sb.toString();
    }

    private void createDetailButtons(final Composite parent) {
        final Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        composite.setLayout(LayoutUtil.createCompositeGrid(1));

        {
            final Button button = new Button(composite, SWT.PUSH);
            fInstallButton = button;
            button.setText("Install...");
            button.setLayoutData(LayoutUtil.createGD(button));
        }
        {
            final SelectionListener defaultUpdate = new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    doUpdateLatest();
                }
            };
            final DropDownButton button = new DropDownButton(composite);
            final Menu menu = button.getDropDownMenu();
            {
                final MenuItem item = new MenuItem(menu, SWT.PUSH);
                item.setText("&Update (default)...");
                item.addSelectionListener(defaultUpdate);
            }
            {
                final MenuItem item = new MenuItem(menu, SWT.PUSH);
                item.setText("&Reinstall...");
                item.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        doReinstall(null);
                    }
                });
            }
            fUpdateButton = button;
            button.setText("Update...");
            button.addSelectionListener(defaultUpdate);
            button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        }
        {
            final Button button = new Button(composite, SWT.PUSH);
            fUninstallButton = button;
            button.setText("Uninstall");
            button.setLayoutData(LayoutUtil.createGD(button));
        }
        LayoutUtil.addSmallFiller(composite, false);
        {
            final Button button = new Button(composite, SWT.PUSH);
            fLoadButton = button;
            button.setText("Load");
            button.setLayoutData(LayoutUtil.createGD(button));
        }

        final SelectionListener listener = new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                if (e.getSource() == fInstallButton) {
                    doInstall();
                } else if (e.getSource() == fUpdateButton) {
                    doUpdateLatest();
                } else if (e.getSource() == fUninstallButton) {
                    doUninstall();
                } else if (e.getSource() == fLoadButton) {
                    doLoad();
                }
            }
        };
        fInstallButton.addSelectionListener(listener);
        fUninstallButton.addSelectionListener(listener);
        fUpdateButton.addSelectionListener(listener);
        fLoadButton.addSelectionListener(listener);
    }

    private void createDetailInfo(final Composite parent) {
        final Group info = new Group(parent, SWT.NONE);
        info.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        info.setLayout(LayoutUtil.createGroupGrid(2, true));
        info.setText("Info:");

        {
            final Composite properties = new Composite(info, SWT.NONE);
            properties.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
            properties.setLayout(LayoutUtil.createCompositeGrid(2));

            final Label label = new Label(properties, SWT.NONE);
            label.setText("License:");
            label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));

            fDetailLicense = new Label(properties, SWT.NONE);
            fDetailLicense.setText("                ");
            fDetailLicense.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        }
        {
            final Composite col = new Composite(info, SWT.NONE);
            col.setLayout(LayoutUtil.createCompositeGrid(1));
            createDetailRef(col, 0);
            col.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        }
        {
            final Composite col = new Composite(info, SWT.NONE);
            col.setLayout(LayoutUtil.createCompositeGrid(1));
            createDetailRef(col, 1);
            col.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        }
    }

    private void createDetailRef(final Composite parent, final int type) {
        {
            final Label label = new Label(parent, SWT.NONE);
            label.setText((type == 0) ? "Dependencies:" : "Reverse:");
            label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
        }

        final TreeViewer viewer = new TreeViewer(parent, (SWT.BORDER | SWT.SINGLE));
        if (type == 0) {
            fDetailDepTable = viewer;
        } else {
            fDetailRevTable = viewer;
        }
        {
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
            gd.heightHint = LayoutUtil.hintHeight(viewer.getTree(), 12);
            gd.widthHint = fDialog.hintWidthInChars(20);
            viewer.getControl().setLayoutData(gd);
        }

        viewer.setContentProvider(new DetailGroup.ContentProvider(5) {
            private static final int DEPENDS = 0;
            private static final int IMPORTS = 1;
            private static final int LINKINGTO = 2;
            private static final int SUGGESTS = 3;
            private static final int ENHANCES = 4;
            {
                fGroups[DEPENDS] = new DetailGroup(0, "Depends");
                fGroups[IMPORTS] = new DetailGroup(1, "Imports");
                fGroups[LINKINGTO] = new DetailGroup(2, "Linking To");
                fGroups[SUGGESTS] = new DetailGroup(3, "Suggests");
                fGroups[ENHANCES] = new DetailGroup(4, "Enhances");
            }

            @Override
            public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
                if (newInput instanceof IRPkgData) {
                    final IRPkgData pkg = (IRPkgData) newInput;
                    fGroups[DEPENDS].setList(pkg.getDepends());
                    fGroups[IMPORTS].setList(pkg.getImports());
                    fGroups[LINKINGTO].setList(pkg.getLinkingTo());
                    fGroups[SUGGESTS].setList(pkg.getSuggests());
                    fGroups[ENHANCES].setList(pkg.getEnhances());
                } else {
                    fGroups[DEPENDS].clearList();
                    fGroups[IMPORTS].clearList();
                    fGroups[LINKINGTO].clearList();
                    fGroups[SUGGESTS].clearList();
                    fGroups[ENHANCES].clearList();
                }
            }
        });
        viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);

        viewer.setLabelProvider(new StyledCellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final Object element = cell.getElement();
                if (element instanceof DetailGroup) {
                    cell.setText(((DetailGroup) element).getLabel());
                    cell.setStyleRanges(null);
                    return;
                } else if (element instanceof IRPkg) {
                    final IRPkg pkg = (IRPkg) element;
                    final StyledString text = new StyledString();
                    text.append(pkg.getName());
                    final String version = pkg.getVersion().toString();
                    if (!version.isEmpty()) {
                        text.append(" (", StyledString.QUALIFIER_STYLER); //$NON-NLS-1$
                        text.append(version, StyledString.QUALIFIER_STYLER);
                        text.append(")", StyledString.QUALIFIER_STYLER); //$NON-NLS-1$
                    }
                    cell.setText(text.getString());
                    cell.setStyleRanges(text.getStyleRanges());
                    return;
                }
                throw new IllegalStateException();
            }
        });

        viewer.setInput(NO_INPUT);

        viewer.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(final DoubleClickEvent event) {
                final Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
                if (element instanceof IRPkg) {
                    showPkg(((IRPkg) element).getName());
                }
            }
        });
    }

    void createActions() {
        fServiceLocator = new NestedServices(fDialog.fServiceLocator.getLocator(), "Tab");
        fServiceLocator.bindTo(this);

        final IHandlerService handlerService = (IHandlerService) fServiceLocator.getLocator()
                .getService(IHandlerService.class);

        fRefreshHandler = new AbstractHandler() {
            @Override
            public void setEnabled(final Object evaluationContext) {
                final IStatus status = fDialog.fStatus;
                setBaseEnabled(status != null && status.isOK());
            }

            @Override
            public Object execute(final ExecutionEvent event) throws ExecutionException {
                final IStatus status = fDialog.fStatus;
                if (status != null && status.isOK()) {
                    fDialog.doApply(true);
                }
                return null;
            }
        };
        handlerService.activateHandler(IWorkbenchCommandConstants.FILE_REFRESH, fRefreshHandler);
        fToolBar.add(new HandlerContributionItem(
                new CommandContributionItemParameter(fServiceLocator.getLocator(), null,
                        IWorkbenchCommandConstants.FILE_REFRESH, HandlerContributionItem.STYLE_PUSH),
                fRefreshHandler));
        fToolBar.add(new Separator());
        fPkgHistory.addActions(fToolBar, fServiceLocator.getLocator());
        fToolBar.update(true);
    }

    TabItem getTab() {
        return fTab;
    }

    void addBinding(final DataBindingSupport db) {
        fFilterPrioritySet = new WritableSet(db.getRealm(), Collections.EMPTY_SET, String.class);
        db.getContext().bindSet(ViewersObservables.observeCheckedElements(fFilterPriorityTable, String.class),
                fFilterPrioritySet);
        new AutoCheckController(fFilterPriorityTable, fFilterPrioritySet);

        fFilterRViewsSet = new WritableSet(db.getRealm(), Collections.EMPTY_SET, IRView.class);
        db.getContext().bindSet(ViewersObservables.observeCheckedElements(fFilterRViewsTable, IRView.class),
                fFilterRViewsSet);
        new AutoCheckController(fFilterRViewsTable, fFilterRViewsSet);

        fFilterController = new TableFilterController(fPkgTable.viewer);

        fFilterController.addFilter(new InstalledFilter());
        { // Priority
            final SetElementFilter filter = new SetElementFilter() {
                @Override
                protected boolean select(final Collection<?> set, final Object element) {
                    final String name = (String) element;
                    if (Util.hasPkgPriority(fPkgSet.getAvailable(), name, set)) {
                        return true;
                    }
                    if (Util.hasPkgPriority(fPkgSet.getInstalled(), name, set)) {
                        return true;
                    }
                    return false;
                }
            };
            fFilterController.addFilter(filter);
            new ObservableSetBinding(fFilterController, fFilterPrioritySet, filter) {
                @Override
                protected java.util.Collection<?> getAll() {
                    return fPkgSet.getPriorities();
                }
            };
        }
        { // Task Views
            final SetElementFilter filter = new SetElementFilter();
            fFilterController.addFilter(filter);
            new ObservableSetBinding(fFilterController, fFilterRViewsSet, filter) {
                @Override
                protected Collection<?> createFilterSet(final Collection<?> set) {
                    final Set<String> pkgNames = new HashSet<String>(set.size() * 50);
                    for (final IRView view : (Collection<? extends IRView>) set) {
                        pkgNames.addAll(view.getPkgList());
                    }
                    return pkgNames;
                }
            };
        }
        {
            final TextElementFilter filter = new TextElementFilter();
            fFilterController.addFilter(filter);
            new SearchTextBinding(fFilterText, fFilterController, filter);
        }

        fFilterController.addListener(new TableFilterController.Listener() {
            @Override
            public void inputUpdated(final boolean newInput) {
                if (newInput) {
                    fSelectedPkgVersion = null;
                    updateDetail();
                }
            }
        });

        fPkgTable.viewer.addPostSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                final List<?> list = ((IStructuredSelection) event.getSelection()).toList();
                doUpdateDetail(list.toArray(new String[list.size()]));
            }
        });

        fDetailTable.viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                final ITreeSelection treeSelection = (ITreeSelection) event.getSelection();
                final Object element = treeSelection.getFirstElement();
                updateDetailDetail((element instanceof IRPkgData) ? treeSelection.getPaths()[0] : null);
            }
        });
    }

    private void clearFilter() {
        fFilterController.startUpdate();
        try {
            fFilterText.clearText();
            fFilterInstButton.setSelection(false);
            fFilterNotInstButton.setSelection(false);
            fFilterPrioritySet.clear();
            fFilterRViewsSet.clear();
        } finally {
            fFilterController.endUpdate();
        }
    }

    public void updateSettings(final IRPkgManager.Ext pkgManager) {
        fFilterController.startUpdate();
        try {
            fPkgSet = pkgManager.getExtRPkgSet();
            if (fPkgSet == null) {
                fPkgSet = IRPkgSet.DUMMY;
            }
            fSelectedPkgVersion = null;

            final List<String> priorities = fPkgSet.getPriorities();
            {
                fFilterPriorityTable.setInput(priorities);
                fFilterPrioritySet.retainAll(priorities);
            }
            {
                final List<? extends IRView> views = pkgManager.getRViews();
                final Control show;
                if (views != null) {
                    fFilterRViewsTable.setInput(views);
                    fFilterRViewsSet.retainAll(views);
                    show = fFilterRViewsTable.getControl().getParent();
                } else {
                    fFilterRViewsTable.setInput(Collections.EMPTY_LIST);
                    fFilterRViewsSet.clear();
                    if (fPkgSet.getAvailable().containsByName("ctv")
                            && !fPkgSet.getInstalled().containsByName("ctv")) {
                        fFilterViewsMessage.setText(
                                "Install CRAN Task Views (<a href=\"ctv\">ctv</a>) package to filter the packages by tasks.");
                        show = fFilterViewsMessage;
                    } else {
                        show = fFilterRViewsTable.getControl().getParent();
                    }
                }
                if (fFilterViewsStack.topControl != show) {
                    fFilterViewsStack.topControl = show;
                    show.getParent().layout(true);
                }
            }
            fFilterController.setInput(fPkgSet.getNames());
        } finally {
            fFilterController.endUpdate();
        }
    }

    private void updateDetail() {
        final List<?> list = ((IStructuredSelection) fPkgTable.viewer.getSelection()).toList();
        doUpdateDetail(list.toArray(new String[list.size()]));
    }

    private void doUpdateDetail(final String[] selection) {
        if (selection.length == 1) {
            final String name = selection[0];
            fSelectedPkgName = name;
            fDetailTable.viewer.setInput(name);
            ITreeSelection treeSelection = ((ITreeSelection) fDetailTable.viewer.getSelection());
            Object element = treeSelection.getFirstElement();
            if (!(element instanceof IRPkgData)) {
                IRPkgData pkg = null;
                if (pkg == null) {
                    final String repoId = fSelectedPkgVersions.get(name);
                    if (repoId != null) {
                        pkg = Util.getPkgByRepo(fPkgSet.getAvailable(), name, repoId);
                    }
                }
                if (pkg == null) {
                    pkg = fPkgSet.getAvailable().getFirstByName(name);
                }
                if (pkg == null) {
                    pkg = fPkgSet.getInstalled().getFirstByName(name);
                }
                if (pkg != null) {
                    fDetailTable.viewer.setSelection(new StructuredSelection(pkg));
                }
            }
            if (fSelectedPkgVersion == null) {
                treeSelection = ((ITreeSelection) fDetailTable.viewer.getSelection());
                element = treeSelection.getFirstElement();
                if (element instanceof IRPkgData) {
                    updateDetailDetail(treeSelection.getPaths()[0]);
                }
            }
            fPkgHistory.selected(name);
            fToolBar.update(true);
        } else {
            fSelectedPkgName = null;
            fDetailTable.viewer.setInput(NO_INPUT);
            fPkgHistory.selected(null);
        }
    }

    private void updateDetailDetail(final TreePath path) {
        if (fSelectedPkgName != null && path != null) {
            final String name = fSelectedPkgName;
            final int id = ((DetailGroup) path.getFirstSegment()).getId();
            final IRPkgData pkg = (IRPkgData) path.getLastSegment();
            fSelectedPkgVersionGroup = id;
            fSelectedPkgVersion = pkg;
            IRPkgData first;
            if (!pkg.getRepoId().isEmpty() && ((first = fPkgSet.getAvailable().getFirstByName(name)) != null)
                    && !pkg.getRepoId().equals(first.getRepoId())) {
                fSelectedPkgVersions.put(name, pkg.getRepoId());
            } else {
                fSelectedPkgVersions.remove(name);
            }

            fDetailLicense.setText(pkg.getLicense());
            fDetailDepTable.setInput(pkg);
            fDetailRevTable.setInput(fPkgSet.getReverse(name));
        } else {
            fSelectedPkgVersionGroup = -1;
            fSelectedPkgVersion = null;

            fDetailLicense.setText(""); //$NON-NLS-1$
            fDetailDepTable.setInput(NO_INPUT);
            fDetailRevTable.setInput(NO_INPUT);
        }

        updateButtons();
    }

    private boolean isModifiable(final IRLibraryLocation libLoc) {
        final Entry entry = fRPkgManager.getRLibPaths().getEntryByLocation(libLoc);
        return (entry != null && (entry.getAccess() & IRLibPaths.WRITABLE) != 0);
    }

    private void updateButtons() {
        boolean available;
        boolean allInstalled;
        boolean allRemovable;
        if (fSelectedPkgName != null) {
            final String name = fSelectedPkgName;
            available = fPkgSet.getAvailable().containsByName(name);
            final IRPkgInfoAndData pkg = fPkgSet.getInstalled().getFirstByName(name);
            if (pkg != null) {
                allInstalled = true;
                allRemovable = isModifiable(pkg.getLibraryLocation());
            } else {
                allInstalled = false;
                allRemovable = false;
            }
        } else {
            final IStructuredSelection selection = (IStructuredSelection) fPkgTable.viewer.getSelection();
            if (selection.isEmpty()) {
                available = false;
                allInstalled = false;
                allRemovable = false;
            } else {
                available = false;
                allInstalled = true;
                allRemovable = true;
                final List<String> checkedLocations = new ArrayList<String>(8);
                for (final Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {
                    final String name = (String) iterator.next();
                    if (!available && fPkgSet.getAvailable().containsByName(name)) {
                        available = true;
                    }
                    if (allInstalled) {
                        final IRPkgInfoAndData pkg = fPkgSet.getInstalled().getFirstByName(name);
                        if (pkg != null) {
                            if (allRemovable) {
                                final IRLibraryLocation libLoc = pkg.getLibraryLocation();
                                if (!checkedLocations.contains(libLoc.getDirectoryPath())) {
                                    allRemovable = isModifiable(libLoc);
                                    checkedLocations.add(libLoc.getDirectoryPath());
                                }
                            }
                        } else {
                            allInstalled = false;
                            allRemovable = false;
                        }
                    }
                }
            }
        }
        fInstallButton.setEnabled(available);
        fUpdateButton.setEnabled(available);
        fUninstallButton.setEnabled(allRemovable);
        fLoadButton.setEnabled(allInstalled);
    }

    void updateStatus(final IStatus status) {
        fRefreshHandler.setEnabled(null);
    }

    private void doInstall() {
        final Map<String, List<RPkgAction.Install>> pkgs = getFirstSelectedAsActions(false, new IGetPkgFilter[0]);
        if (pkgs == null) {
            return;
        }

        final RPkgResolver resolver = new RPkgResolver(fPkgSet, pkgs);
        resolver.setAddSuggested(fDialog.fOptionsTab.installSuggested());
        resolver.run();

        final InstallPkgsWizard wizard = new InstallPkgsWizard(fDialog.getTool(), fRPkgManager,
                InstallPkgsWizard.MODE_INSTALL, resolver);
        final WizardDialog dialog = new WizardDialog(getShell(), wizard);
        dialog.setBlockOnOpen(true);
        dialog.open();

        fPkgTable.viewer.refresh(true);
        updateButtons();
    }

    private void doUpdateLatest() {
        final Map<String, List<RPkgAction.Install>> pkgs = getFirstSelectedAsActions(false,
                new IGetPkgFilter[] { new RequireInstFilter(), new LibSourceFilter(), new LaterVersionFilter(), });
        if (pkgs == null) {
            return;
        }

        final RPkgResolver resolver = new RPkgResolver(fPkgSet, pkgs);
        resolver.setAddSuggested(false);
        resolver.run();

        final InstallPkgsWizard wizard = new InstallPkgsWizard(fDialog.getTool(), fRPkgManager,
                InstallPkgsWizard.MODE_UPDATE, resolver);
        final WizardDialog dialog = new WizardDialog(getShell(), wizard);
        dialog.setBlockOnOpen(true);
        dialog.open();

        fPkgTable.viewer.refresh(true);
        updateButtons();
    }

    private void doReinstall(final List<String> names) {
        final Map<String, List<RPkgAction.Install>> pkgs = getAllSelectedAsActions(names, true,
                new IGetPkgFilter[] { new RequireInstFilter(), new LibSourceFilter(), new NotOlderVersionFilter(),
                        new ReadOnlyFilter(fRPkgManager.getRLibPaths()), });
        if (pkgs == null) {
            return;
        }

        final RPkgResolver resolver = new RPkgResolver(fPkgSet, pkgs);
        resolver.setAddRequired(false);
        resolver.run();

        final InstallPkgsWizard wizard = new InstallPkgsWizard(fDialog.getTool(), fRPkgManager,
                InstallPkgsWizard.MODE_REINSTALL, resolver);
        final WizardDialog dialog = new WizardDialog(getShell(), wizard);
        dialog.setBlockOnOpen(true);
        dialog.open();

        fPkgTable.viewer.refresh(true);
        updateButtons();
    }

    private void doUninstall() {
        final List<? extends IRPkgInfoAndData> pkgs = getSelectedInstalled(new IGetPkgFilter[] {
                new RequireInstFilter(), new LibSourceFilter(), new ReadOnlyFilter(fRPkgManager.getRLibPaths()), });
        if (pkgs == null) {
            return;
        }
        final List<RPkgAction> actions = new ArrayList<RPkgAction>(pkgs.size());
        for (final IRPkgInfoAndData pkg : pkgs) {
            actions.add(new RPkgAction.Uninstall(pkg));
        }
        fRPkgManager.perform(fDialog.getTool(), actions);

        fPkgTable.viewer.refresh(true);
        updateButtons();
    }

    private void doLoad() {
        final List<? extends IRPkgInfoAndData> pkgs = getSelectedInstalled(
                new IGetPkgFilter[] { new RequireInstFilter(), });
        if (pkgs == null) {
            return;
        }

        if (pkgs.size() == 1 && fSelectedPkgVersion != null) {
            fRPkgManager.loadPkgs(fDialog.getTool(), pkgs, true);
        } else {
            fRPkgManager.loadPkgs(fDialog.getTool(), pkgs, false);
        }
    }

    //   private void revert() {
    //      List<String> list;
    //      if (fSelectedPkgName != null) {
    //         list = Collections.singletonList(fSelectedPkgName);
    //      }
    //      else {
    //         final IStructuredSelection selection = (IStructuredSelection) fPkgTable.viewer.getSelection();
    //         list = selection.toList();
    //      }
    //      fPkgTable.viewer.refresh(true);
    //      updateButtons();
    //   }

    private List<IRPkgInfoAndData> getSelectedInstalled(final IGetPkgFilter[] filters) {
        if (fSelectedPkgName != null) {
            final String name = fSelectedPkgName;
            IRPkgInfoAndData inst;
            if (fSelectedPkgVersionGroup == INST) {
                inst = (IRPkgInfoAndData) fSelectedPkgVersion;
            } else {
                inst = fPkgSet.getInstalled().getFirstByName(name);
            }
            for (int j = 0; j < filters.length; j++) {
                if (filters[j].exclude(inst, null)) {
                    return null;
                }
            }
            final List<IRPkgInfoAndData> list = new ArrayList<IRPkgInfoAndData>(1);
            list.add(inst);
            return list;
        } else {
            final IStructuredSelection selection = (IStructuredSelection) fPkgTable.viewer.getSelection();
            final List<IRPkgInfoAndData> list = new ArrayList<IRPkgInfoAndData>(selection.size());
            ITER_SELECTED: for (final Object element : selection.toList()) {
                final String name = (String) element;
                final IRPkgInfoAndData inst = fPkgSet.getInstalled().getFirstByName(name);
                for (int j = 0; j < filters.length; j++) {
                    if (filters[j].exclude(inst, null)) {
                        continue ITER_SELECTED;
                    }
                }
                list.add(inst);
            }
            if (list.isEmpty()) {
                return null;
            }
            return list;
        }
    }

    private final Map<String, List<RPkgAction.Install>> getFirstSelectedAsActions(final boolean sameRepo,
            final IGetPkgFilter[] filters) {
        if (fSelectedPkgName != null) {
            final String name = fSelectedPkgName;
            IRPkgData avail;
            if (fSelectedPkgVersionGroup == AVAIL) {
                avail = fSelectedPkgVersion;
            } else {
                avail = fPkgSet.getAvailable().getFirstByName(name);
            }
            if (avail == null) {
                return null;
            }
            IRPkgInfoAndData inst;
            if (fSelectedPkgVersionGroup == INST) {
                inst = (IRPkgInfoAndData) fSelectedPkgVersion;
            } else {
                inst = fPkgSet.getInstalled().getFirstByName(name);
            }
            if (inst != null && sameRepo) {
                avail = getAvailSameRepo(inst, avail);
            }
            for (int j = 0; j < filters.length; j++) {
                if (filters[j].exclude(inst, avail)) {
                    return null;
                }
            }
            final List<RPkgAction.Install> list = new ArrayList<RPkgAction.Install>(1);
            list.add(new RPkgAction.Install(avail, null, inst));
            return Collections.singletonMap(name, list);
        } else {
            final IStructuredSelection selection = (IStructuredSelection) fPkgTable.viewer.getSelection();
            final Map<String, List<RPkgAction.Install>> map = new HashMap<String, List<RPkgAction.Install>>(
                    selection.size());
            ITER_SELECTED: for (final Object element : selection.toList()) {
                final String name = (String) element;
                IRPkgData avail = fPkgSet.getAvailable().getFirstByName(name);
                if (avail == null) {
                    continue;
                }
                final IRPkgInfoAndData inst = fPkgSet.getInstalled().getFirstByName(name);
                if (inst != null && sameRepo) {
                    avail = getAvailSameRepo(inst, avail);
                }
                for (int j = 0; j < filters.length; j++) {
                    if (filters[j].exclude(inst, avail)) {
                        continue ITER_SELECTED;
                    }
                }
                final List<RPkgAction.Install> list = new ArrayList<RPkgAction.Install>(1);
                list.add(new RPkgAction.Install(avail, null, inst));
                map.put(name, list);
            }
            if (map.isEmpty()) {
                return null;
            }
            return map;
        }
    }

    private final Map<String, List<RPkgAction.Install>> getAllSelectedAsActions(List<String> names,
            final boolean sameRepo, final IGetPkgFilter[] filters) {
        if (names == null && fSelectedPkgName != null) {
            final String name = fSelectedPkgName;
            final IRPkgData avail;
            if (fSelectedPkgVersionGroup == AVAIL) {
                avail = fSelectedPkgVersion;
            } else {
                avail = fPkgSet.getAvailable().getFirstByName(name);
            }
            if (avail == null) {
                return null;
            }
            final List<? extends IRPkgInfoAndData> instList = fPkgSet.getInstalled().getByName(name);
            if (instList.isEmpty()) {
                return null;
            }
            final List<RPkgAction.Install> list = new ArrayList<RPkgAction.Install>(instList.size());
            ITER_INST: for (final IRPkgInfoAndData inst : instList) {
                final IRPkgData instAvail = (sameRepo) ? getAvailSameRepo(inst, avail) : avail;
                for (int j = 0; j < filters.length; j++) {
                    if (filters[j].exclude(inst, instAvail)) {
                        continue ITER_INST;
                    }
                }
                list.add(new RPkgAction.Install(instAvail, null, inst));
            }

            if (list.isEmpty()) {
                return null;
            }
            return Collections.singletonMap(name, list);
        } else {
            if (names == null) {
                names = ((IStructuredSelection) fPkgTable.viewer.getSelection()).toList();
            }
            final Map<String, List<RPkgAction.Install>> map = new HashMap<String, List<RPkgAction.Install>>(
                    names.size());
            for (final String name : names) {
                final IRPkgData avail = fPkgSet.getAvailable().getFirstByName(name);
                if (avail == null) {
                    continue;
                }
                final List<? extends IRPkgInfoAndData> instList = fPkgSet.getInstalled().getByName(name);
                if (instList.isEmpty()) {
                    continue;
                }
                final List<RPkgAction.Install> list = new ArrayList<RPkgAction.Install>(instList.size());
                ITER_INST: for (final IRPkgInfoAndData inst : instList) {
                    final IRPkgData instAvail = (sameRepo) ? getAvailSameRepo(inst, avail) : avail;
                    for (int j = 0; j < filters.length; j++) {
                        if (filters[j].exclude(inst, instAvail)) {
                            continue ITER_INST;
                        }
                    }
                    list.add(new RPkgAction.Install(instAvail, null, inst));
                }

                if (list.isEmpty()) {
                    continue;
                }
                map.put(name, list);
            }
            if (map.isEmpty()) {
                return null;
            }
            return map;
        }
    }

    private IRPkgData getAvailSameRepo(final IRPkgInfoAndData inst, final IRPkgData fallback) {
        IRPkgData pkg = null;
        if (!inst.getRepoId().isEmpty()) {
            final IRPkgList<? extends IRPkgData> repoList = fPkgSet.getAvailable().getBySource(inst.getRepoId());
            if (repoList != null) {
                pkg = repoList.get(inst.getName());
                if (pkg != null) {
                    return pkg;
                }
            }
        }
        return fallback;
    }

    void showPkg(final String name) {
        if (name.equals("R")) {
            return;
        }
        fFilterController.setSelection(name);
    }

    IRPkgSet.Ext getPkgSet() {
        return fPkgSet;
    }

    void install(final List<String> pkgNames) {
        fPkgTable.viewer.setSelection(new StructuredSelection());
        clearFilter();
        fFilterController.schedule(new Runnable() {
            @Override
            public void run() {
                fFilterController.setSelection(pkgNames);
                //            updateDetail();
                doInstall();
            }
        });
    }

    void reinstallAll() {
        doReinstall(fPkgSet.getNames());
    }

}