org.eclipse.wst.sse.ui.preferences.CodeAssistCyclingConfigurationBlock.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.sse.ui.preferences.CodeAssistCyclingConfigurationBlock.java

Source

/*******************************************************************************
 * Copyright (c) 2010 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.wst.sse.ui.preferences;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.Scheme;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
import org.eclipse.wst.sse.ui.internal.contentassist.CompletionProposalCategory;
import org.eclipse.wst.sse.ui.internal.contentassist.CompletionProposalComputerRegistry;
import org.eclipse.wst.sse.ui.internal.util.SWTUtil;

/**
 * <p>A helpful preference configuration block implementation for allowing a user to
 * set their preferences for which content assist categories to show on the default
 * content assist page as well as on their own separate page and page ordering for
 * a specific content type</p>
 * 
 * @base org.eclipse.jdt.internal.ui.preferences.CodeAssistAdvancedConfigurationBlock
 */
public final class CodeAssistCyclingConfigurationBlock {

    /**
     * <p>Used to compare categories based on their assigned page rank</p>
     */
    private final Comparator fCategoryPageComparator = new Comparator() {
        private int getRank(Object o) {
            return ((ModelElement) o).getOwnPageRank();
        }

        public int compare(Object o1, Object o2) {
            int result = getRank(o1) - getRank(o2);
            if (result == 0) {
                result = ((ModelElement) o1).getId().compareTo(((ModelElement) o2).getId());
            }
            return result;
        }
    };

    /**
     * <p>Used to compare categories based on their assigned default page rank</p>
     */
    private final Comparator fCategoryDefaultPageComparator = new Comparator() {
        private int getRank(Object o) {
            return ((ModelElement) o).getDefaultPageRank();
        }

        public int compare(Object o1, Object o2) {
            int result = getRank(o1) - getRank(o2);
            if (result == 0) {
                result = ((ModelElement) o1).getId().compareTo(((ModelElement) o2).getId());
            }
            return result;
        }
    };

    /** the preference model for this block */
    private final PreferenceModel fModel;

    /**
     * <code>{@link Map}<{@link ImageDescriptor}, {@link Image}></code>
     */
    private final Map fImages = new HashMap();

    /** table viewer to configure which categories are displayed on the default page */
    private CheckboxTableViewer fDefaultPageViewer;

    /**
     * table viewer to configure which categories are displayed
     * on their own page, as well as their ordering
     */
    private CheckboxTableViewer fOwnPageViewer;

    /** categories pages sort order up button */
    private Button fPageOrderUpButton;

    /** categories pages sort order down button */
    private Button fPageOrderDownButton;

    /** categories default page sort order up button */
    private Button fDefaultPageOrderUpButton;

    /** categories default page  sort order down button */
    private Button fDefaultPageOrderDownButton;

    /** The content type ID this configuration block is for */
    private String fContentTypeID;

    /** The writable categories configuration */
    private ICompletionProposalCategoriesConfigurationWriter fConfigurationWriter;

    /**
     * <p>Creates a new content assist preference block for the given content type
     * using the given configuration writer</p>
     * 
     * @param contentTypeID content type this content assist preference block is for
     * @param configurationWriter {@link ICompletionProposalCategoriesConfigurationWriter} used
     * to read and write the user preferences 
     */
    public CodeAssistCyclingConfigurationBlock(String contentTypeID,
            ICompletionProposalCategoriesConfigurationWriter configurationWriter) {
        this.fContentTypeID = contentTypeID;
        this.fConfigurationWriter = configurationWriter;

        List categories = CompletionProposalComputerRegistry.getDefault()
                .getProposalCategories(this.fContentTypeID);
        this.fModel = new PreferenceModel(categories);
    }

    /**
     * <p>Saves the user configuration</p>
     * @return <code>true</code> if store was successful, <code>false</code> otherwise
     */
    public boolean storeValues() {
        return this.fConfigurationWriter.saveConfiguration();
    }

