org.eclipse.team.internal.ccvs.ui.wizards.ModeWizardSelectionPage.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.team.internal.ccvs.ui.wizards.ModeWizardSelectionPage.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2008 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.team.internal.ccvs.ui.wizards;

import java.text.Collator; // don't use ICU, pending resolution of issue 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
import org.eclipse.team.internal.ccvs.core.util.StringMatcher;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.wizards.ModeWizard.ModeChange;
import org.eclipse.team.internal.ui.PixelConverter;
import org.eclipse.team.internal.ui.SWTUtils;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.WorkbenchLabelProvider;

//TODO: Filtering the TableViewer is currently slow for large amounts of files. 3.1M5 will feature a framework to help with this, so wait until it is there.
//TODO: Files should be added to the viewer asynchronously, currently bringing up the dialog takes a lot of time for large selections (e.g. jdt.ui)

public class ModeWizardSelectionPage extends WizardPage {

    private final static class ModeChangeCellModifier implements ICellModifier {

        private final ModeChangeTable fTable;

        public ModeChangeCellModifier(ModeChangeTable table) {
            fTable = table;
        }

        public boolean canModify(Object element, String property) {
            return PROPERTY_MODE.equals(property);
        }

        public Object getValue(Object element, String property) {
            if (PROPERTY_MODE.equals(property)) {
                final KSubstOption mode = ((ModeChange) element).getNewMode();
                for (int i = 0; i < MODES.length; i++) {
                    if (MODES[i].equals(mode)) {
                        return new Integer(i);
                    }
                }
            }
            return null;
        }

        public void modify(Object element, String property, Object value) {
            if (element instanceof Item)
                element = ((Item) element).getData();
            if (PROPERTY_MODE.equals(property)) {
                ((ModeChange) element).setNewMode(MODES[((Integer) value).intValue()]);
                fTable.modelChanged(true);
            }
        }
    }

    private final static class ModeChangeLabelProvider implements ITableLabelProvider {

        private final DecoratingLabelProvider fDecoratingLP;
        private final ModeChangeTable fTable;

        public ModeChangeLabelProvider(ModeChangeTable table) {
            fTable = table;
            fDecoratingLP = new DecoratingLabelProvider(new WorkbenchLabelProvider(),
                    PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator());
            fDecoratingLP.addListener(fTable);
        }

        public Image getColumnImage(Object element, int columnIndex) {
            if (columnIndex == INDEX_FILE) {
                return fDecoratingLP.getImage(((ModeChange) element).getFile());
            }
            return null;
        }

        public String getColumnText(Object element, int columnIndex) {
            final ModeChange change = (ModeChange) element;
            switch (columnIndex) {
            case INDEX_FILE:
                return (change.hasChanged() ? "* " : "") + change.getFile().getName(); //$NON-NLS-1$ //$NON-NLS-2$
            case INDEX_MODE:
                return change.getNewMode().getLongDisplayText();
            case INDEX_PATH:
                return change.getFile().getFullPath().toOSString();
            }
            throw new IllegalArgumentException();
        }

        public void addListener(ILabelProviderListener listener) {
        }

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

        public void dispose() {
            fDecoratingLP.removeListener(fTable);
        }

        public void removeListener(ILabelProviderListener listener) {
        }
    }

    private final static class TableComparator extends ViewerComparator implements SelectionListener {

        private final Collator fCollator;
        private final TableViewer fViewer;
        private final TableColumn fFile, fMode, fPath;

        private int fIndex;
        private boolean fAscending;

        public TableComparator(TableViewer viewer, TableColumn fileColumn, TableColumn modeColumn,
                TableColumn pathColumn) {
            //         TODO: possible issue, TableSorter's Collator not shared with base class.  Might cause problem switching to ICU collation.
            fCollator = Collator.getInstance();
            fViewer = viewer;

            fFile = fileColumn;
            fMode = modeColumn;
            fPath = pathColumn;

            //         Set initial sorting to file column
            fIndex = INDEX_FILE;
            fViewer.getTable().setSortColumn(fFile);
            fViewer.getTable().setSortDirection(SWT.DOWN);
            fAscending = true;

            fileColumn.addSelectionListener(this);
            modeColumn.addSelectionListener(this);
            pathColumn.addSelectionListener(this);
        }

