/*******************************************************************************
 * 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$
	}
}