    /**
     * <p>Loads the preference defaults</p>
     */
    public void performDefaults() {
        this.fConfigurationWriter.loadDefaults();
        this.initializeValues();
        this.fModel.performDefaults();
    }

    /**
     * <p>Disposes of this preference block</p>
     */
    public void dispose() {
        for (Iterator it = fImages.values().iterator(); it.hasNext();) {
            Image image = (Image) it.next();
            image.dispose();
        }
    }

    /**
     * <p>Creates the contents of this configuration block using a {@link Group} if
     * the given <code>groupTitle</code> is not <code>null</code> else creates it
     * as a composite and in either case adds itself to the given parent</p>
     * 
     * @param parent {@link Composite} parent to add this configuration block to
     * @param groupTitle Title to use for the configuration block group, if
     * <code>null</code> then a {@link Composite} will be used instead of a
     * {@link Group}
     * 
     * @return the created configuration block that has already been added to the parent
     */
    public Control createContents(Composite parent, String groupTitle) {
        Composite container;
        if (groupTitle != null) {
            container = new Group(parent, SWT.NULL);
            ((Group) container).setText(groupTitle);
        } else {
            container = new Composite(parent, SWT.NULL);
        }
        int columns = 2;
        GridLayout layout = new GridLayout(columns, false);
        container.setLayout(layout);

        GridData data = new GridData(GridData.FILL);
        data.horizontalIndent = 0;
        data.verticalAlignment = GridData.FILL;
        data.horizontalAlignment = GridData.FILL;
        data.grabExcessHorizontalSpace = true;
        container.setLayoutData(data);

        createDefaultPageLabel(container, columns);
        createDefaultPageSection(container);

        createFiller(container, columns);

        createOwnPageLabel(container, columns);
        createOwnPageSection(container);

        createFiller(container, columns);

        if (fModel.pageElements.size() > 0) {
            fDefaultPageViewer.getTable().select(0);
            fOwnPageViewer.getTable().select(0);
            handleOwnPageTableSelection();
            handleDefaultPageTableSelection();
        }

        return container;
    }

    /**
     * <p>Initialize the values of the configuration block</p>
     */
    public void initializeValues() {
        updateCheckedState();
        fDefaultPageViewer.refresh();
        fOwnPageViewer.refresh();
        handleOwnPageTableSelection();
        handleDefaultPageTableSelection();
    }

    private void createDefaultPageSection(Composite composite) {
        createDefaultPageViewer(composite);
        createDefaultPageButtonList(composite);
    }

    private void createDefaultPageLabel(Composite composite, int h_span) {
        final ICommandService commandSvc = (ICommandService) PlatformUI.getWorkbench()
                .getAdapter(ICommandService.class);
        final Command command = commandSvc.getCommand(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
        ParameterizedCommand pCmd = new ParameterizedCommand(command, null);
        String key = getKeyboardShortcut(pCmd);
        if (key == null) {
            key = SSEUIMessages.CodeAssistAdvancedConfigurationBlock_no_shortcut;
        }

        PixelConverter pixelConverter = new PixelConverter(composite);
        int width = pixelConverter.convertWidthInCharsToPixels(40);

        Label label = new Label(composite, SWT.NONE | SWT.WRAP);
        label.setText(NLS.bind(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_page_description,
                new Object[] { key }));
        GridData gd = new GridData(GridData.FILL, GridData.FILL, true, false, h_span, 1);
        gd.widthHint = width;
        label.setLayoutData(gd);

        createFiller(composite, h_span);

        label = new Label(composite, SWT.NONE | SWT.WRAP);
        label.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_default_table_description);
        gd = new GridData(GridData.FILL, GridData.FILL, true, false, h_span, 1);
        gd.widthHint = width;
        label.setLayoutData(gd);
    }

