org.eclipse.team.internal.ccvs.ui.CVSHistoryTableProvider.java Source code

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2006, 2013 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
 *     Brock Janiczak (brockj@tpg.com.au) - Bug 180436 Use table sort indicators on CVS
 *     Brock Janiczak (brockj@tpg.com.au) - Bug 181899 CVS History wrongly ordered
 *     Brock Janiczak <brockj@tpg.com.au> - Bug 182442 Display full comment in tooltip
 *     Olexiy Buyanskyy <olexiyb@gmail.com> - Bug 76386 - [History View] CVS Resource History shows revisions from all branches
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.ui;

import java.util.Date;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.ImageDescriptor;
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.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.history.*;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
import org.eclipse.team.internal.ccvs.core.filehistory.CVSFileRevision;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.core.history.LocalFileRevision;
import org.eclipse.team.internal.ui.TeamUIMessages;
import org.eclipse.team.internal.ui.history.AbstractHistoryCategory;
import org.eclipse.team.internal.ui.history.DateHistoryCategory;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;

import com.ibm.icu.text.DateFormat;

public class CVSHistoryTableProvider {

    public static final String CATEGORIES_COLOR = "org.eclipse.team.cvs.ui.fontsandcolors.cvshistorypagecategories"; //$NON-NLS-1$

    private IFileHistory currentFileHistory;
    private IFile workspaceFile;

    /* private */TreeViewer viewer;
    /* private */Font currentRevisionFont;

    private boolean baseModified;

    private IDialogSettings settings;

    //column constants
    private static final int COL_REVISIONID = 0;
    private static final int COL_BRANCHES = 1;
    private static final int COL_TAGS = 2;
    private static final int COL_DATE = 3;
    private static final int COL_AUTHOR = 4;
    private static final int COL_COMMENT = 5;

    // cvs history table provider settings section name
    private static final String CVS_HISTORY_TABLE_PROVIDER_SECTION = CVSHistoryTableProvider.class.getName();

    // cvs history table provider settings keys
    private static final String COL_REVISIONID_NAME = "COL_REVISIONID"; //$NON-NLS-1$
    private static final String COL_BRANCHES_NAME = "COL_BRANCHES"; //$NON-NLS-1$
    private static final String COL_TAGS_NAME = "COL_TAGS"; //$NON-NLS-1$
    private static final String COL_DATE_NAME = "COL_DATE"; //$NON-NLS-1$
    private static final String COL_AUTHOR_NAME = "COL_AUTHOR"; //$NON-NLS-1$
    private static final String COL_COMMENT_NAME = "COL_COMMENT"; //$NON-NLS-1$

    private static final String COL_NAME = "COLUMN_NAME"; //$NON-NLS-1$
    private static final String SORT_COL_NAME = "SORT_COL_NAME"; //$NON-NLS-1$
    private static final String SORT_COL_DIRECTION = "SORT_COL_DIRECTION"; //$NON-NLS-1$

    public CVSHistoryTableProvider() {
        IDialogSettings viewsSettings = CVSUIPlugin.getPlugin().getDialogSettings();
        settings = viewsSettings.getSection(CVS_HISTORY_TABLE_PROVIDER_SECTION);
        if (settings == null) {
            settings = viewsSettings.addNewSection(CVS_HISTORY_TABLE_PROVIDER_SECTION);
        }
    }

    /**
     * The history label provider.
     */
    class HistoryLabelProvider extends ColumnLabelProvider {

        Image dateImage = null;
        ImageDescriptor dateDesc = null;

        Image localRevImage = null;
        ImageDescriptor localRevDesc = null;

        Image remoteRevImage = null;
        ImageDescriptor remoteRevDesc = null;

        ThemeListener themeListener;
        private DateFormat dateFormat;
        private final int column;

        public HistoryLabelProvider(int column, CVSHistoryTableProvider provider) {
            this.column = column;
            PlatformUI.getWorkbench().getThemeManager()
                    .addPropertyChangeListener(themeListener = new ThemeListener(provider));
        }