        public int compare(Viewer viewer, Object e1, Object e2) {

            final ModeChange mc1 = (ModeChange) e1;
            final ModeChange mc2 = (ModeChange) e2;

            final String s1, s2;

            switch (fIndex) {

            case INDEX_FILE:
                s1 = mc1.getFile().getName();
                s2 = mc2.getFile().getName();
                break;

            case INDEX_MODE:
                s1 = mc1.getNewMode().getLongDisplayText();
                s2 = mc2.getNewMode().getLongDisplayText();
                break;

            case INDEX_PATH:
                s1 = mc1.getFile().getFullPath().toOSString();
                s2 = mc2.getFile().getFullPath().toOSString();
                break;

            default:
                throw new IllegalArgumentException();
            }

            final int compare = fCollator.compare(s1, s2);
            return fAscending ? compare : -compare;
        }

        public void widgetSelected(SelectionEvent e) {
            final int index = columnToIndex(e.widget);
            if (index == fIndex) {
                fIndex = index;
                fAscending = !fAscending;
                fViewer.getTable().setSortDirection(fAscending ? SWT.DOWN : SWT.UP);
            } else {
                fIndex = index;
                TableColumn tableCol = null;
                switch (fIndex) {
                case INDEX_FILE:
                    tableCol = fFile;
                    break;

                case INDEX_MODE:
                    tableCol = fMode;
                    break;

                case INDEX_PATH:
                    tableCol = fPath;
                    break;
                }
                fViewer.getTable().setSortColumn(tableCol);
                fViewer.getTable().setSortDirection(fAscending ? SWT.DOWN : SWT.UP);
            }
            fViewer.refresh();
        }

        public void widgetDefaultSelected(SelectionEvent e) {
            // nop
        }

        private int columnToIndex(Object column) {
            if (column == fFile)
                return INDEX_FILE;
            if (column == fMode)
                return INDEX_MODE;
            if (column == fPath)
                return INDEX_PATH;
            throw new IllegalArgumentException();
        }
    }

    private static final class ModeChangeTable extends Observable
            implements ISelectionChangedListener, ILabelProviderListener {

        private final List fChanges;
        private final TableViewer fViewer;
        private final Filter fFilter;
        private int fNumberOfChanges;

        public ModeChangeTable(Composite composite, PixelConverter converter, List changes) {

            fChanges = changes;
            fNumberOfChanges = 0;

            /**
             * Create a table.
             */
            final Table table = new Table(composite, SWT.V_SCROLL | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
            table.setLayoutData(SWTUtils.createHVFillGridData());
            table.setLinesVisible(false);
            table.setHeaderVisible(true);

            /**
             * The 'File' column
             */
            final TableColumn fileColumn = new TableColumn(table, SWT.NONE, INDEX_FILE);
            fileColumn.setWidth(converter.convertWidthInCharsToPixels(LARGE_COLUMN));
            fileColumn.setText(CVSUIMessages.ModeWizardSelectionPage_2);
            table.setSortColumn(fileColumn);
            table.setSortDirection(SWT.DOWN);
            /**
             * The 'Mode' column
             */
            final TableColumn newModeColumn = new TableColumn(table, SWT.NONE, INDEX_MODE);
            newModeColumn.setWidth(converter.convertWidthInCharsToPixels(COLUMN_MIN_WIDTH_IN_CHARS + 6));
            newModeColumn.setText(CVSUIMessages.ModeWizardSelectionPage_3);

            /**
             * The 'Path' column
             */
            final TableColumn pathColumn = new TableColumn(table, SWT.NONE, INDEX_PATH);
            pathColumn.setWidth(converter.convertWidthInCharsToPixels(50));
            pathColumn.setText(CVSUIMessages.ModeWizardSelectionPage_4);

            fViewer = new TableViewer(table);
            fViewer.setContentProvider(new ModeChangeContentProvider());
            fViewer.setLabelProvider(new ModeChangeLabelProvider(this));
            fViewer.getControl().setLayoutData(SWTUtils.createHVFillGridData());

            final CellEditor newModeEditor = new ComboBoxCellEditor(table, COMBO_TEXT, SWT.READ_ONLY);

            fViewer.setCellEditors(new CellEditor[] { null, newModeEditor, null });
            fViewer.setColumnProperties(new String[] { PROPERTY_FILE, PROPERTY_MODE, PROPERTY_CHANGED });
            fViewer.setCellModifier(new ModeChangeCellModifier(this));

            fViewer.addFilter(fFilter = new Filter());

            fViewer.setComparator(new TableComparator(fViewer, fileColumn, newModeColumn, pathColumn));

            fViewer.setInput(fChanges);

            //TODO: CVSLightweightDecorator.decorate() is lighter than normal decs.
            fViewer.addSelectionChangedListener(this);

            fileColumn.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {

                }
            });

            fViewer.refresh();
        }