    private void createDefaultPageViewer(Composite composite) {
        fDefaultPageViewer = CheckboxTableViewer.newCheckList(composite, SWT.SINGLE | SWT.BORDER);
        Table table = fDefaultPageViewer.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(false);
        table.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false, 1, 1));

        TableColumn nameColumn = new TableColumn(table, SWT.NONE);
        nameColumn.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_default_table_category_column_title);
        nameColumn.setResizable(false);

        fDefaultPageViewer.addCheckStateListener(new ICheckStateListener() {
            public void checkStateChanged(CheckStateChangedEvent event) {
                boolean checked = event.getChecked();
                ModelElement element = (ModelElement) event.getElement();
                element.setShouldDisplayOnDefaultPage(checked);
            }
        });

        fDefaultPageViewer.setContentProvider(new ArrayContentProvider());

        DefaultPageTableLabelProvider labelProvider = new DefaultPageTableLabelProvider();
        fDefaultPageViewer.setLabelProvider(labelProvider);
        fDefaultPageViewer.setInput(fModel.defaultPageElements);
        fDefaultPageViewer.setComparator(new ModelViewerComparator(fCategoryDefaultPageComparator));

        final int ICON_AND_CHECKBOX_WITH = 50;
        final int HEADER_MARGIN = 20;
        int minNameWidth = computeWidth(table, nameColumn.getText()) + HEADER_MARGIN;
        for (int i = 0; i < fModel.defaultPageElements.size(); i++) {
            minNameWidth = Math.max(minNameWidth,
                    computeWidth(table, labelProvider.getColumnText(fModel.defaultPageElements.get(i), 0))
                            + ICON_AND_CHECKBOX_WITH);
        }

        nameColumn.setWidth(minNameWidth);

        table.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                handleDefaultPageTableSelection();
            }
        });
    }

    /**
     * <p>Create the Up and Down buttons for the default page viewer</p>
     * @param parent {@link Composite} to add the button list to 
     */
    private void createDefaultPageButtonList(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));

        GridLayout layout = new GridLayout();
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        composite.setLayout(layout);

        fDefaultPageOrderUpButton = new Button(composite, SWT.PUSH | SWT.CENTER);
        fDefaultPageOrderUpButton.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_Up);
        fDefaultPageOrderUpButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                int index = fDefaultPageViewer.getTable().getSelectionIndex();
                if (index != -1) {
                    fModel.moveDefaultPageCategoryUp(index);
                    fDefaultPageViewer.refresh();
                    handleDefaultPageTableSelection();
                }
            }
        });
        fDefaultPageOrderUpButton.setLayoutData(new GridData());

        SWTUtil.setButtonDimensionHint(fDefaultPageOrderUpButton);

        fDefaultPageOrderDownButton = new Button(composite, SWT.PUSH | SWT.CENTER);
        fDefaultPageOrderDownButton.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_Down);
        fDefaultPageOrderDownButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                int index = fDefaultPageViewer.getTable().getSelectionIndex();
                if (index != -1) {
                    fModel.moveDefaultPageCategoryDown(index);
                    fDefaultPageViewer.refresh();
                    handleDefaultPageTableSelection();
                }
            }
        });
        fDefaultPageOrderDownButton.setLayoutData(new GridData());
        SWTUtil.setButtonDimensionHint(fDefaultPageOrderDownButton);
    }

    private void createFiller(Composite composite, int h_span) {
        Label filler = new Label(composite, SWT.NONE);
        filler.setVisible(false);
        filler.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, h_span, 1));
    }

    private void createOwnPageLabel(Composite composite, int h_span) {
        PixelConverter pixelConverter = new PixelConverter(composite);
        int width = pixelConverter.convertWidthInCharsToPixels(40);

        Label label = new Label(composite, SWT.NONE | SWT.WRAP);
        label.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_separate_table_description);
        GridData gd = new GridData(GridData.FILL, GridData.FILL, false, false, h_span, 1);
        gd.widthHint = width;
        label.setLayoutData(gd);
    }

    private void createOwnPageSection(Composite composite) {
        createOwnPageViewer(composite);
        createOwnPageButtonList(composite);
    }

    private void createOwnPageViewer(Composite composite) {
        fOwnPageViewer = CheckboxTableViewer.newCheckList(composite, SWT.SINGLE | SWT.BORDER);
        Table table = fOwnPageViewer.getTable();
        table.setHeaderVisible(true);
        table.setLinesVisible(false);
        table.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1));

        TableColumn nameColumn = new TableColumn(table, SWT.NONE);
        nameColumn.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_separate_table_category_column_title);
        nameColumn.setResizable(false);

        fOwnPageViewer.setContentProvider(new ArrayContentProvider());

        ITableLabelProvider labelProvider = new OwnPageTableLabelProvider();
        fOwnPageViewer.setLabelProvider(labelProvider);
        fOwnPageViewer.setInput(fModel.pageElements);
        fOwnPageViewer.setComparator(new ModelViewerComparator(fCategoryPageComparator));

        final int ICON_AND_CHECKBOX_WITH = 50;
        final int HEADER_MARGIN = 20;
        int minNameWidth = computeWidth(table, nameColumn.getText()) + HEADER_MARGIN;
        for (int i = 0; i < fModel.pageElements.size(); i++) {
            minNameWidth = Math.max(minNameWidth,
                    computeWidth(table, labelProvider.getColumnText(fModel.pageElements.get(i), 0))
                            + ICON_AND_CHECKBOX_WITH);
        }

        nameColumn.setWidth(minNameWidth);

        fOwnPageViewer.addCheckStateListener(new ICheckStateListener() {
            public void checkStateChanged(CheckStateChangedEvent event) {
                boolean checked = event.getChecked();
                ModelElement element = (ModelElement) event.getElement();
                element.setShouldDisplayOnOwnPage(checked);
            }
        });

        table.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                handleOwnPageTableSelection();
            }
        });

    }

    /**
     * <p>Create the Up and Down buttons for the own page viewer</p>
     * @param parent {@link Composite} to add the button list to 
     */
    private void createOwnPageButtonList(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));

        GridLayout layout = new GridLayout();
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        composite.setLayout(layout);

        fPageOrderUpButton = new Button(composite, SWT.PUSH | SWT.CENTER);
        fPageOrderUpButton.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_PagesUp);
        fPageOrderUpButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                int index = fOwnPageViewer.getTable().getSelectionIndex();
                if (index != -1) {
                    fModel.movePageUp(index);
                    fOwnPageViewer.refresh();
                    handleOwnPageTableSelection();
                }
            }
        });
        fPageOrderUpButton.setLayoutData(new GridData());

        SWTUtil.setButtonDimensionHint(fPageOrderUpButton);

        fPageOrderDownButton = new Button(composite, SWT.PUSH | SWT.CENTER);
        fPageOrderDownButton.setText(SSEUIMessages.CodeAssistAdvancedConfigurationBlock_PagesDown);
        fPageOrderDownButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                int index = fOwnPageViewer.getTable().getSelectionIndex();
                if (index != -1) {
                    fModel.movePageDown(index);
                    fOwnPageViewer.refresh();
                    handleOwnPageTableSelection();
                }
            }
        });
        fPageOrderDownButton.setLayoutData(new GridData());
        SWTUtil.setButtonDimensionHint(fPageOrderDownButton);
    }

    /**
     * <p>Update the enablement of the Up and Down buttons for the own page table viewer</p>
     */
    private void handleOwnPageTableSelection() {
        ModelElement item = (ModelElement) ((IStructuredSelection) fOwnPageViewer.getSelection()).getFirstElement();
        if (item != null) {
            int index = fOwnPageViewer.getTable().getSelectionIndex();
            fPageOrderUpButton.setEnabled(index > 0);
            fPageOrderDownButton.setEnabled(index < fModel.pageElements.size() - 1);
        } else {
            fPageOrderUpButton.setEnabled(false);
            fPageOrderDownButton.setEnabled(false);
        }
    }

    /**
     * <p>Update the enablement of the Up and Down buttons for the default page table viewer</p>
     */
    private void handleDefaultPageTableSelection() {
        ModelElement item = (ModelElement) ((IStructuredSelection) fDefaultPageViewer.getSelection())
                .getFirstElement();
        if (item != null) {
            int index = fDefaultPageViewer.getTable().getSelectionIndex();
            fDefaultPageOrderUpButton.setEnabled(index > 0);
            fDefaultPageOrderDownButton.setEnabled(index < fModel.defaultPageElements.size() - 1);
        } else {
            fDefaultPageOrderUpButton.setEnabled(false);
            fDefaultPageOrderDownButton.setEnabled(false);
        }
    }

    private void updateCheckedState() {
        /* does not matter which set of elements we use here
         * because order does not matter in this case
         */
        final int size = fModel.pageElements.size();
        List defaultChecked = new ArrayList(size);
        List separateChecked = new ArrayList(size);

        for (Iterator it = fModel.pageElements.iterator(); it.hasNext();) {
            ModelElement element = (ModelElement) it.next();
            if (element.shouldDisplayOnDefaultPage())
                defaultChecked.add(element);
            if (element.shouldDisplayOnOwnPage())
                separateChecked.add(element);
        }

        fDefaultPageViewer.setCheckedElements(defaultChecked.toArray(new Object[defaultChecked.size()]));
        fOwnPageViewer.setCheckedElements(separateChecked.toArray(new Object[separateChecked.size()]));
    }

    private int computeWidth(Control control, String name) {
        if (name == null)
            return 0;
        GC gc = new GC(control);
        try {
            gc.setFont(JFaceResources.getDialogFont());
            return gc.stringExtent(name).x + 10;
        } finally {
            gc.dispose();
        }
    }

    private static BindingManager fgLocalBindingManager;
    static {
        fgLocalBindingManager = new BindingManager(new ContextManager(), new CommandManager());
        final IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench()
                .getService(IBindingService.class);
        final Scheme[] definedSchemes = bindingService.getDefinedSchemes();
        if (definedSchemes != null) {
            try {
                for (int i = 0; i < definedSchemes.length; i++) {
                    final Scheme scheme = definedSchemes[i];
                    final Scheme copy = fgLocalBindingManager.getScheme(scheme.getId());
                    copy.define(scheme.getName(), scheme.getDescription(), scheme.getParentId());
                }
            } catch (final NotDefinedException e) {
                Logger.logException(e);
            }
        }
        fgLocalBindingManager.setLocale(bindingService.getLocale());
        fgLocalBindingManager.setPlatform(bindingService.getPlatform());
    }

    private static String getKeyboardShortcut(ParameterizedCommand command) {
        IBindingService bindingService = (IBindingService) PlatformUI.getWorkbench()
                .getAdapter(IBindingService.class);
        fgLocalBindingManager.setBindings(bindingService.getBindings());
        try {
            Scheme activeScheme = bindingService.getActiveScheme();
            if (activeScheme != null)
                fgLocalBindingManager.setActiveScheme(activeScheme);
        } catch (NotDefinedException e) {
            Logger.logException(e);
        }

        TriggerSequence[] bindings = fgLocalBindingManager.getActiveBindingsDisregardingContextFor(command);
        if (bindings.length > 0)
            return bindings[0].format();
        return null;
    }

    /**
     * <p>Gets and image based on an image descriptor, and stores the image so it
     * does not have to be created more then once</p>
     * 
     * @param imgDesc {@link ImageDescriptor} to get the {@link Image} for
     * @return {@link Image} created from the {@link ImageDescriptor}, or stored
     * {@link Image} associated with the given {@link ImageDescriptor} if an
     * {@link Image} had already been created for the given {@link ImageDescriptor}
     */
    private Image getImage(ImageDescriptor imgDesc) {
        if (imgDesc == null)
            return null;

        Image img = (Image) fImages.get(imgDesc);
        if (img == null) {
            img = imgDesc.createImage(false);
            fImages.put(imgDesc, img);
        }
        return img;
    }

    /**
     * <p>Label provider for the table for configuring which categories should be displayed on the
     * default assist page</p>
     */
    private final class DefaultPageTableLabelProvider extends LabelProvider implements ITableLabelProvider {
        /**
         * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
         */
        public Image getColumnImage(Object element, int columnIndex) {
            if (columnIndex == 0)
                return ((ModelElement) element).getImage();
            return null;
        }

        /**
         * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
         */
        public String getColumnText(Object element, int columnIndex) {
            switch (columnIndex) {
            case 0:
                return ((ModelElement) element).getName();
            default:
                Assert.isTrue(false);
                return null;
            }
        }

        /**
         * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
         */
        public String getText(Object element) {
            return getColumnText(element, 0); // needed to make the sorter work
        }
    }

    /**
     * <p>Label provider for the table for configuring which categories should be displayed on their
     * own content assist page</p>
     */
    private final class OwnPageTableLabelProvider extends LabelProvider implements ITableLabelProvider {
        /**
         * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
         */
        public Image getColumnImage(Object element, int columnIndex) {
            if (columnIndex == 0)
                return ((ModelElement) element).getImage();
            return null;
        }

        /**
         * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
         */
        public String getColumnText(Object element, int columnIndex) {
            switch (columnIndex) {
            case 0:
                return ((ModelElement) element).getName();
            default:
                Assert.isTrue(false);
                return null;
            }
        }
    }

    /**
     * <p>PreferenceModel used to read and write the user preferences
     * using the {@link ICompletionProposalCategoriesConfigurationWriter}</p>
     */
    private final class PreferenceModel {
        /** private modifiable page element list */
        private final List fPageElements;

        /** public unmodifiable page element list */
        final List pageElements;

        /** private modifiable default page element list */
        private final List fDefaultPageElements;

        /** public unmodifiable default page element list */
        final List defaultPageElements;

        /**
         * <p>Create the preference model for the given categories</p>
         * 
         * @param categories <code>{@link List}<{@link CompletionProposalCategory}></code>
         */
        public PreferenceModel(List categories) {
            //need separate lists because they will be ordered differently
            this.fPageElements = new ArrayList();
            this.fDefaultPageElements = new ArrayList();
            for (Iterator it = categories.iterator(); it.hasNext();) {
                CompletionProposalCategory category = (CompletionProposalCategory) it.next();
                if (category.hasComputers()) {
                    ModelElement elem = new ModelElement(category);
                    fPageElements.add(elem);
                    fDefaultPageElements.add(elem);
                }
            }
            //sort the lists
            this.performDefaults();

            pageElements = Collections.unmodifiableList(fPageElements);
            defaultPageElements = Collections.unmodifiableList(fDefaultPageElements);
        }

        /**
         * <p>Move the model element specified by the given index
         * up in the content assist page order</p>
         * 
         * @param elementIndex the index of the model element to move up
         */
        public void movePageUp(int elementIndex) {
            Object removed = fPageElements.remove(elementIndex);
            fPageElements.add(elementIndex - 1, removed);

            fConfigurationWriter.setPageOrder(getPageOrderedCategoryIDs());
        }

        /**
        * <p>Move the model element specified by the given index
        * down in the content assist page order</p>
        * 
        * @param elementIndex the index of the model element to move up
        */
        public void movePageDown(int elementIndex) {
            Object removed = fPageElements.remove(elementIndex);
            fPageElements.add(elementIndex + 1, removed);

            fConfigurationWriter.setPageOrder(getPageOrderedCategoryIDs());
        }

        /**
        * <p>Move the model element specified by the given index
        * up in the content assist page order</p>
        * 
        * @param elementIndex the index of the model element to move up
        */
        public void moveDefaultPageCategoryUp(int elementIndex) {
            Object removed = fDefaultPageElements.remove(elementIndex);
            fDefaultPageElements.add(elementIndex - 1, removed);

            fConfigurationWriter.setDefaultPageOrder(getDefaultPageOrderedCategoryIDs());
        }

        /**
        * <p>Move the model element specified by the given index
        * down in the content assist page order</p>
        * 
        * @param elementIndex the index of the model element to move up
        */
        public void moveDefaultPageCategoryDown(int elementIndex) {
            Object removed = fDefaultPageElements.remove(elementIndex);
            fDefaultPageElements.add(elementIndex + 1, removed);

            fConfigurationWriter.setDefaultPageOrder(getDefaultPageOrderedCategoryIDs());
        }

        /**
         * @return <code>{@link List}<{@link String}></code> -
         * List of category IDs by page order
         */
        private List getPageOrderedCategoryIDs() {
            List ordered = new ArrayList(pageElements.size());
            for (int i = 0; i < pageElements.size(); ++i) {
                ordered.add(((ModelElement) pageElements.get(i)).getId());
            }

            return ordered;
        }

        /**
         * @return <code>{@link List}<{@link String}></code> -
         * List of category IDs by default page order
         */
        private List getDefaultPageOrderedCategoryIDs() {
            List ordered = new ArrayList(defaultPageElements.size());
            for (int i = 0; i < defaultPageElements.size(); ++i) {
                ordered.add(((ModelElement) defaultPageElements.get(i)).getId());
            }

            return ordered;
        }

        /**
         * <p>need to re-sort the lists after performing defaults</p>
         */
        public void performDefaults() {
            Collections.sort(fPageElements, fCategoryPageComparator);
            Collections.sort(fDefaultPageElements, fCategoryDefaultPageComparator);
        }
    }

    /**
     * <p>Wraps a {@link CompletionProposalCategory} for use in the {@link PreferenceModel}</p>
     */
    private final class ModelElement {
        /** The wrapped category */
        private final CompletionProposalCategory fCategory;

        /**
         * <p>Create a new model element wrapping the given category</p>
         * 
         * @param category {@link CompletionProposalCategory} to be wrapped by this model element
         * for use in the {@link PreferenceModel}
         */
        ModelElement(CompletionProposalCategory category) {
            fCategory = category;
        }

        /**
         * @return {@link Image} associated with the wrapped category
         */
        Image getImage() {
            return CodeAssistCyclingConfigurationBlock.this.getImage(fCategory.getImageDescriptor());
        }

        /**
         * @return name of the wrapped category
         */
        String getName() {
            return fCategory.getDisplayName();
        }

        String getId() {
            return fCategory.getId();
        }

        /**
         * @return <code>true</code> if the wrapped category should be displayed on the
         * default content assist page, <code>false</code> otherwise
         */
        boolean shouldDisplayOnDefaultPage() {
            return fConfigurationWriter.shouldDisplayOnDefaultPage(this.getId());
        }

        /**
         * @param included <code>true</code> if the wrapped category should be displayed on the
         * default content assist page, <code>false</code> otherwise
         */
        void setShouldDisplayOnDefaultPage(boolean included) {
            fConfigurationWriter.setShouldDisplayOnDefaultPage(this.getId(), included);
        }

        /**
         * @return <code>true</code> if the wrapped category should be displayed on the
         * its own content assist page, <code>false</code> otherwise
         */
        boolean shouldDisplayOnOwnPage() {
            return fConfigurationWriter.shouldDisplayOnOwnPage(this.getId());
        }

        /**
         * @param shouldDisplay <code>true</code> if the wrapped category should be displayed on the
         * its own content assist page, <code>false</code> otherwise
         */
        void setShouldDisplayOnOwnPage(boolean shouldDisplay) {
            fConfigurationWriter.setShouldDisplayOnOwnPage(this.getId(), shouldDisplay);
        }

        /**
         * @return the wrapped categories content assist page sort rank compared to the
         * other categories
         */
        int getOwnPageRank() {
            return fConfigurationWriter.getPageSortOrder(this.getId());
        }

        /**
         * @return the wrapped categories content assist page sort rank compared to the
         * other categories
         */
        int getDefaultPageRank() {
            return fConfigurationWriter.getDefaultPageSortOrder(this.getId());
        }
    }

    private class ModelViewerComparator extends ViewerComparator {
        /**
         * 
         */
        public ModelViewerComparator(Comparator comparator) {
            super(comparator);
        }

        /**
         * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
         */
        public int compare(Viewer viewer, Object e1, Object e2) {
            return this.getComparator().compare(e1, e2);
        }
    }
}