        public void dispose() {
            if (dateImage != null) {
                dateImage.dispose();
                dateImage = null;
            }

            if (localRevImage != null) {
                localRevImage.dispose();
                localRevImage = null;
            }

            if (remoteRevImage != null) {
                remoteRevImage.dispose();
                remoteRevImage = null;
            }

            if (themeListener != null) {
                PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(themeListener);
            }
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
         */
        public String getToolTipText(Object element) {
            if (column == COL_COMMENT && !isSingleLine(element)) {
                IFileRevision entry = adaptToFileRevision(element);
                if (entry != null)
                    return entry.getComment();
            }
            return null;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.CellLabelProvider#useNativeToolTip(java.lang.Object)
         */
        public boolean useNativeToolTip(Object object) {
            return column != COL_COMMENT || isSingleLine(object);
        }

        private boolean isSingleLine(Object object) {
            IFileRevision entry = adaptToFileRevision(object);
            if (entry != null)
                return entry.getComment() == null || entry.getComment().indexOf('\n') == -1;
            return true;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.ColumnLabelProvider#getImage(java.lang.Object)
         */
        public Image getImage(Object element) {
            return getColumnImage(element, column);
        }

        public Image getColumnImage(Object element, int columnIndex) {
            if (element instanceof DateHistoryCategory && columnIndex == COL_REVISIONID) {
                if (dateImage == null) {
                    dateDesc = CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_DATES_CATEGORY);
                    dateImage = dateDesc.createImage();
                }
                return dateImage;
            }

            if (element instanceof LocalFileRevision && columnIndex == COL_REVISIONID) {
                if (localRevImage == null) {
                    localRevDesc = CVSUIPlugin.getPlugin()
                            .getImageDescriptor(ICVSUIConstants.IMG_LOCALREVISION_TABLE);
                    localRevImage = localRevDesc.createImage();
                }
                return localRevImage;
            }

            if (element instanceof CVSFileRevision && columnIndex == COL_REVISIONID) {
                if (remoteRevImage == null) {
                    remoteRevDesc = CVSUIPlugin.getPlugin()
                            .getImageDescriptor(ICVSUIConstants.IMG_REMOTEREVISION_TABLE);
                    remoteRevImage = remoteRevDesc.createImage();
                }
                return remoteRevImage;
            }
            return null;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
         */
        public String getText(Object element) {
            return getColumnText(element, column);
        }

        public String getColumnText(Object element, int columnIndex) {
            if (element instanceof AbstractHistoryCategory) {
                if (columnIndex != COL_REVISIONID)
                    return ""; //$NON-NLS-1$

                return ((AbstractHistoryCategory) element).getName();
            }

            IFileRevision entry = adaptToFileRevision(element);
            if (entry == null)
                return ""; //$NON-NLS-1$
            switch (columnIndex) {
            case COL_REVISIONID:
                String revision = entry.getContentIdentifier();
                String currentRevision = getCurrentRevision();
                if (currentRevision != null && currentRevision.equals(revision)) {
                    if (baseModified)
                        revision = NLS.bind(CVSUIMessages.nameAndRevision,
                                new String[] { revision, CVSUIMessages.CVSHistoryTableProvider_base }); //NLS.bind(CVSUIMessages.currentRevision, new String[] { revision }); 
                    else
                        revision = NLS.bind(CVSUIMessages.currentRevision, new String[] { revision }); //NLS.bind(CVSUIMessages.currentRevision, new String[] { revision });
                }
                return revision;
            case COL_BRANCHES:
                ITag[] branches = entry.getBranches();
                StringBuffer resultBranches = new StringBuffer();
                for (int i = 0; i < branches.length; i++) {
                    resultBranches.append(branches[i].getName());
                    if (i < branches.length - 1) {
                        resultBranches.append(", "); //$NON-NLS-1$
                    }
                }
                return resultBranches.toString();
            case COL_TAGS:
                ITag[] tags = entry.getTags();
                StringBuffer result = new StringBuffer();
                for (int i = 0; i < tags.length; i++) {
                    result.append(tags[i].getName());
                    if (i < tags.length - 1) {
                        result.append(", "); //$NON-NLS-1$
                    }
                }
                return result.toString();
            case COL_DATE:
                long date = entry.getTimestamp();
                Date dateFromLong = new Date(date);
                return getDateFormat().format(dateFromLong);
            case COL_AUTHOR:
                return entry.getAuthor();
            case COL_COMMENT:
                return getCommentAsSingleLine(entry);
            }
            return ""; //$NON-NLS-1$
        }

        private synchronized DateFormat getDateFormat() {
            if (dateFormat == null)
                dateFormat = DateFormat.getInstance();
            return dateFormat;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
         */
        public Color getForeground(Object element) {
            if (element instanceof AbstractHistoryCategory) {
                ITheme current = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
                return current.getColorRegistry().get(CVSHistoryTableProvider.CATEGORIES_COLOR);
            }

            IFileRevision entry = adaptToFileRevision(element);
            if (!entry.exists()) {
                return Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
            }

            return null;
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
         */
        public Color getBackground(Object element) {
            return null;
        }

        /*
         * (non-Javadoc)
         * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
         */
        public Font getFont(Object element) {
            if (element instanceof AbstractHistoryCategory) {
                return getCurrentRevisionFont();
            }

            IFileRevision entry = adaptToFileRevision(element);
            if (entry == null)
                return null;
            String revision = entry.getContentIdentifier();
            //String comment = entry.getComment();
            String tempCurrentRevision = getCurrentRevision();
            Font returnFont = null;

            if (tempCurrentRevision != null && tempCurrentRevision.equals(revision)) {
                returnFont = getCurrentRevisionFont();
            }
            //Check to see if this is the local workspace file
            if (workspaceFile != null && entry instanceof LocalFileRevision) {
                LocalFileRevision localRevision = (LocalFileRevision) entry;
                if (localRevision.isCurrentState())
                    return getCurrentRevisionFont();
            }

            return returnFont;
        }

        private Font getCurrentRevisionFont() {
            if (currentRevisionFont == null) {
                Font defaultFont = JFaceResources.getDefaultFont();
                FontData[] data = defaultFont.getFontData();
                for (int i = 0; i < data.length; i++) {
                    data[i].setStyle(SWT.BOLD);
                }
                currentRevisionFont = new Font(viewer.getTree().getDisplay(), data);
            }
            return currentRevisionFont;
        }

    }

    /**
     * The history sorter
     */
    class HistoryComparator extends ViewerComparator {
        private boolean reversed = false;
        private int columnNumber;

        private VersionCollator versionCollator = new VersionCollator();

        // column headings:   "Revision" "Branches" "Tags" "Date" "Author" "Comment"
        private int[][] SORT_ORDERS_BY_COLUMN = {
                { COL_REVISIONID, COL_DATE, COL_AUTHOR, COL_COMMENT, COL_TAGS, COL_BRANCHES }, /* revision */
                { COL_BRANCHES, COL_DATE, COL_REVISIONID, COL_AUTHOR, COL_COMMENT, COL_TAGS }, /* tags */
                { COL_TAGS, COL_DATE, COL_REVISIONID, COL_AUTHOR, COL_COMMENT, COL_BRANCHES }, /* tags */
                { COL_DATE, COL_REVISIONID, COL_AUTHOR, COL_COMMENT, COL_TAGS, COL_BRANCHES }, /* date */
                { COL_AUTHOR, COL_REVISIONID, COL_DATE, COL_COMMENT, COL_TAGS, COL_BRANCHES }, /* author */
                { COL_COMMENT, COL_REVISIONID, COL_DATE, COL_AUTHOR, COL_TAGS, COL_BRANCHES } /* comment */
        };

        /**
         * The constructor.
         */
        public HistoryComparator(int columnNumber) {
            this.columnNumber = columnNumber;
        }

        /**
         * Compares two log entries, sorting first by the main column of this sorter,
         * then by subsequent columns, depending on the column sort order.
         */
        public int compare(Viewer compareViewer, Object o1, Object o2) {
            if (o1 instanceof AbstractHistoryCategory || o2 instanceof AbstractHistoryCategory)
                return 0;

            IFileRevision e1 = adaptToFileRevision(o1);
            IFileRevision e2 = adaptToFileRevision(o2);
            int result = 0;
            if (e1 == null || e2 == null) {
                result = super.compare(compareViewer, o1, o2);
            } else {
                int[] columnSortOrder = SORT_ORDERS_BY_COLUMN[columnNumber];
                for (int i = 0; i < columnSortOrder.length; ++i) {
                    result = compareColumnValue(columnSortOrder[i], e1, e2);
                    if (result != 0)
                        break;
                }
            }
            if (reversed)
                result = -result;
            return result;
        }

        /**
         * Compares two markers, based only on the value of the specified column.
         */
        int compareColumnValue(int columnNumber, IFileRevision e1, IFileRevision e2) {
            switch (columnNumber) {
            case COL_REVISIONID: /* revision */
                if (e1 instanceof LocalFileRevision || e2 instanceof LocalFileRevision) {
                    //compare based on dates
                    long date1 = e1.getTimestamp();
                    long date2 = e2.getTimestamp();
                    if (date1 == date2)
                        return 0;

                    return date1 > date2 ? 1 : -1;
                }
                return versionCollator.compare(e1.getContentIdentifier(), e2.getContentIdentifier());
            case COL_BRANCHES: /* branches */
                ITag[] branches1 = e1.getBranches();
                ITag[] branches2 = e2.getBranches();
                if (branches2.length == 0) {
                    return -1;
                }
                if (branches1.length == 0) {
                    return 1;
                }
                return getComparator().compare(branches1[0].getName(), branches2[0].getName());
            case COL_TAGS: /* tags */
                ITag[] tags1 = e1.getTags();
                ITag[] tags2 = e2.getTags();
                if (tags2.length == 0) {
                    return -1;
                }
                if (tags1.length == 0) {
                    return 1;
                }
                return getComparator().compare(tags1[0].getName(), tags2[0].getName());
            case COL_DATE: /* date */
                long date1 = e1.getTimestamp();
                long date2 = e2.getTimestamp();
                if (date1 == date2)
                    return 0;

                return date1 > date2 ? 1 : -1;

            case COL_AUTHOR: /* author */
                String author1 = e1.getAuthor();
                String author2 = e2.getAuthor();
                if (author2 == null)
                    return -1;

                if (author1 == null)
                    return 1;

                return getComparator().compare(author1, author2);
            case COL_COMMENT: /* comment */
                String comment1 = e1.getComment();
                String comment2 = e2.getComment();
                if (comment2 == null)
                    return -1;

                if (comment1 == null)
                    return 1;

                return getComparator().compare(comment1, comment2);
            default:
                return 0;
            }
        }

        /**
         * Returns the number of the column by which this is sorting.
         */
        public int getColumnNumber() {
            return columnNumber;
        }

        /**
         * Returns true for descending, or false
         * for ascending sorting order.
         */
        public boolean isReversed() {
            return reversed;
        }

        /**
         * Sets the sorting order.
         */
        public void setReversed(boolean newReversed) {
            reversed = newReversed;
        }
    }

    protected IFileRevision adaptToFileRevision(Object element) {
        // Get the log entry for the provided object
        IFileRevision entry = null;
        if (element instanceof IFileRevision) {
            entry = (IFileRevision) element;
        } else if (element instanceof IAdaptable) {
            entry = (IFileRevision) ((IAdaptable) element).getAdapter(IFileRevision.class);
        } else if (element instanceof AbstractHistoryCategory) {
            if (((AbstractHistoryCategory) element).hasRevisions())
                entry = ((AbstractHistoryCategory) element).getRevisions()[0];
        }
        return entry;
    }

    /**
     * Create a TableViewer that can be used to display a list of IFileRevision instances.
     * Ths method provides the labels and sorter but does not provide a content provider
     * 
     * @param parent
     * @return TableViewer
     */
    public TreeViewer createTree(Composite parent) {
        Tree tree = new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
        tree.setHeaderVisible(true);
        tree.setLinesVisible(true);

        GridData data = new GridData(GridData.FILL_BOTH);
        tree.setLayoutData(data);

        TableLayout layout = new TableLayout(true);
        tree.setLayout(layout);

        this.viewer = new TreeViewer(tree);
        createColumns(viewer, layout);

        // Initialize the sorting
        ColumnViewerToolTipSupport.enableFor(viewer);
        viewer.refresh();

        tree.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                if (currentRevisionFont != null) {
                    currentRevisionFont.dispose();
                }
            }
        });

        return viewer;
    }

    /**
     * Creates the columns for the history table.
     */
    private void createColumns(TreeViewer tree, TableLayout layout) {
        SelectionListener headerListener = getColumnListener(viewer);

        // revision
        TreeViewerColumn viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_REVISIONID, this));
        TreeColumn col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_REVISIONID_NAME);
        col.setResizable(true);
        col.setText(TeamUIMessages.GenericHistoryTableProvider_Revision);
        col.addSelectionListener(headerListener);