        public TableViewer getViewer() {
            return fViewer;
        }

        public void selectionChanged(SelectionChangedEvent event) {
            setChanged();
            notifyObservers(fViewer.getSelection());
        }

        public void modelChanged(boolean updateLabels) {
            fViewer.refresh(updateLabels);
            fNumberOfChanges = 0;
            for (Iterator iter = fChanges.iterator(); iter.hasNext();) {
                ModeChange change = (ModeChange) iter.next();
                if (change.hasChanged())
                    ++fNumberOfChanges;
            }
            setChanged();
            notifyObservers();
        }

        public Filter getFilter() {
            return fFilter;
        }

        public IStructuredSelection getSelection() {
            return (IStructuredSelection) fViewer.getSelection();
        }

        public void labelProviderChanged(LabelProviderChangedEvent event) {
            fViewer.refresh();
        }

        public void selectAll() {
            fViewer.setSelection(new StructuredSelection(fChanges));
            fViewer.getControl().setFocus();
        }

        public void selectNone() {
            fViewer.setSelection(StructuredSelection.EMPTY);
            fViewer.getControl().setFocus();
        }

        public int getNumberOfChanges() {
            return fNumberOfChanges;
        }
    }

    private static final class ModeChangeContentProvider implements IStructuredContentProvider {

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        public Object[] getElements(Object inputElement) {
            //         new FetchJob(fViewer, (List)inputElement, fPattern);
            //         return new Object[0];
            return ((List) inputElement).toArray();
        }

        public void dispose() {
        }
    }

    private static final class ModeCombo extends SelectionAdapter implements Observer {

        private final Combo fCombo;
        private final ModeChangeTable fTable;

        public ModeCombo(ModeChangeTable table, Composite parent) {
            fTable = table;
            fCombo = new Combo(parent, SWT.READ_ONLY);
            fCombo.setLayoutData(SWTUtils.createHFillGridData());
            fCombo.setItems(COMBO_TEXT);
            fCombo.addSelectionListener(this);
            fTable.addObserver(this);
        }

        public void widgetSelected(SelectionEvent e) {
            final KSubstOption mode = MODES[fCombo.getSelectionIndex()];
            final IStructuredSelection selection = fTable.getSelection();
            for (final Iterator iter = selection.iterator(); iter.hasNext();) {
                final ModeChange change = (ModeChange) iter.next();
                change.setNewMode(mode);
            }
            fTable.modelChanged(true);
        }

        public void update(Observable o, Object arg) {
            final IStructuredSelection selection = (IStructuredSelection) fTable.getViewer().getSelection();

            if (selection.isEmpty()) {
                fCombo.deselectAll();
                fCombo.setEnabled(false);
            } else {
                fCombo.setEnabled(true);
                final KSubstOption option = ((ModeChange) selection.getFirstElement()).getNewMode();
                for (Iterator iter = selection.iterator(); iter.hasNext();) {
                    if (option != ((ModeChange) iter.next()).getNewMode()) {
                        fCombo.deselectAll();
                        return;
                    }
                }
                fCombo.setText(option.getLongDisplayText());
            }
        }
    }

    private static final class Filter extends ViewerFilter {

        private boolean fFilterUnchanged;
        private StringMatcher fMatcher;

        public Filter() {
            fFilterUnchanged = false;
            fMatcher = new StringMatcher("*", true, false); //$NON-NLS-1$
        }

        public boolean select(Viewer viewer, Object parentElement, Object element) {
            final ModeChange change = (ModeChange) element;
            if (fFilterUnchanged && !change.hasChanged())
                return false;
            if (!fMatcher.match(change.getFile().getName()))
                return false;
            return true;
        }

        public void setPattern(String pattern) {
            pattern = pattern.trim();
            if (!pattern.endsWith("*")) { //$NON-NLS-1$
                pattern += "*"; //$NON-NLS-1$
            }
            fMatcher = new StringMatcher(pattern, true, false);
        }

        public void filterUnchanged(boolean filter) {
            fFilterUnchanged = filter;
        }
    }

    private static final class ResetButton extends SelectionAdapter implements Observer {

        private final ModeChangeTable fTable;
        private final Button fButton;

        public ResetButton(ModeChangeTable table, Composite parent, PixelConverter converter) {
            final int buttonWidth = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
            fTable = table;
            fButton = new Button(parent, SWT.NONE);
            fButton.setLayoutData(
                    SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.CENTER, false, false));
            fButton.setText(CVSUIMessages.ModeWizardSelectionPage_8);
            fButton.setToolTipText(CVSUIMessages.ModeWizardSelectionPage_9);
            fButton.addSelectionListener(this);
            fTable.addObserver(this);
        }

        public void widgetSelected(SelectionEvent e) {
            fButton.setEnabled(false);
            final IStructuredSelection selection = fTable.getSelection();
            for (Iterator iter = selection.iterator(); iter.hasNext();) {
                final ModeChange change = (ModeChange) iter.next();
                change.setNewMode(change.getMode());
            }
            fTable.modelChanged(true);
        }

        public void update(Observable o, Object arg) {
            final IStructuredSelection selection = fTable.getSelection();
            for (final Iterator iter = selection.iterator(); iter.hasNext();) {
                if (((ModeChange) iter.next()).hasChanged()) {
                    fButton.setEnabled(true);
                    return;
                }
            }
            fButton.setEnabled(false);
        }
    }

    private static final class GuessButton extends SelectionAdapter implements Observer {

        private final ModeChangeTable fTable;
        private final Button fButton;

        public GuessButton(ModeChangeTable table, Composite parent, PixelConverter converter) {
            fTable = table;
            final int buttonWidth = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
            fButton = new Button(parent, SWT.NONE);
            fButton.setLayoutData(
                    SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.CENTER, false, false));
            fButton.setText(CVSUIMessages.ModeWizardSelectionPage_10);
            fButton.setToolTipText(CVSUIMessages.ModeWizardSelectionPage_11);
            fButton.addSelectionListener(this);
            fTable.addObserver(this);

        }

        public void widgetSelected(SelectionEvent e) {
            final IStructuredSelection selection = fTable.getSelection();
            for (Iterator iter = selection.iterator(); iter.hasNext();) {
                final ModeChange change = (ModeChange) iter.next();
                change.setNewMode(KSubstOption.fromFile(change.getFile()));
            }
            fTable.modelChanged(true);
        }

        public void update(Observable o, Object arg) {
            fButton.setEnabled(!fTable.getSelection().isEmpty());
        }
    }

    private static final class SelectAllButton extends SelectionAdapter {

        private final ModeWizardSelectionPage fPage;
        private final Button fButton;

        public SelectAllButton(ModeWizardSelectionPage page, Composite parent, PixelConverter converter) {
            fPage = page;
            final int buttonWidth = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
            fButton = new Button(parent, SWT.NONE);
            fButton.setLayoutData(
                    SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.CENTER, false, false));
            fButton.setText(CVSUIMessages.ModeWizardSelectionPage_12);
            fButton.addSelectionListener(this);
        }

        public void widgetSelected(SelectionEvent e) {
            fPage.getTable().selectAll();
        }
    }

    private static final class SelectNoneButton extends SelectionAdapter {

        private final ModeWizardSelectionPage fPage;
        private final Button fButton;

        public SelectNoneButton(ModeWizardSelectionPage page, Composite parent, PixelConverter converter) {
            fPage = page;
            final int buttonWidth = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
            fButton = new Button(parent, SWT.NONE);
            fButton.setLayoutData(
                    SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.CENTER, false, false));
            fButton.setText(CVSUIMessages.ModeWizardSelectionPage_13);
            fButton.addSelectionListener(this);
        }

        public void widgetSelected(SelectionEvent e) {
            fPage.getTable().selectNone();
        }
    }

    private static final class ShowChangesOnlyCheckbox extends SelectionAdapter {

        private final ModeWizardSelectionPage fPage;
        private final Button fCheck;

        public ShowChangesOnlyCheckbox(ModeWizardSelectionPage page, Composite parent) {
            fPage = page;
            fCheck = new Button(parent, SWT.CHECK);
            fCheck.setText(CVSUIMessages.ModeWizardSelectionPage_14);
            fCheck.setLayoutData(SWTUtils.createHFillGridData());
            fCheck.setSelection(false);
            fCheck.addSelectionListener(this);
        }

        public void widgetSelected(SelectionEvent e) {
            final ModeChangeTable table = fPage.getTable();
            table.getFilter().filterUnchanged(fCheck.getSelection());
            table.modelChanged(true);
        }
    }

    private static final class FilterTextBox extends SelectionAdapter implements ModifyListener {
        private final ModeWizardSelectionPage fPage;
        private final Text fTextField;

        public FilterTextBox(ModeWizardSelectionPage page, Composite parent, PixelConverter converter) {
            fPage = page;
            fTextField = new Text(parent, SWT.SINGLE | SWT.BORDER);
            fTextField.setLayoutData(SWTUtils.createHFillGridData());

            final int buttonWidth = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
            final Button button = new Button(parent, SWT.PUSH);
            button.setText(CVSUIMessages.ModeWizardSelectionPage_15);
            button.setLayoutData(
                    SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.CENTER, false, false));
            button.addSelectionListener(this);

            fTextField.addModifyListener(this);
        }

        public void widgetSelected(SelectionEvent e) {
            fTextField.setText(""); //$NON-NLS-1$
            fTextField.setFocus();
        }

        public void modifyText(ModifyEvent e) {
            final ModeChangeTable table = fPage.getTable();
            table.getFilter().setPattern(fTextField.getText());
            table.modelChanged(false);
        }

        public void setFocus() {
            fTextField.setFocus();
        }
    }

    private static final class ChangeCounterLabel implements Observer {

        private final Label fLabel;
        private final ModeChangeTable fTable;

        ChangeCounterLabel(Composite parent, ModeChangeTable table) {
            fTable = table;
            fTable.addObserver(this);
            fLabel = SWTUtils.createLabel(parent, null);
            fLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
        }

        public void update(Observable o, Object arg) {
            updateText(fTable.getNumberOfChanges());
        }

        /**
         * @param numberOfChanges
         */
        private void updateText(int numberOfChanges) {
            fLabel.setText(NLS.bind(CVSUIMessages.ModeWizardSelectionPage_17,
                    new String[] { Integer.toString(numberOfChanges) }));
        }

    }

    private static final class SelectionCounterLabel implements Observer {

        private final Label fLabel;
        private final ModeChangeTable fTable;

        public SelectionCounterLabel(Composite parent, ModeChangeTable table) {
            fTable = table;
            fTable.addObserver(this);
            fLabel = new Label(parent, SWT.WRAP | SWT.RIGHT);
            fLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
        }

        public void update(Observable o, Object arg) {
            updateText(fTable.getSelection().size());
        }

        /**
         * @param numberOfChanges
         */
        private void updateText(int selected) {
            fLabel.setText(NLS.bind(CVSUIMessages.ModeWizardSelectionPage_25,
                    new String[] { Integer.toString(selected) }));
        }
    }

    private final static int LARGE_COLUMN = 50;

    protected static final int INDEX_FILE = 0;
    protected static final int INDEX_MODE = 1;
    protected static final int INDEX_PATH = 2;

    protected static final String PROPERTY_FILE = "file"; //$NON-NLS-1$
    protected static final String PROPERTY_MODE = "mode"; //$NON-NLS-1$
    protected static final String PROPERTY_CHANGED = "changed"; //$NON-NLS-1$

    protected static final KSubstOption[] MODES;
    protected static final String[] COMBO_TEXT;

    static final int COLUMN_MIN_WIDTH_IN_CHARS;

    static {
        MODES = KSubstOption.getAllKSubstOptions();
        Arrays.sort(MODES, new Comparator() {
            public int compare(Object a, Object b) {
                String aKey = ((KSubstOption) a).getLongDisplayText();
                String bKey = ((KSubstOption) b).getLongDisplayText();
                return aKey.compareTo(bKey);
            }
        });
        COMBO_TEXT = new String[MODES.length];
        int maxLength = 0;
        for (int i = 0; i < MODES.length; i++) {
            COMBO_TEXT[i] = MODES[i].getLongDisplayText();
            if (COMBO_TEXT[i].length() > maxLength)
                maxLength = COMBO_TEXT[i].length();
        }
        COLUMN_MIN_WIDTH_IN_CHARS = maxLength;
    }

    private final List fChanges;
    protected ModeChangeTable fTable;

    private CommitCommentArea fCommentArea;

    public ModeWizardSelectionPage(List modeChanges) {
        super(CVSUIMessages.ModeWizardSelectionPage_18, CVSUIMessages.ModeWizardSelectionPage_19,
                CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_WIZBAN_KEYWORD)); // 
        setDescription(CVSUIMessages.ModeWizardSelectionPage_20);
        fChanges = modeChanges;
    }

    public void createControl(final Composite parent) {

        final PixelConverter converter = SWTUtils.createDialogPixelConverter(parent);

        final int horizontalSpace = converter.convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
        final int verticalSpace = converter.convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);

        /**
         * The main composite with the vertical sash
         */
        final Composite mainComposite = SWTUtils.createHVFillComposite(parent, SWTUtils.MARGINS_DEFAULT);
        // set F1 help
        PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, IHelpContextIds.KEYWORD_SUBSTITUTION_PAGE);

        final SashForm mainSash = new SashForm(mainComposite, SWT.VERTICAL);
        mainSash.setLayoutData(SWTUtils.createHVFillGridData());

        /**
         * The composite with the filter box, the table and the selection and filter controls.
         */
        final Composite topComposite = SWTUtils.createHVFillComposite(mainSash, SWTUtils.MARGINS_NONE);
        ((GridLayout) topComposite.getLayout()).marginBottom = verticalSpace;

        final Composite topGroup = SWTUtils.createHVFillGroup(topComposite,
                CVSUIMessages.ModeWizardSelectionPage_21, SWTUtils.MARGINS_DIALOG);

        final Composite filterComposite = SWTUtils.createHFillComposite(topGroup, SWTUtils.MARGINS_NONE, 2);
        final FilterTextBox filterBox = new FilterTextBox(ModeWizardSelectionPage.this, filterComposite, converter);

        fTable = new ModeChangeTable(topGroup, converter, fChanges);

        final Composite selectionComposite = SWTUtils.createHFillComposite(topGroup, SWTUtils.MARGINS_NONE, 2);

        new ChangeCounterLabel(selectionComposite, fTable);
        new SelectionCounterLabel(selectionComposite, fTable);

        new ShowChangesOnlyCheckbox(ModeWizardSelectionPage.this, selectionComposite);

        final Composite buttonComposite = SWTUtils.createHFillComposite(selectionComposite, SWTUtils.MARGINS_NONE,
                2);
        buttonComposite.setLayoutData(new GridData());
        new SelectAllButton(ModeWizardSelectionPage.this, buttonComposite, converter);
        new SelectNoneButton(ModeWizardSelectionPage.this, buttonComposite, converter);

        /**
         * The bottom sash which separates the mode controls from the commit comment area
         */
        final SashForm bottomSash = new SashForm(mainSash, SWT.NONE);
        bottomSash.setLayoutData(SWTUtils.createHFillGridData());

        /**
         * The left composite with the mode controls.
         */
        final Composite leftComposite = SWTUtils.createHVFillComposite(bottomSash, SWTUtils.MARGINS_NONE, 1);
        ((GridLayout) leftComposite.getLayout()).marginRight = horizontalSpace;
        ((GridLayout) leftComposite.getLayout()).marginTop = verticalSpace;

        final Group leftGroup = SWTUtils.createHVFillGroup(leftComposite, CVSUIMessages.ModeWizardSelectionPage_22,
                SWTUtils.MARGINS_DIALOG, 3);

        new ModeCombo(fTable, leftGroup);
        new GuessButton(fTable, leftGroup, converter);
        new ResetButton(fTable, leftGroup, converter);
        SWTUtils.createPlaceholder(leftGroup, 1);
        final Label infoLabel = SWTUtils.createLabel(leftGroup, CVSUIMessages.ModeWizardSelectionPage_23, 3);

        fTable.addObserver(new Observer() {
            public void update(Observable o, Object arg) {
                final boolean enabled = !fTable.getSelection().isEmpty();
                leftGroup.setEnabled(enabled);
                infoLabel.setEnabled(enabled);
            }
        });

        /**
         * The right composite with the commit comment area.
         */
        final Composite rightComposite = SWTUtils.createHVFillComposite(bottomSash, SWTUtils.MARGINS_NONE);
        ((GridLayout) rightComposite.getLayout()).marginLeft = horizontalSpace;
        ((GridLayout) rightComposite.getLayout()).marginTop = verticalSpace;

        final Group rightGroup = SWTUtils.createHVFillGroup(rightComposite,
                CVSUIMessages.ModeWizardSelectionPage_24, SWTUtils.MARGINS_DIALOG);
        (fCommentArea = new CommitCommentArea()).createArea(rightGroup);

        /**
         * Set up the page
         */
        mainSash.setWeights(new int[] { 5, 2 });
        bottomSash.setWeights(new int[] { 3, 2 });
        fTable.modelChanged(true);
        fTable.selectAll();
        filterBox.setFocus();
        setupListeners();
        setControl(mainComposite);
        validatePage();
    }

    private void setupListeners() {
        fCommentArea.addPropertyChangeListener(new IPropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent event) {
                if (event.getProperty() != null) {
                    validatePage();
                }
            }
        });
    }

    protected ModeChangeTable getTable() {
        return fTable;
    }

    public List getChanges() {
        final List changes = new ArrayList();
        for (Iterator iter = fChanges.iterator(); iter.hasNext();) {
            final ModeChange change = (ModeChange) iter.next();
            if (change.hasChanged())
                changes.add(change);
        }
        return changes;
    }

    public String getComment(Shell shell) {
        return fCommentArea.getCommentWithPrompt(shell);
    }

    private void validatePage() {
        if (fCommentArea.getComment(false).equals("")) { //$NON-NLS-1$
            final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
            final String allowEmptyComment = store.getString(ICVSUIConstants.PREF_ALLOW_EMPTY_COMMIT_COMMENTS);
            if (allowEmptyComment.equals(MessageDialogWithToggle.NEVER)) {
                setPageComplete(false); // then the page is not complete
                return;
            }
        }
        setPageComplete(true);
    }

}