        // branches
        viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_BRANCHES, this));
        col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_BRANCHES_NAME);
        col.setResizable(true);
        col.setText(CVSUIMessages.HistoryView_branches);
        col.addSelectionListener(headerListener);

        // tags
        viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_TAGS, this));
        col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_TAGS_NAME);
        col.setResizable(true);
        col.setText(CVSUIMessages.HistoryView_tags);
        col.addSelectionListener(headerListener);

        // creation date
        viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_DATE, this));
        col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_DATE_NAME);
        col.setResizable(true);
        col.setText(TeamUIMessages.GenericHistoryTableProvider_RevisionTime);
        col.addSelectionListener(headerListener);

        // author
        viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_AUTHOR, this));
        col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_AUTHOR_NAME);
        col.setResizable(true);
        col.setText(TeamUIMessages.GenericHistoryTableProvider_Author);
        col.addSelectionListener(headerListener);

        //comment
        viewerCol = new TreeViewerColumn(tree, SWT.NONE);
        viewerCol.setLabelProvider(new HistoryLabelProvider(COL_COMMENT, this));
        col = viewerCol.getColumn();
        col.setData(COL_NAME, COL_COMMENT_NAME);
        col.setResizable(true);
        col.setText(TeamUIMessages.GenericHistoryTableProvider_Comment);
        col.addSelectionListener(headerListener);

        loadColumnLayout(layout);
    }

    public void loadColumnLayout(TableLayout layout) {
        int widths[] = new int[] { getSettingsInt(COL_REVISIONID_NAME), getSettingsInt(COL_BRANCHES_NAME),
                getSettingsInt(COL_TAGS_NAME), getSettingsInt(COL_DATE_NAME), getSettingsInt(COL_AUTHOR_NAME),
                getSettingsInt(COL_COMMENT_NAME) };
        ColumnLayoutData weightData[] = getWeightData(widths);
        for (int i = 0; i < weightData.length; i++) {
            layout.addColumnData(weightData[i]);
        }

        String sortName = settings.get(SORT_COL_NAME);
        if (sortName == null) {
            sortName = COL_DATE_NAME;
        }
        int sortDirection = SWT.DOWN;
        try {
            sortDirection = settings.getInt(SORT_COL_DIRECTION);
        } catch (NumberFormatException e) {
            // Silently ignored
        }
        TreeColumn sortColumn = null;
        int columnNumber = 0;
        TreeColumn columns[] = viewer.getTree().getColumns();
        for (int i = 0; i < columns.length; i++) {
            if (columns[i].getData(COL_NAME).equals(sortName)) {
                sortColumn = columns[i];
                columnNumber = i;
            }
        }
        viewer.getTree().setSortColumn(sortColumn);
        viewer.getTree().setSortDirection(sortDirection);
        HistoryComparator sorter = new HistoryComparator(columnNumber);
        sorter.setReversed(sortDirection == SWT.DOWN);
        viewer.setComparator(sorter);
    }

    private int getSettingsInt(String key) {
        String value = settings.get(key);
        int ret = 0;
        try {
            ret = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            ret = -1;
        }
        return ret;
    }

    private ColumnLayoutData[] getWeightData(int[] widths) {
        boolean reset = true;
        for (int i = 0; i < widths.length; i++) {
            if (widths[i] > 0) {
                reset = false;
                break;
            }
        }
        ColumnLayoutData[] ret = new ColumnLayoutData[widths.length];
        for (int i = 0; i < widths.length; i++) {
            if (reset) {
                // use same weight for all columns
                ret[i] = new ColumnWeightData(10, true);
            } else {
                if (widths[i] < 0)
                    ret[i] = new ColumnPixelData(getWidthForColumn(i));
                else
                    ret[i] = new ColumnPixelData(widths[i]);
            }
        }
        return ret;
    }

    private int getWidthForColumn(int i) {
        // see #createColumns
        int chars = 4;
        switch (i) {
        case COL_REVISIONID:
            chars += TeamUIMessages.GenericHistoryTableProvider_Revision.length();
            break;
        case COL_BRANCHES:
            chars += CVSUIMessages.HistoryView_branches.length();
            break;
        case COL_TAGS:
            chars += CVSUIMessages.HistoryView_tags.length();
            break;
        case COL_DATE:
            chars += TeamUIMessages.GenericHistoryTableProvider_RevisionTime.length();
            break;
        case COL_AUTHOR:
            chars += TeamUIMessages.GenericHistoryTableProvider_Author.length();
            break;
        case COL_COMMENT:
            chars += TeamUIMessages.GenericHistoryTableProvider_Comment.length();
            break;
        }
        return new PixelConverter(viewer.getTree()).convertWidthInCharsToPixels(chars);
    }

    public void saveColumnLayout() {
        TreeColumn columns[] = viewer.getTree().getColumns();
        for (int i = 0; i < columns.length; i++) {
            settings.put((String) columns[i].getData(COL_NAME), columns[i].getWidth());
        }
        settings.put(SORT_COL_NAME, (String) viewer.getTree().getSortColumn().getData(COL_NAME));
        settings.put(SORT_COL_DIRECTION, viewer.getTree().getSortDirection());
    }

    /**
     * Adds the listener that sets the sorter.
     */
    private SelectionListener getColumnListener(final TreeViewer treeViewer) {
        /**
         * This class handles selections of the column headers.
         * Selection of the column header will cause resorting
         * of the shown tasks using that column's sorter.
         * Repeated selection of the header will toggle
         * sorting order (ascending versus descending).
         */
        return new SelectionAdapter() {
            /**
             * Handles the case of user selecting the
             * header area.
             * <p>If the column has not been selected previously,
             * it will set the sorter of that column to be
             * the current tasklist sorter. Repeated
             * presses on the same column header will
             * toggle sorting order (ascending/descending).
             */
            public void widgetSelected(SelectionEvent e) {
                // column selected - need to sort
                int column = treeViewer.getTree().indexOf((TreeColumn) e.widget);
                HistoryComparator oldSorter = (HistoryComparator) treeViewer.getComparator();
                TreeColumn treeColumn = ((TreeColumn) e.widget);
                if (oldSorter != null && column == oldSorter.getColumnNumber()) {
                    oldSorter.setReversed(!oldSorter.isReversed());

                    treeViewer.getTree().setSortColumn(treeColumn);
                    treeViewer.getTree().setSortDirection(oldSorter.isReversed() ? SWT.DOWN : SWT.UP);
                    treeViewer.refresh();
                } else {
                    treeViewer.getTree().setSortColumn(treeColumn);
                    treeViewer.getTree().setSortDirection(SWT.UP);
                    treeViewer.setComparator(new HistoryComparator(column));
                }
            }
        };
    }

    public void setFile(IFileHistory fileHistory, IFile workspaceFile) {
        this.currentFileHistory = fileHistory;
        this.workspaceFile = workspaceFile;
    }

    public IFileHistory getIFileHistory() {
        return this.currentFileHistory;
    }

    public String getCurrentRevision() {

        try {
            if (workspaceFile != null) {
                ICVSFile cvsWorkspaceFile = CVSWorkspaceRoot.getCVSFileFor(workspaceFile);
                byte[] syncBytes = cvsWorkspaceFile.getSyncBytes();
                if (syncBytes != null) {
                    String workspaceRevision = ResourceSyncInfo.getRevision(syncBytes);
                    return workspaceRevision;
                }
            }

        } catch (CVSException e) {
        }

        return null;
    }

    public void setBaseModified(boolean modified) {
        this.baseModified = modified;
    }

    private static class ThemeListener implements IPropertyChangeListener {

        private final CVSHistoryTableProvider provider;

        ThemeListener(CVSHistoryTableProvider provider) {
            this.provider = provider;
        }

        public void propertyChange(PropertyChangeEvent event) {
            provider.viewer.refresh();
        }
    }

    public void setWorkspaceFile(IFile workspaceFile) {
        this.workspaceFile = workspaceFile;
    }

    public static String getCommentAsSingleLine(IFileRevision entry) {
        String comment = entry.getComment();
        if (comment != null) {
            int index = comment.indexOf("\n"); //$NON-NLS-1$
            switch (index) {
            case -1:
                return comment;
            case 0:
                return CVSUIMessages.HistoryView_______4;
            default:
                return NLS.bind(CVSUIMessages.CVSCompareRevisionsInput_truncate,
                        new String[] { comment.substring(0, index) });
            }
        }
        return ""; //$NON-NLS-1$
    }
}