org.tigris.subversion.subclipse.ui.history.SVNHistoryPage.java Source code

Java tutorial

Introduction

Here is the source code for org.tigris.subversion.subclipse.ui.history.SVNHistoryPage.java

Source

/*******************************************************************************
 * Copyright (c) 2006 Subclipse project 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:
 *     Subclipse project committers - initial API and implementation
 ******************************************************************************/
package org.tigris.subversion.subclipse.ui.history;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.ui.history.HistoryPage;
import org.eclipse.team.ui.history.IHistoryPageSite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.tigris.subversion.subclipse.core.IResourceStateChangeListener;
import org.tigris.subversion.subclipse.core.ISVNLocalFile;
import org.tigris.subversion.subclipse.core.ISVNLocalResource;
import org.tigris.subversion.subclipse.core.ISVNRemoteFile;
import org.tigris.subversion.subclipse.core.ISVNRemoteResource;
import org.tigris.subversion.subclipse.core.ISVNResource;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.core.SVNProviderPlugin;
import org.tigris.subversion.subclipse.core.SVNStatus;
import org.tigris.subversion.subclipse.core.SVNTeamProvider;
import org.tigris.subversion.subclipse.core.commands.ChangeCommitPropertiesCommand;
import org.tigris.subversion.subclipse.core.commands.GetLogsCommand;
import org.tigris.subversion.subclipse.core.history.AliasManager;
import org.tigris.subversion.subclipse.core.history.ILogEntry;
import org.tigris.subversion.subclipse.core.history.LogEntry;
import org.tigris.subversion.subclipse.core.history.LogEntryChangePath;
import org.tigris.subversion.subclipse.core.resources.LocalResourceStatus;
import org.tigris.subversion.subclipse.core.resources.RemoteFolder;
import org.tigris.subversion.subclipse.core.resources.RemoteResource;
import org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
import org.tigris.subversion.subclipse.ui.CancelableSVNLogMessageCallback;
import org.tigris.subversion.subclipse.ui.IHelpContextIds;
import org.tigris.subversion.subclipse.ui.ISVNUIConstants;
import org.tigris.subversion.subclipse.ui.Policy;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.actions.ExportRemoteFolderAction;
import org.tigris.subversion.subclipse.ui.actions.GenerateChangeLogAction;
import org.tigris.subversion.subclipse.ui.actions.OpenRemoteFileAction;
import org.tigris.subversion.subclipse.ui.actions.ShowAnnotationAction;
import org.tigris.subversion.subclipse.ui.actions.ShowDifferencesAsUnifiedDiffAction;
import org.tigris.subversion.subclipse.ui.actions.ShowHistoryAction;
import org.tigris.subversion.subclipse.ui.actions.WorkspaceAction;
import org.tigris.subversion.subclipse.ui.console.TextViewerAction;
import org.tigris.subversion.subclipse.ui.dialogs.HistorySearchDialog;
import org.tigris.subversion.subclipse.ui.dialogs.SetCommitPropertiesDialog;
import org.tigris.subversion.subclipse.ui.dialogs.ShowRevisionsDialog;
import org.tigris.subversion.subclipse.ui.internal.Utils;
import org.tigris.subversion.subclipse.ui.operations.BranchTagOperation;
import org.tigris.subversion.subclipse.ui.operations.MergeOperation;
import org.tigris.subversion.subclipse.ui.operations.ReplaceOperation;
import org.tigris.subversion.subclipse.ui.operations.SwitchOperation;
import org.tigris.subversion.subclipse.ui.settings.ProjectProperties;
import org.tigris.subversion.subclipse.ui.svnproperties.SvnRevPropertiesView;
import org.tigris.subversion.subclipse.ui.util.EmptySearchViewerFilter;
import org.tigris.subversion.subclipse.ui.util.LinkList;
import org.tigris.subversion.subclipse.ui.wizards.BranchTagWizard;
import org.tigris.subversion.subclipse.ui.wizards.ClosableWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizard;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardSwitchPage;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNProperty;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNRevisionRange;
import org.tigris.subversion.svnclientadapter.SVNUrl;

/**
 * <code>IHistoryPage</code> for generic history view 
 * 
 * @author Eugene Kuleshov (migration from legacy history view)
 */
public class SVNHistoryPage extends HistoryPage implements IResourceStateChangeListener, KeyListener {

    private SashForm svnHistoryPageControl;
    private SashForm innerSashForm;
    private HistorySearchDialog historySearchDialog;

    HistoryTableProvider historyTableProvider;
    TableViewer tableHistoryViewer;
    StructuredViewer changePathsViewer;
    TextViewer textViewer;

    private boolean showComments;
    private boolean showAffectedPaths;
    private boolean wrapCommentsText;
    boolean shutdown = false;

    private ProjectProperties projectProperties;

    private Composite tableParent;

    private static HistorySearchViewerFilter historySearchViewerFilter;

    // cached for efficiency
    ILogEntry[] entries;
    LogEntryChangePath[] currentLogEntryChangePath;
    ILogEntry lastEntry;
    SVNRevision revisionStart = SVNRevision.HEAD;

    AbstractFetchJob fetchLogEntriesJob = null;
    AbstractFetchJob fetchAllLogEntriesJob = null;
    AbstractFetchJob fetchNextLogEntriesJob = null;
    FetchChangePathJob fetchChangePathJob = null;
    AliasManager tagManager;

    IResource resource;
    ISVNRemoteResource remoteResource;
    ISelection selection;

    private SVNRevisionRange[] revisionRanges;
    private boolean revertEnabled;

    private IAction searchAction;
    private IAction clearSearchAction;
    private IAction getNextAction;
    private IAction getAllAction;
    private IAction toggleStopOnCopyAction;
    private IAction toggleIncludeMergedRevisionsAction;
    private IAction toggleShowComments;
    private IAction toggleWrapCommentsAction;
    private IAction toggleShowAffectedPathsAction;

    private IAction openAction;
    private IAction getContentsAction;
    private IAction updateToRevisionAction;
    private IAction openChangedPathAction;
    private IAction showHistoryAction;
    private IAction showRevisionPropertiesAction;
    private IAction compareAction;
    private IAction showAnnotationAction;
    private IAction exportAction;
    private IAction createTagFromRevisionChangedPathAction;
    private IAction copyChangedPathAction;

    //  private IAction switchChangedPathAction;
    //  private IAction revertChangesChangedPathAction;

    private IAction createTagFromRevisionAction;
    private IAction setCommitPropertiesAction;
    private IAction showRevisionsAction;
    private IAction revertChangesAction;
    private IAction refreshAction;
    private IAction switchAction;
    private GenerateChangeLogAction generateChangeLogAction;

    private ToggleAffectedPathsOptionAction[] toggleAffectedPathsLayoutActions;
    private ToggleAffectedPathsOptionAction[] toggleAffectedPathsModeActions;

    private TextViewerAction copyAction;
    private TextViewerAction selectAllAction;

    private LinkList linkList;
    private Cursor handCursor;
    private Cursor busyCursor;

    private IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore();

    private boolean includeTags = true;
    private boolean includeBugs = false;

    public SVNHistoryPage(Object object) {
        SVNProviderPlugin.addResourceStateChangeListener(this);
    }

    public void dispose() {
        super.dispose();

        SVNProviderPlugin.removeResourceStateChangeListener(this);

        if (busyCursor != null) {
            busyCursor.dispose();
        }
        if (handCursor != null) {
            handCursor.dispose();
        }
    }

    public Control getControl() {
        return svnHistoryPageControl;
    }

    public void setFocus() {
        if (tableHistoryViewer != null) {
            tableHistoryViewer.getControl().setFocus();
        }
    }

    public String getDescription() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getName() {
        return remoteResource == null ? null
                : remoteResource.getRepositoryRelativePath() + Policy.bind("SVNHistoryPage.0") //$NON-NLS-1$
                        + remoteResource.getRepository();
    }

    public boolean isValidInput(Object object) {
        if (object instanceof IResource) {
            RepositoryProvider provider = RepositoryProvider.getProvider(((IResource) object).getProject());
            return provider instanceof SVNTeamProvider;
        } else if (object instanceof ISVNRemoteResource) {
            return true;
        }

        // TODO
        // } else if(object instanceof CVSFileRevision) {
        // return true;
        // } else if(object instanceof CVSLocalFileRevision) {
        // return true;

        return false;
    }

    public void refresh() {
        entries = null;
        lastEntry = null;
        revisionStart = SVNRevision.HEAD;
        // show a Busy Cursor during refresh
        BusyIndicator.showWhile(tableHistoryViewer.getTable().getDisplay(), new Runnable() {
            public void run() {
                if (resource != null) {
                    try {
                        remoteResource = SVNWorkspaceRoot.getBaseResourceFor(resource);
                        historyTableProvider.setRemoteResource(remoteResource);
                        projectProperties = ProjectProperties.getProjectProperties(resource);
                        historyTableProvider.setProjectProperties(projectProperties);
                    } catch (SVNException e) {
                    }
                }
                if (tableHistoryViewer.getInput() == null)
                    tableHistoryViewer.setInput(remoteResource);
                tableHistoryViewer.refresh();
                tableHistoryViewer.resetFilters();
                Object firstElement = tableHistoryViewer.getElementAt(0);
                if (firstElement != null) {
                    tableHistoryViewer.reveal(firstElement);
                }
                getClearSearchAction().setEnabled(false);
            }
        });
    }

    public Object getAdapter(Class adapter) {
        // TODO Auto-generated method stub
        return null;
    }

    public boolean inputSet() {
        Object input = getInput();

        if (input instanceof IResource) {
            IResource res = (IResource) input;

            RepositoryProvider teamProvider = RepositoryProvider.getProvider(res.getProject(),
                    SVNProviderPlugin.getTypeId());
            if (teamProvider != null) {
                try {
                    ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(res);
                    LocalResourceStatus localResourceStatus = (localResource != null) ? localResource.getStatus()
                            : null;
                    if (localResource != null && localResourceStatus.isManaged()
                            && (!localResourceStatus.isAdded() || localResourceStatus.isCopied())) {
                        this.resource = res;
                        this.remoteResource = localResource.getBaseResource();

                        this.projectProperties = ProjectProperties.getProjectProperties(res);
                        boolean includeBugs = projectProperties != null;
                        boolean includeTags = tagsPropertySet(res);
                        if (includeTags != this.includeTags || this.includeBugs != includeBugs) {
                            this.includeTags = includeTags;
                            this.includeBugs = includeBugs;
                            refreshTable();
                        }
                        this.historyTableProvider.setRemoteResource(this.remoteResource);
                        this.historyTableProvider.setProjectProperties(this.projectProperties);
                        if (historySearchViewerFilter != null) {

                            //               HistorySearchViewerFilter[] filters = { historySearchViewerFilter };            
                            //               this.tableHistoryViewer.setFilters(filters);
                            this.tableHistoryViewer.resetFilters();
                            this.tableHistoryViewer.addFilter(historySearchViewerFilter);

                            historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
                            historySearchDialog.setSearchAll(false);
                            historySearchDialog.setStartRevision(historySearchViewerFilter.getStartRevision());
                            historySearchDialog.setEndRevision(historySearchViewerFilter.getEndRevision());
                            historySearchViewerFilter = null;
                            getClearSearchAction().setEnabled(true);
                        } else {
                            this.tableHistoryViewer.resetFilters();
                            getClearSearchAction().setEnabled(false);
                        }
                        this.tableHistoryViewer.setInput(this.remoteResource);

                        // setContentDescription(Policy.bind("HistoryView.titleWithArgument",
                        // baseResource.getName())); //$NON-NLS-1$
                        // setTitleToolTip(baseResource.getRepositoryRelativePath());
                        return true;
                    }
                } catch (TeamException e) {
                    SVNUIPlugin.openError(getSite().getShell(), null, null, e);
                }
            }

        } else if (input instanceof ISVNRemoteResource) {
            this.resource = null;
            this.remoteResource = (ISVNRemoteResource) input;

            boolean includeTags = tagsPropertySet(remoteResource);
            if (includeTags != this.includeTags) {
                this.includeTags = includeTags;
                refreshTable();
            }

            try {
                this.projectProperties = ProjectProperties.getProjectProperties(this.remoteResource);
            } catch (SVNException e) {
                if (!e.operationInterrupted()) {
                    SVNUIPlugin.openError(getSite().getShell(), null, null, e);
                }
            }
            boolean includeBugs = projectProperties != null;
            if (includeTags != this.includeTags || this.includeBugs != includeBugs) {
                this.includeTags = includeTags;
                this.includeBugs = includeBugs;
                refreshTable();
            }
            this.historyTableProvider.setRemoteResource(this.remoteResource);
            this.historyTableProvider.setProjectProperties(this.projectProperties);
            if (historySearchViewerFilter != null) {
                //      HistorySearchViewerFilter[] filters = { historySearchViewerFilter };            
                //      this.tableHistoryViewer.setFilters(filters);
                this.tableHistoryViewer.resetFilters();
                this.tableHistoryViewer.addFilter(historySearchViewerFilter);
                historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
                historySearchDialog.setSearchAll(false);
                historySearchDialog.setStartRevision(historySearchViewerFilter.getStartRevision());
                historySearchDialog.setEndRevision(historySearchViewerFilter.getEndRevision());
                historySearchViewerFilter = null;
                getClearSearchAction().setEnabled(true);
            } else {
                this.tableHistoryViewer.resetFilters();
                getClearSearchAction().setEnabled(false);
            }
            this.tableHistoryViewer.setInput(this.remoteResource);
            // setContentDescription(Policy.bind("HistoryView.titleWithArgument",
            // remoteResource.getName())); //$NON-NLS-1$
            // setTitleToolTip(remoteResource.getRepositoryRelativePath());
            return true;

        }

        return false;
    }

    private boolean tagsPropertySet(IResource resource) {
        if (resource == null)
            return false;
        ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
        try {
            if (svnResource.isManaged()) {
                ISVNProperty property = null;
                property = svnResource.getSvnProperty("subclipse:tags"); //$NON-NLS-1$
                if (property != null && property.getValue() != null)
                    return true;
            }
        } catch (SVNException e) {
        }

        IResource checkResource = resource;
        while (checkResource.getParent() != null) {
            checkResource = checkResource.getParent();
            if (checkResource.getParent() == null)
                return false;
            svnResource = SVNWorkspaceRoot.getSVNResourceFor(checkResource);
            try {
                if (svnResource.isManaged()) {
                    ISVNProperty property = null;
                    property = svnResource.getSvnProperty("subclipse:tags"); //$NON-NLS-1$
                    if (property != null && property.getValue() != null)
                        return true;
                }
            } catch (SVNException e) {
            }
        }

        return false;
    }

    private boolean tagsPropertySet(ISVNRemoteResource resource) {
        ISVNClientAdapter client = null;
        try {
            client = SVNProviderPlugin.getPlugin().getSVNClient();
            ISVNProperty property = null;
            SVNProviderPlugin.disableConsoleLogging();
            property = client.propertyGet(resource.getUrl(), "subclipse:tags"); //$NON-NLS-1$
            if (property != null && property.getValue() != null) {
                SVNProviderPlugin.enableConsoleLogging();
                return true;
            }
            ISVNRemoteResource checkResource = resource;
            while (checkResource.getParent() != null) {
                checkResource = checkResource.getParent();
                property = client.propertyGet(checkResource.getUrl(), "subclipse:tags"); //$NON-NLS-1$
                if (property != null && property.getValue() != null) {
                    SVNProviderPlugin.enableConsoleLogging();
                    return true;
                }
            }
        } catch (Exception e) {
            SVNProviderPlugin.enableConsoleLogging();
        } finally {
            SVNProviderPlugin.getPlugin().getSVNClientManager().returnSVNClient(client);
        }
        return false;
    }

    public void createControl(Composite parent) {
        this.busyCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_WAIT);
        this.handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);

        this.showComments = store.getBoolean(ISVNUIConstants.PREF_SHOW_COMMENTS);
        this.wrapCommentsText = store.getBoolean(ISVNUIConstants.PREF_WRAP_COMMENTS);
        this.showAffectedPaths = store.getBoolean(ISVNUIConstants.PREF_SHOW_PATHS);

        this.svnHistoryPageControl = new SashForm(parent, SWT.VERTICAL);
        this.svnHistoryPageControl.setLayoutData(new GridData(GridData.FILL_BOTH));

        this.toggleAffectedPathsModeActions = new ToggleAffectedPathsOptionAction[] {
                new ToggleAffectedPathsOptionAction(this, "HistoryView.affectedPathsTableLayout", //$NON-NLS-1$
                        ISVNUIConstants.IMG_AFFECTED_PATHS_TABLE_MODE, ISVNUIConstants.PREF_AFFECTED_PATHS_MODE,
                        ISVNUIConstants.MODE_FLAT),
                new ToggleAffectedPathsOptionAction(this, "HistoryView.affectedPathsFlatLayout", //$NON-NLS-1$
                        ISVNUIConstants.IMG_AFFECTED_PATHS_FLAT_MODE, ISVNUIConstants.PREF_AFFECTED_PATHS_MODE,
                        ISVNUIConstants.MODE_FLAT2),
                new ToggleAffectedPathsOptionAction(this, "HistoryView.affectedPathsCompressedLayout", //$NON-NLS-1$
                        ISVNUIConstants.IMG_AFFECTED_PATHS_COMPRESSED_MODE,
                        ISVNUIConstants.PREF_AFFECTED_PATHS_MODE, ISVNUIConstants.MODE_COMPRESSED), };

        this.toggleAffectedPathsLayoutActions = new ToggleAffectedPathsOptionAction[] {
                new ToggleAffectedPathsOptionAction(this, "HistoryView.affectedPathsHorizontalLayout", //$NON-NLS-1$
                        ISVNUIConstants.IMG_AFFECTED_PATHS_HORIZONTAL_LAYOUT,
                        ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT, ISVNUIConstants.LAYOUT_HORIZONTAL),
                new ToggleAffectedPathsOptionAction(this, "HistoryView.affectedPathsVerticalLayout", //$NON-NLS-1$
                        ISVNUIConstants.IMG_AFFECTED_PATHS_VERTICAL_LAYOUT,
                        ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT, ISVNUIConstants.LAYOUT_VERTICAL), };

        createTableHistory(svnHistoryPageControl);
        createAffectedPathsViewer();
        contributeActions();

        svnHistoryPageControl.setWeights(new int[] { 70, 30 });

        // set F1 help
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(svnHistoryPageControl,
        // IHelpContextIds.RESOURCE_HISTORY_VIEW);
        // initDragAndDrop();

        // add listener for editor page activation - this is to support editor
        // linking
        // getSite().getPage().addPartListener(partListener);
        // getSite().getPage().addPartListener(partListener2);
    }

    protected void createTableHistory(Composite parent) {
        boolean redraw = false;
        if (tableParent == null) {
            tableParent = new Composite(parent, SWT.NONE);
            GridLayout layout = new GridLayout();
            layout.numColumns = 1;
            layout.marginHeight = 0;
            layout.marginWidth = 0;
            tableParent.setLayout(layout);
            GridData gridData = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
            gridData.horizontalIndent = 0;
            gridData.verticalIndent = 0;
            tableParent.setLayoutData(gridData);
        } else {
            Control[] children = tableParent.getChildren();
            for (int i = 0; i < children.length; i++) {
                children[i].dispose();
            }
            redraw = true;
        }
        this.historyTableProvider = new HistoryTableProvider(
                SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION, "SVNHistoryPage"); //$NON-NLS-1$
        this.historyTableProvider.setRemoteResource(remoteResource);
        this.historyTableProvider.setProjectProperties(this.projectProperties);
        historyTableProvider
                .setIncludeMergeRevisions(store.getBoolean(ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS));
        historyTableProvider.setIncludeTags(includeTags);
        historyTableProvider.setIncludeBugs(includeBugs);
        this.tableHistoryViewer = historyTableProvider.createTable(tableParent);
        if (redraw) {
            tableParent.layout(true);
            tableParent.redraw();
        }
        this.tableHistoryViewer.getTable().addKeyListener(this);
        // set the content provider for the table
        this.tableHistoryViewer.setContentProvider(new IStructuredContentProvider() {

            public Object[] getElements(Object inputElement) {
                // Short-circuit to optimize
                if (entries != null)
                    return entries;

                if (!(inputElement instanceof ISVNRemoteResource))
                    return null;
                final ISVNRemoteResource remoteResource = (ISVNRemoteResource) inputElement;

                int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);

                // If we are filtering by revision range, override entries to fetch.
                if (historySearchDialog != null && !historySearchDialog.getSearchAllLogs()) {
                    if (historySearchDialog.getStartRevision() != null
                            || historySearchDialog.getEndRevision() != null) {
                        if (getClearSearchAction().isEnabled())
                            entriesToFetch = 0;
                    }
                }

                if (entriesToFetch > 0)
                    fetchLogEntriesJob = new FetchLogEntriesJob();
                else
                    fetchLogEntriesJob = new FetchAllLogEntriesJob();
                if (fetchLogEntriesJob.getState() != Job.NONE) {
                    fetchLogEntriesJob.cancel();
                    try {
                        fetchLogEntriesJob.join();
                    } catch (InterruptedException e) {
                        SVNUIPlugin.log(new SVNException(
                                Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$
                    }
                }
                fetchLogEntriesJob.setRemoteFile(remoteResource);
                IWorkbenchWindow window = SVNUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow();
                if (window.getActivePage() != null && window.getActivePage().getActivePart() != null
                        && window.getActivePage().getActivePart().getSite() != null) {
                    Utils.schedule(fetchLogEntriesJob, window.getActivePage().getActivePart().getSite());
                } else {
                    Utils.schedule(fetchLogEntriesJob, null);
                }

                return new Object[0];
            }

            public void dispose() {
            }

            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
                entries = null;
                lastEntry = null;
                revisionStart = SVNRevision.HEAD;
            }
        });

        // set the selectionchanged listener for the table
        // updates the comments and affected paths when selection changes
        this.tableHistoryViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            private ILogEntry currentLogEntry;
            private int currentSelectionSize = -1;

            public void selectionChanged(SelectionChangedEvent event) {
                ISelection selection = event.getSelection();
                ILogEntry logEntry = getLogEntry((IStructuredSelection) selection);
                if (logEntry != currentLogEntry
                        || ((IStructuredSelection) selection).size() != currentSelectionSize) {
                    this.currentLogEntry = logEntry;
                    this.currentSelectionSize = ((IStructuredSelection) selection).size();
                    updatePanels(selection);
                }

                SVNHistoryPage.this.selection = selection;
            }
        });

        // Double click open action
        this.tableHistoryViewer.getTable().addListener(SWT.DefaultSelection, new Listener() {
            public void handleEvent(Event e) {
                getOpenRemoteFileAction().run();
            }
        });

        // Contribute actions to popup menu for the table
        {
            MenuManager menuMgr = new MenuManager();
            Menu menu = menuMgr.createContextMenu(tableHistoryViewer.getTable());
            menuMgr.addMenuListener(new IMenuListener() {
                public void menuAboutToShow(IMenuManager menuMgr) {
                    fillTableMenu(menuMgr);
                }
            });
            menuMgr.setRemoveAllWhenShown(true);
            tableHistoryViewer.getTable().setMenu(menu);
            getHistoryPageSite().getPart().getSite().registerContextMenu(menuMgr, tableHistoryViewer);
        }
        if (redraw) {
            parent.layout(true);
            parent.redraw();
        }
    }

    public void refreshTable() {
        createTableHistory(svnHistoryPageControl);
    }

    private void fillChangePathsMenu(IMenuManager manager) {
        //
        // Commented out Get Contents, Revert and Switch options until when/if
        // they can be fixed.  Problem is that we need a way to get the local
        // resource from the LogEntryChangePath.
        //
        IStructuredSelection sel = (IStructuredSelection) changePathsViewer.getSelection();
        if (sel.size() == 1) {
            if (sel.getFirstElement() instanceof LogEntryChangePath) {
                //           manager.add(getGetContentsAction());
            }
            manager.add(getCreateTagFromRevisionChangedPathAction());
        }
        //     manager.add(getRevertChangesChangedPathAction());
        //     manager.add(getSwitchChangedPathAction());
        manager.add(new Separator("exportImportGroup")); //$NON-NLS-1$
        if (sel.size() == 1) {
            if (sel.getFirstElement() instanceof LogEntryChangePath) {
                manager.add(getExportAction());
                if (((LogEntryChangePath) sel.getFirstElement()).getAction() == 'D') {
                    manager.add(getCopyChangedPathAction());
                }
            }
        }
        manager.add(new Separator("openGroup")); //$NON-NLS-1$
        if (sel.size() == 1) {
            if (sel.getFirstElement() instanceof LogEntryChangePath) {
                manager.add(getShowAnnotationAction());
            }
            manager.add(getCompareAction());
        }
        if (sel.getFirstElement() instanceof LogEntryChangePath) {
            manager.add(getOpenChangedPathAction());
        }
        if (sel.size() == 1)
            manager.add(getShowHistoryAction());
    }

    private void fillTableMenu(IMenuManager manager) {
        // file actions go first (view file)
        manager.add(new Separator(IWorkbenchActionConstants.GROUP_FILE));
        // Add the "Add to Workspace" action if 1 revision is selected.
        ISelection sel = tableHistoryViewer.getSelection();
        if (!sel.isEmpty()) {
            if (sel instanceof IStructuredSelection) {
                if (((IStructuredSelection) sel).size() == 1) {
                    if (resource != null && resource instanceof IFile) {
                        manager.add(getGetContentsAction());
                        manager.add(getUpdateToRevisionAction());
                    }
                    //          manager.add(getShowDifferencesAsUnifiedDiffAction());
                    // if (resource != null) {
                    manager.add(getCreateTagFromRevisionAction());
                    // }
                    manager.add(getSetCommitPropertiesAction());

                    manager.add(getShowRevisionPropertiesAction());

                    ILogEntry logEntry = (ILogEntry) ((IStructuredSelection) sel).getFirstElement();
                    if (logEntry.getNumberOfChildren() > 0)
                        manager.add(getShowRevisionsAction());
                }
                if (resource != null) {
                    manager.add(getRevertChangesAction());
                    if (((IStructuredSelection) sel).size() == 1)
                        manager.add(getSwitchAction());
                }

                manager.add(new Separator("exportImportGroup")); //$NON-NLS-1$
                getGenerateChangeLogAction()
                        .setEnabled(!store.getBoolean(ISVNUIConstants.PREF_FETCH_CHANGE_PATH_ON_DEMAND));
                manager.add(getGenerateChangeLogAction());
            }
        }
        manager.add(new Separator("additions")); //$NON-NLS-1$
        manager.add(getRefreshAction());
        manager.add(new Separator("additions-end")); //$NON-NLS-1$
    }

    public void createAffectedPathsViewer() {
        int[] weights = null;
        weights = svnHistoryPageControl.getWeights();
        if (innerSashForm != null) {
            innerSashForm.dispose();
        }
        if (changePathsViewer != null) {
            changePathsViewer.getControl().dispose();
        }

        int mode = store.getInt(ISVNUIConstants.PREF_AFFECTED_PATHS_MODE);
        int layout = store.getInt(ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT);

        if (layout == ISVNUIConstants.LAYOUT_HORIZONTAL) {
            innerSashForm = new SashForm(svnHistoryPageControl, SWT.HORIZONTAL);
        } else {
            innerSashForm = new SashForm(svnHistoryPageControl, SWT.VERTICAL);
            createText(innerSashForm);
        }

        switch (mode) {
        case ISVNUIConstants.MODE_COMPRESSED:
            changePathsViewer = new ChangePathsTreeViewer(innerSashForm, this);
            break;
        case ISVNUIConstants.MODE_FLAT2:
            changePathsViewer = new ChangePathsFlatViewer(innerSashForm, this);
            break;
        default:
            changePathsViewer = new ChangePathsTableProvider(innerSashForm, this);
            break;
        }

        changePathsViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                SVNHistoryPage.this.selection = changePathsViewer.getSelection();
            }
        });

        changePathsViewer.getControl().addListener(SWT.DefaultSelection, new Listener() {
            public void handleEvent(Event e) {
                getOpenChangedPathAction().run();
            }
        });

        // Contribute actions to changed paths pane
        MenuManager menuMgr = new MenuManager();
        Menu menu = menuMgr.createContextMenu(changePathsViewer.getControl());
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager menuMgr) {
                fillChangePathsMenu(menuMgr);
            }
        });
        menuMgr.setRemoveAllWhenShown(true);
        changePathsViewer.getControl().setMenu(menu);

        if (layout == ISVNUIConstants.LAYOUT_HORIZONTAL) {
            createText(innerSashForm);
        }

        setViewerVisibility();

        innerSashForm.layout();
        if (weights != null && weights.length == 2) {
            svnHistoryPageControl.setWeights(weights);
        }
        svnHistoryPageControl.layout();

        updatePanels(tableHistoryViewer.getSelection());
    }

    /**
     * Create the TextViewer for the logEntry comments
     */
    protected void createText(Composite parent) {
        // this.textViewer = new TextViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
        SourceViewer result = new SourceViewer(parent, null, null, true,
                SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
        result.getTextWidget().setIndent(2);
        result.configure(new TextSourceViewerConfiguration(EditorsUI.getPreferenceStore()) {
            public Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
                return Collections.singletonMap("org.eclipse.ui.DefaultTextEditor.Subclipse", //$NON-NLS-1$
                        new IAdaptable() {
                            public Object getAdapter(Class adapter) {
                                if (adapter == IResource.class && getInput() instanceof IResource) {
                                    return getInput();
                                } else if (adapter == ISVNRemoteResource.class
                                        && getInput() instanceof ISVNRemoteResource) {
                                    return getInput();
                                }
                                return Platform.getAdapterManager().getAdapter(SVNHistoryPage.this, adapter);
                            }
                        });
            }

            public int getHyperlinkStateMask(ISourceViewer sourceViewer) {
                return SWT.NONE;
            }

            public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
                IHyperlinkDetector[] detectors = super.getHyperlinkDetectors(sourceViewer);
                IHyperlinkDetector[] newDetectors;
                if (detectors == null) {
                    newDetectors = new IHyperlinkDetector[1];
                } else {
                    newDetectors = new IHyperlinkDetector[detectors.length + 1];
                    System.arraycopy(detectors, 0, newDetectors, 0, detectors.length);
                }

                newDetectors[newDetectors.length - 1] = new IHyperlinkDetector() {
                    public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region,
                            boolean canShowMultipleHyperlinks) {
                        if (linkList == null || !linkList.isLinkAt(region.getOffset())) {
                            return null;
                        }

                        final String linkUrl = linkList.getLinkAt(region.getOffset());
                        final int[] linkRange = linkList.getLinkRange(region.getOffset());

                        return new IHyperlink[] { new IHyperlink() {
                            public IRegion getHyperlinkRegion() {
                                return new Region(linkRange[0], linkRange[1]);
                            }

                            public void open() {
                                try {
                                    URL url = new URL(linkUrl);
                                    PlatformUI.getWorkbench().getBrowserSupport().createBrowser("Subclipse") //$NON-NLS-1$
                                            .openURL(url);
                                } catch (Exception e1) {
                                    Program.launch(linkUrl);
                                }
                            }

                            public String getHyperlinkText() {
                                return null;
                            }

                            public String getTypeLabel() {
                                return null;
                            }
                        } };
                    }
                };

                return newDetectors;
            }
        });

        this.textViewer = result;

        this.textViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                copyAction.update();
            }
        });

        Font font = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry()
                .get(ISVNUIConstants.SVN_COMMENT_FONT);
        if (font != null) {
            this.textViewer.getTextWidget().setFont(font);
        }

        // Create actions for the text editor (copy and select all)
        copyAction = new TextViewerAction(this.textViewer, ITextOperationTarget.COPY);
        copyAction.setText(Policy.bind("HistoryView.copy")); //$NON-NLS-1$

        selectAllAction = new TextViewerAction(this.textViewer, ITextOperationTarget.SELECT_ALL);
        selectAllAction.setText(Policy.bind("HistoryView.selectAll")); //$NON-NLS-1$

        IHistoryPageSite parentSite = getHistoryPageSite();
        IPageSite pageSite = parentSite.getWorkbenchPageSite();
        IActionBars actionBars = pageSite.getActionBars();

        actionBars.setGlobalActionHandler(ITextEditorActionConstants.COPY, copyAction);
        actionBars.setGlobalActionHandler(ITextEditorActionConstants.SELECT_ALL, selectAllAction);
        actionBars.updateActionBars();

        // Contribute actions to popup menu for the comments area
        {
            MenuManager menuMgr = new MenuManager();
            menuMgr.setRemoveAllWhenShown(true);
            menuMgr.addMenuListener(new IMenuListener() {
                public void menuAboutToShow(IMenuManager menuMgr) {
                    menuMgr.add(copyAction);
                    menuMgr.add(selectAllAction);
                }
            });

            StyledText text = this.textViewer.getTextWidget();
            Menu menu = menuMgr.createContextMenu(text);
            text.setMenu(menu);
        }
    }

    private void contributeActions() {
        toggleShowComments = new Action(Policy.bind("HistoryView.showComments"), //$NON-NLS-1$
                SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_COMMENTS)) {
            public void run() {
                showComments = isChecked();
                setViewerVisibility();
                store.setValue(ISVNUIConstants.PREF_SHOW_COMMENTS, showComments);
            }
        };
        toggleShowComments.setChecked(showComments);
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextAction,
        // IHelpContextIds.SHOW_COMMENT_IN_HISTORY_ACTION);

        // Toggle wrap comments action
        toggleWrapCommentsAction = new Action(Policy.bind("HistoryView.wrapComments")) { //$NON-NLS-1$
            public void run() {
                wrapCommentsText = isChecked();
                setViewerVisibility();
                store.setValue(ISVNUIConstants.PREF_WRAP_COMMENTS, wrapCommentsText);
            }
        };
        toggleWrapCommentsAction.setChecked(wrapCommentsText);
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextWrapAction,
        // IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION);

        // Toggle path visible action
        toggleShowAffectedPathsAction = new Action(Policy.bind("HistoryView.showAffectedPaths"), //$NON-NLS-1$
                SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_AFFECTED_PATHS_FLAT_MODE)) {
            public void run() {
                showAffectedPaths = isChecked();
                setViewerVisibility();
                store.setValue(ISVNUIConstants.PREF_SHOW_PATHS, showAffectedPaths);
            }
        };
        toggleShowAffectedPathsAction.setChecked(showAffectedPaths);
        // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleListAction,
        // IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION);

        // Toggle stop on copy action
        toggleStopOnCopyAction = new Action(Policy.bind("HistoryView.stopOnCopy")) { //$NON-NLS-1$
            public void run() {
                refresh();
                SVNUIPlugin.getPlugin().getPreferenceStore().setValue(ISVNUIConstants.PREF_STOP_ON_COPY,
                        toggleStopOnCopyAction.isChecked());
            }
        };
        toggleStopOnCopyAction.setChecked(store.getBoolean(ISVNUIConstants.PREF_STOP_ON_COPY));

        // Toggle include merged revisions action
        toggleIncludeMergedRevisionsAction = new Action(Policy.bind("HistoryView.includeMergedRevisions")) { //$NON-NLS-1$
            public void run() {
                store.setValue(ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS,
                        toggleIncludeMergedRevisionsAction.isChecked());
                refreshTable();
                refresh();
            }
        };
        toggleIncludeMergedRevisionsAction
                .setChecked(store.getBoolean(ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS));

        IHistoryPageSite parentSite = getHistoryPageSite();
        IPageSite pageSite = parentSite.getWorkbenchPageSite();
        IActionBars actionBars = pageSite.getActionBars();

        // Contribute toggle text visible to the toolbar drop-down
        IMenuManager actionBarsMenu = actionBars.getMenuManager();
        actionBarsMenu.add(getGetNextAction());
        actionBarsMenu.add(getGetAllAction());
        actionBarsMenu.add(toggleStopOnCopyAction);
        actionBarsMenu.add(toggleIncludeMergedRevisionsAction);
        actionBarsMenu.add(new Separator());
        actionBarsMenu.add(toggleWrapCommentsAction);
        actionBarsMenu.add(new Separator());
        actionBarsMenu.add(toggleShowComments);
        actionBarsMenu.add(toggleShowAffectedPathsAction);
        actionBarsMenu.add(new Separator());
        for (int i = 0; i < toggleAffectedPathsModeActions.length; i++) {
            actionBarsMenu.add(toggleAffectedPathsModeActions[i]);
        }
        actionBarsMenu.add(new Separator());
        for (int i = 0; i < toggleAffectedPathsLayoutActions.length; i++) {
            actionBarsMenu.add(toggleAffectedPathsLayoutActions[i]);
        }

        // Create the local tool bar
        IToolBarManager tbm = actionBars.getToolBarManager();
        // tbm.add(getRefreshAction());
        tbm.add(new Separator());
        tbm.add(getSearchAction());
        tbm.add(getClearSearchAction());
        tbm.add(new Separator());
        tbm.add(toggleShowComments);
        tbm.add(toggleShowAffectedPathsAction);
        tbm.add(new Separator());
        tbm.add(getGetNextAction());
        tbm.add(getGetAllAction());
        // tbm.add(getLinkWithEditorAction());
        tbm.update(false);

        actionBars.updateActionBars();
    }

    ILogEntry getLogEntry(IStructuredSelection ss) {
        if (ss.getFirstElement() instanceof LogEntryChangePath) {
            return ((LogEntryChangePath) ss.getFirstElement()).getLogEntry();
        }
        return (ILogEntry) ss.getFirstElement();
    }

    void updatePanels(ISelection selection) {
        if (selection == null || !(selection instanceof IStructuredSelection)) {
            textViewer.setDocument(new Document("")); //$NON-NLS-1$
            changePathsViewer.setInput(null);
            return;
        }
        IStructuredSelection ss = (IStructuredSelection) selection;
        if (ss.size() != 1) {
            textViewer.setDocument(new Document("")); //$NON-NLS-1$
            changePathsViewer.setInput(null);
            return;
        }
        LogEntry entry = (LogEntry) ss.getFirstElement();
        textViewer.setDocument(new Document(entry.getComment()));
        StyledText text = textViewer.getTextWidget();

        // TODO move this logic into the hyperlink detector created in createText()
        if (projectProperties == null) {
            linkList = ProjectProperties.getUrls(entry.getComment());
        } else {
            linkList = projectProperties.getLinkList(entry.getComment());
        }
        if (linkList != null) {
            int[][] linkRanges = linkList.getLinkRanges();
            // String[] urls = linkList.getUrls();
            for (int i = 0; i < linkRanges.length; i++) {
                text.setStyleRange(new StyleRange(linkRanges[i][0], linkRanges[i][1],
                        JFaceColors.getHyperlinkText(Display.getCurrent()), null));
            }
        }
        if (changePathsViewer instanceof ChangePathsTreeViewer) {
            ((ChangePathsTreeViewer) changePathsViewer).setCurrentLogEntry(entry);
        }
        if (changePathsViewer instanceof ChangePathsFlatViewer) {
            ((ChangePathsFlatViewer) changePathsViewer).setCurrentLogEntry(entry);
        }
        if (changePathsViewer instanceof ChangePathsTableProvider) {
            ((ChangePathsTableProvider) changePathsViewer).setCurrentLogEntry(entry);
        }
        changePathsViewer.setInput(entry);
    }

    void setViewerVisibility() {
        if (showComments && showAffectedPaths) {
            svnHistoryPageControl.setMaximizedControl(null);
            innerSashForm.setMaximizedControl(null);
        } else if (showComments) {
            svnHistoryPageControl.setMaximizedControl(null);
            innerSashForm.setMaximizedControl(textViewer.getTextWidget());
        } else if (showAffectedPaths) {
            svnHistoryPageControl.setMaximizedControl(null);
            innerSashForm.setMaximizedControl(changePathsViewer.getControl());
        } else {
            svnHistoryPageControl.setMaximizedControl(tableParent);
        }
        changePathsViewer.refresh();
        textViewer.getTextWidget().setWordWrap(wrapCommentsText);
    }

    void setCurrentLogEntryChangePath(final LogEntryChangePath[] currentLogEntryChangePath) {
        this.currentLogEntryChangePath = currentLogEntryChangePath;
        if (!shutdown) {
            // Getting the changePaths
            /*
             * final SVNRevision.Number revisionId =
             * remoteResource.getLastChangedRevision();
             */
            getSite().getShell().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    if (currentLogEntryChangePath != null && changePathsViewer != null
                            && !changePathsViewer.getControl().isDisposed()) {
                        // once we got the changePath, we refresh the table
                        changePathsViewer.refresh();
                        // selectRevision(revisionId);
                    }
                }
            });
        }
    }

    /**
     * Select the revision in the receiver.
     */
    public void selectRevision(SVNRevision.Number revision) {
        if (entries == null) {
            return;
        }

        ILogEntry entry = null;
        for (int i = 0; i < entries.length; i++) {
            if (entries[i].getRevision().equals(revision)) {
                entry = entries[i];
                break;
            }
        }

        if (entry != null) {
            IStructuredSelection selection = new StructuredSelection(entry);
            tableHistoryViewer.setSelection(selection, true);
        }
    }

    public void scheduleFetchChangePathJob(ILogEntry logEntry) {
        if (fetchChangePathJob == null) {
            fetchChangePathJob = new FetchChangePathJob();
        }
        if (fetchChangePathJob.getState() != Job.NONE) {
            fetchChangePathJob.cancel();
            try {
                fetchChangePathJob.join();
            } catch (InterruptedException e) {
                SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
                // SVNUIPlugin.log(new
                // SVNException(Policy.bind("HistoryView.errorFetchingEntries",
                // remoteResource.getName()), e)); //$NON-NLS-1$
            }
        }
        fetchChangePathJob.setLogEntry(logEntry);
        Utils.schedule(fetchChangePathJob, getSite());
    }

    public boolean isShowChangePaths() {
        return showAffectedPaths;
    }

    private SVNRevision getSelectedRevision() {
        IStructuredSelection sel = (IStructuredSelection) tableHistoryViewer.getSelection();
        if (sel.getFirstElement() instanceof ILogEntry) {
            return ((ILogEntry) sel.getFirstElement()).getRevision();
        }
        return null;
    }

    private IAction getOpenRemoteFileAction() {
        if (openAction == null) {
            openAction = new Action() {
                public void run() {
                    OpenRemoteFileAction delegate = new OpenRemoteFileAction();
                    delegate.init(this);
                    delegate.selectionChanged(this, tableHistoryViewer.getSelection());
                    if (isEnabled()) {
                        try {
                            // disableEditorActivation = true;
                            delegate.run(this);
                        } finally {
                            // disableEditorActivation = false;
                        }
                    }
                }
            };
        }
        return openAction;
    }

    private boolean isFile() {
        IStructuredSelection sel = (IStructuredSelection) changePathsViewer.getSelection();
        if (sel.size() == 1 && sel.getFirstElement() instanceof LogEntryChangePath) {
            LogEntryChangePath changePath = (LogEntryChangePath) sel.getFirstElement();
            try {
                return changePath.getRemoteResource() instanceof ISVNRemoteFile;
            } catch (SVNException e) {
            }
        }
        return false;
    }

    // open changed Path (double-click)
    private IAction getOpenChangedPathAction() {
        if (openChangedPathAction == null) {
            openChangedPathAction = new Action("Open") { //$NON-NLS-1$
                public void run() {
                    if (!isFile()) {
                        MessageDialog.openError(Display.getDefault().getActiveShell(),
                                Policy.bind("SVNHistoryPage.7"), Policy.bind("SVNHistoryPage.8")); //$NON-NLS-1$ //$NON-NLS-2$
                        return;
                    }
                    OpenRemoteFileAction delegate = new OpenRemoteFileAction();
                    delegate.setUsePegRevision(true);
                    delegate.init(this);
                    delegate.selectionChanged(this, changePathsViewer.getSelection());
                    if (isEnabled()) {
                        try {
                            delegate.run(this);
                        } finally {
                            // disableEditorActivation = false;
                        }
                    }
                }
            };
        }
        //  openChangedPathAction.setEnabled(isFile());
        return openChangedPathAction;

    }

    private IAction getShowHistoryAction() {
        if (showHistoryAction == null) {
            showHistoryAction = new Action("Show History", //$NON-NLS-1$
                    SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_SHOWHISTORY)) {
                public void run() {
                    HistoryAction delegate = new HistoryAction();
                    delegate.selectionChanged(this, changePathsViewer.getSelection());
                    delegate.run(this);
                }
            };
        }
        return showHistoryAction;
    }

    private IAction getCopyChangedPathAction() {
        if (copyChangedPathAction == null) {
            copyChangedPathAction = new Action(Policy.bind("HistoryView.copyChangedPath")) { //$NON-NLS-1$
                public void run() {
                    ContainerSelectionDialog dialog = new ContainerSelectionDialog(
                            Display.getDefault().getActiveShell(), null, false,
                            Policy.bind("CopyAction.selectionLabel")); //$NON-NLS-1$
                    if (dialog.open() == ContainerSelectionDialog.OK) {
                        Object[] result = dialog.getResult();
                        if (result == null || result.length == 0)
                            return;
                        final Path path = (Path) result[0];
                        IProject selectedProject;
                        File target = null;
                        if (path.segmentCount() == 1) {
                            selectedProject = ResourcesPlugin.getWorkspace().getRoot().getProject(path.toString());
                            target = selectedProject.getLocation().toFile();
                        } else {
                            IFile targetFile = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
                            selectedProject = targetFile.getProject();
                            target = targetFile.getLocation().toFile();
                        }
                        final IProject targetProject = selectedProject;
                        final File destPath = target;
                        BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                            public void run() {
                                ISVNClientAdapter client = null;
                                try {
                                    IStructuredSelection sel = (IStructuredSelection) changePathsViewer
                                            .getSelection();
                                    if (sel.getFirstElement() instanceof LogEntryChangePath) {
                                        LogEntryChangePath changePath = (LogEntryChangePath) sel.getFirstElement();
                                        SVNRevision revision = changePath.getRevision();
                                        if (changePath.getAction() == 'D') {
                                            long rev = Long.parseLong(revision.toString());
                                            rev--;
                                            revision = new SVNRevision.Number(rev);
                                        }
                                        client = SVNProviderPlugin.getPlugin().getSVNClient();
                                        client.copy(changePath.getUrl(), destPath, revision, revision, true, false);
                                        targetProject.refreshLocal(IResource.DEPTH_INFINITE,
                                                new NullProgressMonitor());
                                        SVNUIPlugin.getPlugin().getRepositoryManager().resourceCreated(null, null);
                                    }
                                } catch (Exception e) {
                                    MessageDialog.openError(Display.getDefault().getActiveShell(),
                                            Policy.bind("HistoryView.copyError"), e.getMessage()); //$NON-NLS-1$
                                } finally {
                                    SVNProviderPlugin.getPlugin().getSVNClientManager().returnSVNClient(client);
                                }
                            }
                        });
                    }
                }
            };
        }
        return copyChangedPathAction;
    }

    private IAction getExportAction() {
        if (exportAction == null) {
            exportAction = new Action("Export...", //$NON-NLS-1$
                    SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_EXPORT)) {
                public void run() {
                    ExportAction delegate = new ExportAction();
                    delegate.selectionChanged(this, changePathsViewer.getSelection());
                    delegate.run(this);
                }
            };
        }
        return exportAction;
    }

    private IAction getShowAnnotationAction() {
        if (showAnnotationAction == null) {
            showAnnotationAction = new Action("Show Annotation", //$NON-NLS-1$
                    SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_ANNOTATE)) {
                public void run() {
                    if (!isFile()) {
                        MessageDialog.openError(Display.getDefault().getActiveShell(),
                                Policy.bind("SVNHistoryPage.11"), Policy.bind("SVNHistoryPage.12")); //$NON-NLS-1$ //$NON-NLS-2$
                        return;
                    }
                    AnnotationAction delegate = new AnnotationAction();
                    delegate.selectionChanged(this, changePathsViewer.getSelection());
                    delegate.run(this);
                }
            };
        }
        //   showAnnotationAction.setEnabled(isFile());
        return showAnnotationAction;
    }

    private IAction getCompareAction() {
        if (compareAction == null) {
            compareAction = new Action("Compare...") { //$NON-NLS-1$
                public void run() {
                    CompareAction delegate = new CompareAction();
                    delegate.selectionChanged(this, changePathsViewer.getSelection());
                    delegate.run(this);
                }
            };
        }
        return compareAction;
    }

    private IAction getCreateTagFromRevisionChangedPathAction() {
        if (createTagFromRevisionChangedPathAction == null) {
            createTagFromRevisionChangedPathAction = new Action() { //$NON-NLS-1$
                public void run() {
                    SVNRevision selectedRevision = null;
                    ISelection selection = changePathsViewer.getSelection();
                    if (!(selection instanceof IStructuredSelection))
                        return;
                    IStructuredSelection sel = (IStructuredSelection) selection;
                    ISVNRemoteResource remoteResource = null;
                    if (sel.getFirstElement() instanceof LogEntryChangePath) {
                        try {
                            remoteResource = ((LogEntryChangePath) sel.getFirstElement()).getRemoteResource();
                            selectedRevision = remoteResource.getRevision();
                        } catch (SVNException e) {
                        }
                    } else if (sel.getFirstElement() instanceof HistoryFolder) {
                        HistoryFolder historyFolder = (HistoryFolder) sel.getFirstElement();
                        Object[] children = historyFolder.getChildren();
                        if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
                            LogEntryChangePath changePath = (LogEntryChangePath) children[0];
                            try {
                                remoteResource = changePath.getRemoteResource().getRepository()
                                        .getRemoteFolder(historyFolder.getPath());
                                selectedRevision = getSelectedRevision();
                            } catch (SVNException e) {
                            }
                        }
                    }
                    if (remoteResource == null)
                        return;
                    ISVNRemoteResource[] remoteResources = { remoteResource };
                    BranchTagWizard wizard = new BranchTagWizard(remoteResources);
                    wizard.setRevisionNumber(Long.parseLong(selectedRevision.toString()));
                    WizardDialog dialog = new ClosableWizardDialog(getSite().getShell(), wizard);
                    if (dialog.open() == WizardDialog.OK) {
                        final SVNUrl sourceUrl = wizard.getUrl();
                        final SVNUrl destinationUrl = wizard.getToUrl();
                        final String message = wizard.getComment();
                        final SVNRevision revision = wizard.getRevision();
                        final boolean makeParents = wizard.isMakeParents();
                        try {
                            BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                                public void run() {
                                    ISVNClientAdapter client = null;
                                    try {
                                        client = SVNProviderPlugin.getPlugin().getSVNClientManager().getSVNClient();
                                        client.copy(sourceUrl, destinationUrl, message, revision, makeParents);
                                        SVNUIPlugin.getPlugin().getRepositoryManager().resourceCreated(null, null);
                                    } catch (Exception e) {
                                        MessageDialog.openError(getSite().getShell(),
                                                Policy.bind("HistoryView.createTagFromRevision"), e //$NON-NLS-1$
                                                        .getMessage());
                                    } finally {
                                        SVNProviderPlugin.getPlugin().getSVNClientManager().returnSVNClient(client);
                                    }
                                }
                            });
                        } catch (Exception e) {
                            MessageDialog.openError(getSite().getShell(),
                                    Policy.bind("HistoryView.createTagFromRevision"), e //$NON-NLS-1$
                                            .getMessage());
                        }
                    }
                    //           SvnWizardBranchTagPage branchTagPage = new SvnWizardBranchTagPage(remoteResource);
                    //            branchTagPage.setRevisionNumber(Long.parseLong(selectedRevision.toString()));
                    //           SvnWizard wizard = new SvnWizard(branchTagPage);
                    //            SvnWizardDialog dialog = new SvnWizardDialog(getSite().getShell(), wizard);
                    //            wizard.setParentDialog(dialog); 
                    //            if (!(dialog.open() == SvnWizardDialog.OK)) return;
                    //            final SVNUrl sourceUrl = branchTagPage.getUrl();
                    //            final SVNUrl destinationUrl = branchTagPage.getToUrl();
                    //            final String message = branchTagPage.getComment();
                    //            final SVNRevision revision = branchTagPage.getRevision();
                    //            final boolean makeParents = branchTagPage.isMakeParents();
                    //            try {
                    //                BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                    //                    public void run() {
                    //                      try {
                    //                        ISVNClientAdapter client = SVNProviderPlugin.getPlugin().getSVNClientManager().createSVNClient();
                    //                        client.copy(sourceUrl, destinationUrl, message, revision, makeParents);
                    //                      } catch(Exception e) {
                    //                        MessageDialog.openError(getSite().getShell(), Policy.bind("HistoryView.createTagFromRevision"), e
                    //                            .getMessage());
                    //                      }
                    //                    }
                    //                  });
                    //            } catch(Exception e) {
                    //              MessageDialog.openError(getSite().getShell(), Policy.bind("HistoryView.createTagFromRevision"), e
                    //                  .getMessage());
                    //            }
                }
            };
        }

        ISelection selection = changePathsViewer.getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection sel = (IStructuredSelection) selection;
            SVNRevision selectedRevision = null;
            if (sel.size() == 1) {
                //        ISVNRemoteResource remoteResource = null;
                if (sel.getFirstElement() instanceof LogEntryChangePath
                        && ((LogEntryChangePath) sel.getFirstElement()).getAction() != 'D') {
                    //           try {
                    //            remoteResource = ((LogEntryChangePath)sel.getFirstElement()).getRemoteResource();
                    selectedRevision = ((LogEntryChangePath) sel.getFirstElement()).getRevision();
                    //            selectedRevision = remoteResource.getRevision();
                    //         } catch (SVNException e) {}
                } else if (sel.getFirstElement() instanceof HistoryFolder) {
                    HistoryFolder historyFolder = (HistoryFolder) sel.getFirstElement();
                    Object[] children = historyFolder.getChildren();
                    if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
                        selectedRevision = getSelectedRevision();
                    }
                }
                createTagFromRevisionChangedPathAction.setEnabled(selectedRevision != null);
                if (selectedRevision == null) {
                    createTagFromRevisionChangedPathAction
                            .setText(Policy.bind("HistoryView.createTagFromRevision", "" //$NON-NLS-1$ //$NON-NLS-2$
                                    + ((LogEntryChangePath) sel.getFirstElement()).getRevision()));
                } else {
                    createTagFromRevisionChangedPathAction
                            .setText(Policy.bind("HistoryView.createTagFromRevision", "" //$NON-NLS-1$ //$NON-NLS-2$
                                    + selectedRevision));
                }
            }
        }
        createTagFromRevisionChangedPathAction
                .setImageDescriptor(SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_BRANCHTAG));

        return createTagFromRevisionChangedPathAction;
    }

    class AnnotationAction extends ShowAnnotationAction {
        public IStructuredSelection fSelection;

        public AnnotationAction() {
            super();
        }

        protected ISVNRemoteFile getSingleSelectedSVNRemoteFile() {
            ISVNRemoteResource remoteResource = null;
            if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
                try {
                    remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
                    if (remoteResource instanceof RemoteResource) {
                        ((RemoteResource) remoteResource)
                                .setPegRevision(((LogEntryChangePath) fSelection.getFirstElement()).getRevision());
                    }
                } catch (SVNException e) {
                }
            }
            return (ISVNRemoteFile) remoteResource;
        }

        protected boolean isEnabled() {
            return true;
        }

        public void selectionChanged(IAction action, ISelection sel) {
            if (sel instanceof IStructuredSelection) {
                fSelection = (IStructuredSelection) sel;
            }
        }
    }

    class HistoryAction extends ShowHistoryAction {
        public IStructuredSelection fSelection;

        public HistoryAction() {
            super();
        }

        protected ISVNRemoteResource[] getSelectedRemoteResources() {
            ISVNRemoteResource remoteResource = null;
            if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
                try {
                    remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
                } catch (SVNException e) {
                }
            } else if (fSelection.getFirstElement() instanceof HistoryFolder) {
                HistoryFolder historyFolder = (HistoryFolder) fSelection.getFirstElement();
                Object[] children = historyFolder.getChildren();
                if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
                    LogEntryChangePath changePath = (LogEntryChangePath) children[0];
                    try {
                        remoteResource = changePath.getRemoteResource().getRepository()
                                .getRemoteFolder(historyFolder.getPath());
                    } catch (SVNException e) {
                    }
                }
            }
            if (remoteResource != null) {
                ISVNRemoteResource[] selectedResource = { remoteResource };
                return selectedResource;
            }
            return new ISVNRemoteResource[0];
        }

        protected boolean isEnabled() {
            return true;
        }

        public void selectionChanged(IAction action, ISelection sel) {
            if (sel instanceof IStructuredSelection) {
                fSelection = (IStructuredSelection) sel;
            }
        }
    }

    class ExportAction extends ExportRemoteFolderAction {
        public IStructuredSelection fSelection;

        public ExportAction() {
            super();
        }

        protected ISVNRemoteResource[] getSelectedRemoteResources() {
            ISVNRemoteResource remoteResource = null;
            if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
                try {
                    remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
                } catch (SVNException e) {
                }
            }
            if (remoteResource != null) {
                ISVNRemoteResource[] selectedResource = { remoteResource };
                return selectedResource;
            }
            return new ISVNRemoteResource[0];
        }

        protected boolean isEnabled() {
            return true;
        }

        public void selectionChanged(IAction action, ISelection sel) {
            if (sel instanceof IStructuredSelection) {
                fSelection = (IStructuredSelection) sel;
            }
        }
    }

    class CompareAction extends ShowDifferencesAsUnifiedDiffAction {
        public IStructuredSelection fSelection;;

        public CompareAction() {
            super();
            setUsePegRevision(true);
        }

        protected ISVNRemoteResource[] getSelectedRemoteResources() {
            ISVNRemoteResource remoteResource = null;
            if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
                try {
                    remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
                    if (remoteResource instanceof RemoteResource) {
                        ((RemoteResource) remoteResource)
                                .setPegRevision(((LogEntryChangePath) fSelection.getFirstElement()).getRevision());
                    }
                } catch (SVNException e) {
                }
            } else if (fSelection.getFirstElement() instanceof HistoryFolder) {
                HistoryFolder historyFolder = (HistoryFolder) fSelection.getFirstElement();
                Object[] children = historyFolder.getChildren();
                if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
                    LogEntryChangePath changePath = (LogEntryChangePath) children[0];
                    try {
                        ISVNRemoteResource changePathResource = changePath.getRemoteResource();
                        ISVNRemoteResource remoteFolder = changePathResource.getRepository()
                                .getRemoteFolder(historyFolder.getPath());
                        remoteResource = new RemoteFolder(null, changePathResource.getRepository(),
                                remoteFolder.getUrl(), changePathResource.getRevision(),
                                (SVNRevision.Number) changePathResource.getRevision(), null, null);
                    } catch (SVNException e) {
                    }
                }
            }
            if (remoteResource != null) {
                ISVNRemoteResource[] selectedResource = { remoteResource };
                return selectedResource;
            }
            return new ISVNRemoteResource[0];
        }

        protected boolean isEnabled() {
            return true;
        }

        public void selectionChanged(IAction action, ISelection sel) {
            if (sel instanceof IStructuredSelection) {
                fSelection = (IStructuredSelection) sel;
            }
        }
    }

    // get contents Action (context menu)
    private IAction getGetContentsAction() {
        if (getContentsAction == null) {
            getContentsAction = getContextMenuAction(Policy.bind("HistoryView.getContentsAction"), //$NON-NLS-1$
                    new IWorkspaceRunnable() {
                        public void run(IProgressMonitor monitor) throws CoreException {
                            ISelection selection = getSelection();
                            if (!(selection instanceof IStructuredSelection))
                                return;
                            IStructuredSelection ss = (IStructuredSelection) selection;
                            ISVNRemoteFile remoteFile = (ISVNRemoteFile) getLogEntry(ss).getRemoteResource();
                            monitor.beginTask(null, 100);
                            try {
                                if (remoteFile != null) {
                                    if (confirmOverwrite()) {
                                        if (remoteFile instanceof RemoteResource) {
                                            if (resource != null) {
                                                ISVNLocalResource localResource = SVNWorkspaceRoot
                                                        .getSVNResourceFor(resource);
                                                ((RemoteResource) remoteFile)
                                                        .setPegRevision(localResource.getRevision());
                                            } else {
                                                ((RemoteResource) remoteFile).setPegRevision(SVNRevision.HEAD);
                                            }
                                        }
                                        InputStream in = ((IResourceVariant) remoteFile)
                                                .getStorage(new SubProgressMonitor(monitor, 50)).getContents();
                                        IFile file = (IFile) resource;
                                        file.setContents(in, false, true, new SubProgressMonitor(monitor, 50));
                                    }
                                }
                            } catch (TeamException e) {
                                throw new CoreException(e.getStatus());
                            } finally {
                                monitor.done();
                            }
                        }
                    });
            PlatformUI.getWorkbench().getHelpSystem().setHelp(getContentsAction,
                    IHelpContextIds.GET_FILE_CONTENTS_ACTION);
        }
        return getContentsAction;
    }

    // update to the selected revision (context menu)
    private IAction getUpdateToRevisionAction() {
        if (updateToRevisionAction == null) {
            updateToRevisionAction = getContextMenuAction(Policy.bind("HistoryView.getRevisionAction"), //$NON-NLS-1$
                    new IWorkspaceRunnable() {
                        public void run(IProgressMonitor monitor) throws CoreException {
                            ISelection selection = getSelection();
                            if (!(selection instanceof IStructuredSelection))
                                return;
                            IStructuredSelection ss = (IStructuredSelection) selection;
                            ISVNRemoteFile remoteFile = (ISVNRemoteFile) getLogEntry(ss).getRemoteResource();
                            try {
                                if (remoteFile != null) {
                                    if (confirmOverwrite()) {
                                        IFile file = (IFile) resource;
                                        new ReplaceOperation(getSite().getPage().getActivePart(), file,
                                                remoteFile.getLastChangedRevision()).run(monitor);
                                        historyTableProvider.setRemoteResource(remoteFile);
                                        historyTableProvider.setProjectProperties(
                                                ProjectProperties.getProjectProperties(resource));
                                        Display.getDefault().asyncExec(new Runnable() {
                                            public void run() {
                                                tableHistoryViewer.refresh();
                                            }
                                        });
                                    }
                                }
                            } catch (InvocationTargetException e) {
                                throw new CoreException(new SVNStatus(IStatus.ERROR, 0, e.getMessage()));
                            } catch (InterruptedException e) {
                                // Cancelled by user
                            }
                        }
                    });
            PlatformUI.getWorkbench().getHelpSystem().setHelp(updateToRevisionAction,
                    IHelpContextIds.GET_FILE_REVISION_ACTION);
        }
        return updateToRevisionAction;
    }

    // get switch action (context menu)
    private IAction getSwitchAction() {
        if (switchAction == null) {
            switchAction = new Action() {
                public void run() {
                    if (selection instanceof IStructuredSelection) {
                        IStructuredSelection ss = (IStructuredSelection) selection;
                        if (ss.size() == 1) {
                            ILogEntry currentSelection = getLogEntry(ss);
                            IResource[] resources = { resource };
                            SvnWizardSwitchPage switchPage = new SvnWizardSwitchPage(resources,
                                    currentSelection.getRevision().getNumber());
                            SvnWizard wizard = new SvnWizard(switchPage);
                            SvnWizardDialog dialog = new SvnWizardDialog(getSite().getShell(), wizard);
                            wizard.setParentDialog(dialog);
                            if (dialog.open() == SvnWizardDialog.OK) {
                                SVNUrl[] svnUrls = switchPage.getUrls();
                                SVNRevision svnRevision = switchPage.getRevision();
                                SwitchOperation switchOperation = new SwitchOperation(
                                        getSite().getPage().getActivePart(), resources, svnUrls, svnRevision);
                                switchOperation.setDepth(switchPage.getDepth());
                                switchOperation.setSetDepth(switchPage.isSetDepth());
                                switchOperation.setIgnoreExternals(switchPage.isIgnoreExternals());
                                switchOperation.setForce(switchPage.isForce());
                                switchOperation.setIgnoreAncestry(switchPage.isIgnoreAncestry());
                                switchOperation.setConflictResolver(switchPage.getConflictResolver());
                                try {
                                    switchOperation.run();
                                } catch (Exception e) {
                                    MessageDialog.openError(getSite().getShell(), switchAction.getText(),
                                            e.getMessage());
                                }
                            }
                        }
                    }
                }
            };
        }
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            if (ss.size() == 1) {
                ILogEntry currentSelection = getLogEntry(ss);
                switchAction.setText(Policy.bind("HistoryView.switchToRevision", "" //$NON-NLS-1$ //$NON-NLS-2$
                        + currentSelection.getRevision().getNumber()));
            }
        }
        switchAction
                .setImageDescriptor(SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_SWITCH));
        return switchAction;
    }

    // get create tag from revision action (context menu)
    private IAction getCreateTagFromRevisionAction() {
        if (createTagFromRevisionAction == null) {
            createTagFromRevisionAction = new Action() {
                public void run() {
                    ISelection selection = getSelection();
                    if (!(selection instanceof IStructuredSelection))
                        return;
                    ILogEntry currentSelection = getLogEntry((IStructuredSelection) selection);
                    BranchTagWizard wizard;
                    if (resource == null) {
                        ISVNRemoteResource[] remoteResources = { historyTableProvider.getRemoteResource() };
                        wizard = new BranchTagWizard(remoteResources);
                    } else {
                        IResource[] resources = { resource };
                        wizard = new BranchTagWizard(resources);
                    }
                    wizard.setRevisionNumber(currentSelection.getRevision().getNumber());
                    WizardDialog dialog = new ClosableWizardDialog(getSite().getShell(), wizard);
                    if (dialog.open() == WizardDialog.OK) {
                        final SVNUrl sourceUrl = wizard.getUrl();
                        final SVNUrl destinationUrl = wizard.getToUrl();
                        final String message = wizard.getComment();
                        final SVNRevision revision = wizard.getRevision();
                        final boolean makeParents = wizard.isMakeParents();
                        boolean createOnServer = wizard.isCreateOnServer();
                        IResource[] resources = { resource };
                        try {
                            if (resource == null) {
                                BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                                    public void run() {
                                        ISVNClientAdapter client = null;
                                        try {
                                            client = SVNProviderPlugin.getPlugin().getSVNClientManager()
                                                    .getSVNClient();
                                            client.copy(sourceUrl, destinationUrl, message, revision, makeParents);
                                            SVNUIPlugin.getPlugin().getRepositoryManager().resourceCreated(null,
                                                    null);
                                        } catch (Exception e) {
                                            MessageDialog.openError(getSite().getShell(),
                                                    Policy.bind("HistoryView.createTagFromRevision"), e //$NON-NLS-1$
                                                            .getMessage());
                                        } finally {
                                            SVNProviderPlugin.getPlugin().getSVNClientManager()
                                                    .returnSVNClient(client);
                                        }
                                    }
                                });
                            } else {
                                BranchTagOperation branchTagOperation = new BranchTagOperation(
                                        getSite().getPage().getActivePart(), resources, new SVNUrl[] { sourceUrl },
                                        destinationUrl, createOnServer, wizard.getRevision(), message);
                                branchTagOperation.setMakeParents(makeParents);
                                branchTagOperation.setNewAlias(wizard.getNewAlias());
                                branchTagOperation.run();
                            }
                        } catch (Exception e) {
                            MessageDialog.openError(getSite().getShell(),
                                    Policy.bind("HistoryView.createTagFromRevision"), e //$NON-NLS-1$
                                            .getMessage());
                        }
                    }
                }
            };
        }
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            if (ss.size() == 1) {
                ILogEntry currentSelection = getLogEntry(ss);
                createTagFromRevisionAction.setText(Policy.bind("HistoryView.createTagFromRevision", "" //$NON-NLS-1$ //$NON-NLS-2$
                        + currentSelection.getRevision().getNumber()));
            }
        }
        createTagFromRevisionAction
                .setImageDescriptor(SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_BRANCHTAG));
        return createTagFromRevisionAction;
    }

    private IAction getSetCommitPropertiesAction() {
        // set Action (context menu)
        if (setCommitPropertiesAction == null) {
            setCommitPropertiesAction = new Action(Policy.bind("HistoryView.setCommitProperties")) { //$NON-NLS-1$
                public void run() {
                    try {
                        final ISelection selection = getSelection();
                        if (!(selection instanceof IStructuredSelection))
                            return;
                        final ILogEntry ourSelection = getLogEntry((IStructuredSelection) selection);

                        // Failing that, try the resource originally selected by the user if
                        // from the Team menu

                        // TODO: Search all paths from currentSelection and find the
                        // shortest path and
                        // get the resources for that instance (in order to get the 'best'
                        // "bugtraq" properties)
                        final ProjectProperties projectProperties = (resource != null)
                                ? ProjectProperties.getProjectProperties(resource)
                                : (ourSelection.getRemoteResource() != null)
                                        ? ProjectProperties.getProjectProperties(ourSelection.getRemoteResource())
                                        : ProjectProperties.getProjectProperties(remoteResource); // will return null!

                        final ISVNResource svnResource = ourSelection.getRemoteResource() != null
                                ? ourSelection.getRemoteResource()
                                : ourSelection.getResource();

                        SetCommitPropertiesDialog dialog = new SetCommitPropertiesDialog(getSite().getShell(),
                                ourSelection.getRevision(), resource, projectProperties);
                        // Set previous text - the text to edit
                        dialog.setOldAuthor(ourSelection.getAuthor());
                        dialog.setOldComment(ourSelection.getComment());

                        boolean doCommit = (dialog.open() == Window.OK);
                        if (doCommit) {
                            final String author;
                            final String commitComment;
                            if (ourSelection.getAuthor().equals(dialog.getAuthor()))
                                author = null;
                            else
                                author = dialog.getAuthor();
                            if (ourSelection.getComment().equals(dialog.getComment()))
                                commitComment = null;
                            else
                                commitComment = dialog.getComment();

                            final ChangeCommitPropertiesCommand command = new ChangeCommitPropertiesCommand(
                                    svnResource.getRepository(), ourSelection.getRevision(), commitComment, author);

                            PlatformUI.getWorkbench().getProgressService().run(true, true,
                                    new IRunnableWithProgress() {
                                        public void run(IProgressMonitor monitor) throws InvocationTargetException {
                                            try {
                                                command.run(monitor);
                                            } catch (SVNException e) {
                                                throw new InvocationTargetException(e);
                                            } finally {
                                                if (ourSelection instanceof LogEntry) {
                                                    LogEntry logEntry = (LogEntry) ourSelection;
                                                    if (command.isLogMessageChanged())
                                                        logEntry.setComment(commitComment);
                                                    if (command.isAuthorChanged())
                                                        logEntry.setAuthor(author);
                                                }
                                                getSite().getShell().getDisplay().asyncExec(new Runnable() {
                                                    public void run() {
                                                        tableHistoryViewer.refresh();
                                                        tableHistoryViewer.setSelection(selection, true);
                                                    }
                                                });
                                            }
                                        }
                                    });
                        }
                    } catch (InvocationTargetException e) {
                        SVNUIPlugin.openError(getSite().getShell(), null, null, e,
                                SVNUIPlugin.LOG_NONTEAM_EXCEPTIONS);
                    } catch (InterruptedException e) {
                        // Do nothing
                    } catch (SVNException e) {
                        // TODO Auto-generated catch block
                        SVNUIPlugin.openError(getSite().getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
                    }
                }

                // we don't allow multiple selection
                public boolean isEnabled() {
                    ISelection selection = getSelection();
                    return selection instanceof IStructuredSelection
                            && ((IStructuredSelection) selection).size() == 1;
                }
            };
        }
        return setCommitPropertiesAction;
    }

    private IAction getShowRevisionPropertiesAction() {
        if (showRevisionPropertiesAction == null) {
            showRevisionPropertiesAction = new Action(Policy.bind("HistoryView.showRevisionProperties")) { //$NON-NLS-1$
                public void run() {
                    try {
                        final ISelection selection = getSelection();
                        if (!(selection instanceof IStructuredSelection))
                            return;
                        final ILogEntry ourSelection = getLogEntry((IStructuredSelection) selection);

                        ISVNRemoteResource selectionRemoteResource = ourSelection.getRemoteResource();
                        SvnRevPropertiesView revPropsView = null;

                        try {
                            revPropsView = (SvnRevPropertiesView) getSite().getPage()
                                    .showView(SvnRevPropertiesView.VIEW_ID);
                        } catch (PartInitException e) {
                            SVNUIPlugin.openError(getSite().getShell(), null, null, e,
                                    SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
                        }

                        if (revPropsView != null) {
                            revPropsView.showSvnProperties(selectionRemoteResource);
                            revPropsView.refresh();
                        }
                    } catch (SVNException e) {
                        SVNUIPlugin.openError(getSite().getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
                    }
                }

                // we don't allow multiple selection
                public boolean isEnabled() {
                    ISelection selection = getSelection();
                    return selection instanceof IStructuredSelection
                            && ((IStructuredSelection) selection).size() == 1;
                }
            };
        }
        return showRevisionPropertiesAction;
    }

    private IAction getShowRevisionsAction() {
        if (showRevisionsAction == null) {
            showRevisionsAction = new Action(Policy.bind("HistoryView.showMergedRevisions")) { //$NON-NLS-1$
                public void run() {
                    ISelection selection = getSelection();
                    if (!(selection instanceof IStructuredSelection))
                        return;
                    IStructuredSelection ss = (IStructuredSelection) selection;
                    ILogEntry logEntry = (ILogEntry) ss.getFirstElement();
                    ShowRevisionsDialog dialog = null;
                    if (resource != null)
                        dialog = new ShowRevisionsDialog(getSite().getShell(), logEntry, resource, includeTags,
                                SVNHistoryPage.this);
                    else if (remoteResource != null)
                        dialog = new ShowRevisionsDialog(getSite().getShell(), logEntry, remoteResource,
                                includeTags, SVNHistoryPage.this);
                    if (dialog != null)
                        dialog.open();
                }
            };
        }
        return showRevisionsAction;
    }

    private SVNRevisionRange[] getRevisionRanges() {
        List revisionRanges = new ArrayList();
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            List selectionList = ss.toList();
            TableItem[] items = tableHistoryViewer.getTable().getItems();
            SVNRevision revision1 = null;
            SVNRevision revision2 = null;
            for (int i = 0; i < items.length; i++) {
                if (items[i].getData() instanceof ILogEntry) {
                    ILogEntry logEntry = (ILogEntry) items[i].getData();
                    if (selectionList.contains(logEntry)) {
                        if (revision1 == null) {
                            revision1 = logEntry.getRevision();
                        }
                        revision2 = logEntry.getRevision();
                        revertEnabled = true;
                        LogEntryChangePath[] changePaths = logEntry.getLogEntryChangePaths();
                        if (changePaths != null) {
                            for (LogEntryChangePath changePath : changePaths) {
                                if (changePath.getPath().equals(remoteResource.getRepositoryRelativePath())) {
                                    if (changePath.getAction() == 'A') {
                                        revertEnabled = false;
                                    }
                                    break;
                                }
                            }
                        }
                    } else {
                        if (revision1 != null) {
                            SVNRevisionRange revisionRange = new SVNRevisionRange(revision1, revision2);
                            revisionRanges.add(revisionRange);
                            revision1 = null;
                            revision2 = null;
                        }
                    }
                }
            }
            if (revision1 != null) {
                SVNRevisionRange revisionRange = new SVNRevisionRange(revision1, revision2);
                revisionRanges.add(revisionRange);
            }
        }
        SVNRevisionRange[] revisionRangeArray = new SVNRevisionRange[revisionRanges.size()];
        revisionRanges.toArray(revisionRangeArray);
        return revisionRangeArray;
    }

    // get revert changes action (context menu)
    private IAction getRevertChangesAction() {
        revisionRanges = getRevisionRanges();
        if (revertChangesAction == null) {
            revertChangesAction = new Action() {
                public void run() {
                    ISelection selection = getSelection();
                    if (!(selection instanceof IStructuredSelection))
                        return;
                    final IStructuredSelection ss = (IStructuredSelection) selection;
                    if (ss.size() == 1) {
                        if (!MessageDialog.openConfirm(getSite().getShell(),
                                Policy.bind("HistoryView.revertRevision"), Policy.bind(
                                        "HistoryView.confirmRevertRevision", resource.getFullPath().toString()))) //$NON-NLS-1$
                            return;
                    } else {
                        if (!MessageDialog.openConfirm(getSite().getShell(),
                                Policy.bind("HistoryView.revertRevisions"), Policy.bind(
                                        "HistoryView.confirmRevertRevisions", resource.getFullPath().toString()))) //$NON-NLS-1$
                            return;
                    }
                    BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
                        public void run() {
                            ILogEntry firstElement = getFirstElement();
                            ILogEntry lastElement = getLastElement();
                            final IResource[] resources = { resource };
                            try {
                                final SVNUrl path1 = new SVNUrl(firstElement.getResource().getUrl() + "@HEAD");
                                final SVNUrl path2 = new SVNUrl(lastElement.getResource().getUrl() + "@HEAD");
                                for (int i = 0; i < revisionRanges.length; i++) {
                                    final SVNRevision revision1 = revisionRanges[i].getFromRevision();
                                    final SVNRevision revision2 = new SVNRevision.Number(
                                            ((SVNRevision.Number) revisionRanges[i].getToRevision()).getNumber()
                                                    - 1);
                                    WorkspaceAction mergeAction = new WorkspaceAction() {
                                        protected void execute(IAction action)
                                                throws InvocationTargetException, InterruptedException {
                                            new MergeOperation(getSite().getPage().getActivePart(), resources,
                                                    path1, revision1, path2, revision2).run();
                                        }
                                    };
                                    mergeAction.run(null);
                                }
                            } catch (Exception e) {
                                MessageDialog.openError(getSite().getShell(), revertChangesAction.getText(),
                                        e.getMessage());
                            }
                        }
                    });
                }
            };
        }
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            if (ss.size() == 1) {
                ILogEntry currentSelection = getLogEntry(ss);
                revertChangesAction.setText(Policy.bind("HistoryView.revertChangesFromRevision", "" //$NON-NLS-1$ //$NON-NLS-2$
                        + currentSelection.getRevision().getNumber()));
            }
            if (ss.size() > 1) {
                revertChangesAction.setText(Policy.bind("HistoryView.revertChangesFromRevisions")); //$NON-NLS-1$
            }
        }
        revertChangesAction.setImageDescriptor(
                SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_MARKMERGED));
        revertChangesAction.setEnabled(revertEnabled);
        return revertChangesAction;
    }

    private GenerateChangeLogAction getGenerateChangeLogAction() {
        if (generateChangeLogAction == null)
            generateChangeLogAction = new GenerateChangeLogAction(new ISelectionProvider() {
                public void addSelectionChangedListener(ISelectionChangedListener listener) {
                }

                public ISelection getSelection() {
                    return SVNHistoryPage.this.getSelection();
                }

                public void removeSelectionChangedListener(ISelectionChangedListener listener) {
                }

                public void setSelection(ISelection selection) {
                }
            });
        return generateChangeLogAction;
    }

    // Refresh action (toolbar)
    private IAction getRefreshAction() {
        if (refreshAction == null) {
            SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
            refreshAction = new Action(Policy.bind("HistoryView.refreshLabel"), //$NON-NLS-1$
                    plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH_ENABLED)) {
                public void run() {
                    refresh();
                }
            };
            refreshAction.setToolTipText(Policy.bind("HistoryView.refresh")); //$NON-NLS-1$
            refreshAction
                    .setDisabledImageDescriptor(plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH_DISABLED));
            refreshAction.setHoverImageDescriptor(plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH));
        }
        return refreshAction;
    }

    // Search action (toolbar)
    private IAction getSearchAction() {
        if (searchAction == null) {
            SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
            searchAction = new Action(Policy.bind("HistoryView.search"), //$NON-NLS-1$
                    plugin.getImageDescriptor(ISVNUIConstants.IMG_FILTER_HISTORY)) {
                public void run() {
                    if (historySearchDialog == null) {
                        historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
                    }
                    historySearchDialog.setRemoteResource(remoteResource);
                    if (historySearchDialog.open() == Window.OK) {
                        searchAction.setEnabled(false);
                        Utils.schedule(new SearchHistoryJob(), SVNUIPlugin.getPlugin().getWorkbench()
                                .getActiveWorkbenchWindow().getActivePage().getActivePart().getSite());
                    }
                }
            };
            searchAction.setDisabledImageDescriptor(
                    plugin.getImageDescriptor(ISVNUIConstants.IMG_FILTER_HISTORY_DISABLED));
        }
        return searchAction;
    }

    // Clear search action (toolbar)
    private IAction getClearSearchAction() {
        if (clearSearchAction == null) {
            SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
            clearSearchAction = new Action(Policy.bind("HistoryView.clearSearch"), //$NON-NLS-1$
                    plugin.getImageDescriptor(ISVNUIConstants.IMG_CLEAR)) {
                public void run() {
                    BusyIndicator.showWhile(tableHistoryViewer.getTable().getDisplay(), new Runnable() {
                        public void run() {
                            ViewerFilter[] filters = tableHistoryViewer.getFilters();
                            for (int i = 0; i < filters.length; i++) {
                                ViewerFilter filter = filters[i];
                                if (filter instanceof HistorySearchViewerFilter) {
                                    tableHistoryViewer.removeFilter(filter);
                                } else if (filter instanceof EmptySearchViewerFilter) {
                                    tableHistoryViewer.removeFilter(filter);
                                }
                            }
                            setEnabled(false);
                            if (!historySearchDialog.getSearchAllLogs()
                                    && (historySearchDialog.getStartRevision() != null
                                            || historySearchDialog.getEndRevision() != null)) {
                                getRefreshAction().run();
                            }
                        }
                    });
                }
            };
            clearSearchAction
                    .setDisabledImageDescriptor(plugin.getImageDescriptor(ISVNUIConstants.IMG_CLEAR_DISABLED));
        }
        return clearSearchAction;
    }

    // Get Get All action (toolbar)
    private IAction getGetAllAction() {
        if (getAllAction == null) {
            SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
            getAllAction = new Action(Policy.bind("HistoryView.getAll"), //$NON-NLS-1$
                    plugin.getImageDescriptor(ISVNUIConstants.IMG_GET_ALL)) {
                public void run() {
                    final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
                    if (fetchAllLogEntriesJob == null) {
                        fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
                    }
                    if (fetchAllLogEntriesJob.getState() != Job.NONE) {
                        fetchAllLogEntriesJob.cancel();
                        try {
                            fetchAllLogEntriesJob.join();
                        } catch (InterruptedException e) {
                            SVNUIPlugin.log(new SVNException(
                                    Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$
                        }
                    }
                    fetchAllLogEntriesJob.setRemoteFile(remoteResource);
                    Utils.schedule(fetchAllLogEntriesJob, getSite());
                }
            };
            getAllAction.setToolTipText(Policy.bind("HistoryView.getAll")); //$NON-NLS-1$
        }
        return getAllAction;
    }

    // Get Get Next action (toolbar)
    public IAction getGetNextAction() {
        if (getNextAction == null) {
            getNextAction = new GetNextAction();
        }
        return getNextAction;
    }

    /**
     * All context menu actions use this class This action : - updates
     * currentSelection - action.run
     */
    private Action getContextMenuAction(String title, final IWorkspaceRunnable action) {
        return new Action(title) {
            public void run() {
                try {
                    if (resource == null)
                        return;
                    PlatformUI.getWorkbench().getProgressService().run(true, true, new IRunnableWithProgress() {
                        public void run(IProgressMonitor monitor) throws InvocationTargetException {
                            try {
                                action.run(monitor);
                            } catch (CoreException e) {
                                throw new InvocationTargetException(e);
                            }
                        }
                    });
                } catch (InvocationTargetException e) {
                    SVNUIPlugin.openError(getSite().getShell(), null, null, e, SVNUIPlugin.LOG_NONTEAM_EXCEPTIONS);
                } catch (InterruptedException e) {
                    // Do nothing
                }
            }

            // we don't allow multiple selection
            public boolean isEnabled() {
                ISelection selection = getSelection();
                return selection instanceof IStructuredSelection && ((IStructuredSelection) selection).size() == 1;
            }
        };
    }

    /**
     * Ask the user to confirm the overwrite of the file if the file has been
     * modified since last commit
     */
    private boolean confirmOverwrite() {
        IFile file = (IFile) resource;
        if (file != null && file.exists()) {
            ISVNLocalFile svnFile = SVNWorkspaceRoot.getSVNFileFor(file);
            try {
                if (svnFile.isDirty()) {
                    String title = Policy.bind("HistoryView.overwriteTitle"); //$NON-NLS-1$
                    String msg = Policy.bind("HistoryView.overwriteMsg"); //$NON-NLS-1$
                    final MessageDialog dialog = new MessageDialog(getSite().getShell(), title, null, msg,
                            MessageDialog.QUESTION,
                            new String[] { IDialogConstants.YES_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
                    final int[] result = new int[1];
                    getSite().getShell().getDisplay().syncExec(new Runnable() {
                        public void run() {
                            result[0] = dialog.open();
                        }
                    });
                    if (result[0] != 0) {
                        // cancel
                        return false;
                    }
                }
            } catch (SVNException e) {
                SVNUIPlugin.log(e.getStatus());
            }
        }
        return true;
    }

    private ISelection getSelection() {
        return selection;
    }

    private ILogEntry getFirstElement() {
        ILogEntry firstElement = null;
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            Iterator iter = ss.iterator();
            while (iter.hasNext()) {
                ILogEntry element = (ILogEntry) iter.next();
                if (firstElement == null
                        || element.getRevision().getNumber() > firstElement.getRevision().getNumber())
                    firstElement = element;
            }
        }
        return firstElement;
    }

    private ILogEntry getLastElement() {
        ILogEntry lastElement = null;
        ISelection selection = getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            Iterator iter = ss.iterator();
            while (iter.hasNext()) {
                ILogEntry element = (ILogEntry) iter.next();
                if (lastElement == null
                        || element.getRevision().getNumber() < lastElement.getRevision().getNumber())
                    lastElement = element;
            }
        }
        return lastElement;
    }

    private final class GetNextAction extends Action implements IPropertyChangeListener {
        GetNextAction() {
            super(Policy.bind("HistoryView.getNext"), //$NON-NLS-1$
                    SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_GET_NEXT));
            updateFromProperties();
            SVNUIPlugin.getPlugin().getPreferenceStore().addPropertyChangeListener(this);
        }

        public void run() {
            final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
            if (fetchNextLogEntriesJob == null) {
                fetchNextLogEntriesJob = new FetchNextLogEntriesJob();
            }
            if (fetchNextLogEntriesJob.getState() != Job.NONE) {
                fetchNextLogEntriesJob.cancel();
                try {
                    fetchNextLogEntriesJob.join();
                } catch (InterruptedException e) {
                    SVNUIPlugin.log(new SVNException(
                            Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), e)); //$NON-NLS-1$
                }
            }
            fetchNextLogEntriesJob.setRemoteFile(remoteResource);
            Utils.schedule(fetchNextLogEntriesJob, getSite());
        }

        public void propertyChange(PropertyChangeEvent event) {
            if (ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH.equals(event.getProperty())) {
                updateFromProperties();
            }
        }

        private void updateFromProperties() {
            int entriesToFetch = SVNUIPlugin.getPlugin().getPreferenceStore()
                    .getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
            setToolTipText(Policy.bind("HistoryView.getNext") + " " + entriesToFetch); //$NON-NLS-1$ //$NON-NLS-2$
            if (entriesToFetch <= 0) {
                setEnabled(false);
            }
        }
    }

    private class FetchLogEntriesJob extends AbstractFetchJob {
        public ISVNRemoteResource remoteResource;

        public FetchLogEntriesJob() {
            super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$;
        }

        public void setRemoteFile(ISVNRemoteResource resource) {
            this.remoteResource = resource;
        }

        public IStatus run(IProgressMonitor monitor) {
            try {
                if (remoteResource != null && !shutdown) {
                    if (resource == null) {
                        if (remoteResource == null || !SVNUIPlugin.getPlugin().getPreferenceStore()
                                .getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE)) {
                            tagManager = null;
                        } else {
                            tagManager = new AliasManager(remoteResource.getUrl());
                        }
                    } else {
                        tagManager = new AliasManager(resource);
                    }
                    final SVNRevision pegRevision = remoteResource.getRevision();
                    SVNRevision revisionEnd = new SVNRevision.Number(0);
                    boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
                    boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
                    int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
                    long limit = entriesToFetch;
                    try {
                        entries = getLogEntries(monitor, remoteResource, pegRevision, revisionStart, revisionEnd,
                                stopOnCopy, limit + 1, tagManager, includeMergedRevisions);
                    } catch (TeamException e) {
                        if (revisionStart.equals(SVNRevision.HEAD) && pegRevision != null && e.getMessage() != null
                                && e.getMessage().contains("svn: Unable to find repository location")) {
                            revisionStart = pegRevision;
                            entries = getLogEntries(monitor, remoteResource, pegRevision, revisionStart,
                                    revisionEnd, stopOnCopy, limit + 1, tagManager, includeMergedRevisions);
                        } else {
                            throw e;
                        }
                    }

                    long entriesLength = entries.length;
                    if (entriesLength > limit) {
                        ILogEntry[] fetchedEntries = new ILogEntry[entries.length - 1];
                        for (int i = 0; i < entries.length - 1; i++) {
                            fetchedEntries[i] = entries[i];
                        }
                        entries = fetchedEntries;
                        getNextAction.setEnabled(true);
                    } else {
                        getNextAction.setEnabled(false);
                    }
                    final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision();
                    getSite().getShell().getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            if (entries != null && tableHistoryViewer != null
                                    && !tableHistoryViewer.getTable().isDisposed()) {
                                // once we got the entries, we refresh the table
                                if (entries.length > 0) {
                                    lastEntry = entries[entries.length - 1];
                                    long lastEntryNumber = lastEntry.getRevision().getNumber();
                                    revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
                                }
                                tableHistoryViewer.refresh();
                                selectRevision(revisionId);
                            }
                        }
                    });
                }
                return Status.OK_STATUS;
            } catch (TeamException e) {
                if (e instanceof SVNException) {
                    if (((SVNException) e).operationInterrupted()) {
                        return Status.OK_STATUS;
                    }
                }
                return e.getStatus();
            }
        }
    }

    private class FetchNextLogEntriesJob extends AbstractFetchJob {
        public ISVNRemoteResource remoteResource;

        public FetchNextLogEntriesJob() {
            super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$;
        }

        public void setRemoteFile(ISVNRemoteResource resource) {
            this.remoteResource = resource;
        }

        public IStatus run(IProgressMonitor monitor) {
            try {
                if (remoteResource != null && !shutdown) {
                    SVNRevision pegRevision = remoteResource.getRevision();
                    SVNRevision revisionEnd = new SVNRevision.Number(0);
                    boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
                    boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
                    int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
                    long limit = entriesToFetch;
                    ILogEntry[] nextEntries = getLogEntries(monitor, remoteResource, pegRevision, revisionStart,
                            revisionEnd, stopOnCopy, limit + 1, tagManager, includeMergedRevisions);
                    long entriesLength = nextEntries.length;
                    ILogEntry[] fetchedEntries = null;
                    if (entriesLength > limit) {
                        fetchedEntries = new ILogEntry[nextEntries.length - 1];
                        for (int i = 0; i < nextEntries.length - 1; i++)
                            fetchedEntries[i] = nextEntries[i];
                        getNextAction.setEnabled(true);
                    } else {
                        fetchedEntries = new ILogEntry[nextEntries.length];
                        for (int i = 0; i < nextEntries.length; i++)
                            fetchedEntries[i] = nextEntries[i];
                        getNextAction.setEnabled(false);
                    }
                    ArrayList entryArray = new ArrayList();
                    if (entries == null)
                        entries = new ILogEntry[0];
                    for (int i = 0; i < entries.length; i++)
                        entryArray.add(entries[i]);
                    for (int i = 0; i < fetchedEntries.length; i++)
                        entryArray.add(fetchedEntries[i]);
                    entries = new ILogEntry[entryArray.size()];
                    entryArray.toArray(entries);
                    getSite().getShell().getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            if (entries != null && tableHistoryViewer != null
                                    && !tableHistoryViewer.getTable().isDisposed()) {
                                // once we got the entries, we refresh the table
                                ISelection selection = tableHistoryViewer.getSelection();
                                tableHistoryViewer.refresh();
                                tableHistoryViewer.setSelection(selection);
                            }
                        }
                    });
                    if (entries.length > 0) {
                        lastEntry = entries[entries.length - 1];
                        long lastEntryNumber = lastEntry.getRevision().getNumber();
                        revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
                    }
                }
                return Status.OK_STATUS;
            } catch (TeamException e) {
                return e.getStatus();
            }
        }
    }

    private abstract class AbstractFetchJob extends Job {
        public AbstractFetchJob(String name) {
            super(name);
        }

        public abstract void setRemoteFile(ISVNRemoteResource resource);

        protected ILogEntry[] getLogEntries(IProgressMonitor monitor, ISVNRemoteResource remoteResource,
                SVNRevision pegRevision, SVNRevision revisionStart, SVNRevision revisionEnd, boolean stopOnCopy,
                long limit, AliasManager tagManager, boolean includeMergedRevisions) throws TeamException {
            // If filtering by revision range, pass upper/lower revisions to API and override limit.
            SVNRevision start = revisionStart;
            SVNRevision end = revisionEnd;
            long fetchLimit = limit;
            if (historySearchDialog != null && !historySearchDialog.getSearchAllLogs()) {
                if (historySearchDialog.getStartRevision() != null
                        || historySearchDialog.getEndRevision() != null) {
                    if (getClearSearchAction().isEnabled()) {
                        if (historySearchDialog.getStartRevision() != null)
                            end = historySearchDialog.getStartRevision();
                        if (historySearchDialog.getEndRevision() != null)
                            start = historySearchDialog.getEndRevision();
                        fetchLimit = 0;
                        getGetNextAction().setEnabled(false);
                    }
                }
            }
            ISVNClientAdapter svnClient = remoteResource.getRepository().getSVNClient();
            try {
                CancelableSVNLogMessageCallback callback = new CancelableSVNLogMessageCallback(monitor, svnClient);
                GetLogsCommand logCmd = new GetLogsCommand(remoteResource, pegRevision, start, end, stopOnCopy,
                        fetchLimit, tagManager, includeMergedRevisions);
                logCmd.setCallback(callback);
                logCmd.run(monitor);
                return logCmd.getLogEntries();
            } finally {
                remoteResource.getRepository().returnSVNClient(svnClient);
            }
        }
    }

    private class FetchAllLogEntriesJob extends AbstractFetchJob {
        public ISVNRemoteResource remoteResource;

        public FetchAllLogEntriesJob() {
            super(Policy.bind("HistoryView.fetchHistoryJob")); //$NON-NLS-1$;
        }

        public void setRemoteFile(ISVNRemoteResource resource) {
            this.remoteResource = resource;
        }

        public IStatus run(IProgressMonitor monitor) {
            try {
                if (remoteResource != null && !shutdown) {
                    if (resource == null) {
                        if (remoteResource == null || !SVNUIPlugin.getPlugin().getPreferenceStore()
                                .getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE))
                            tagManager = null;
                        else
                            tagManager = new AliasManager(remoteResource.getUrl());
                    } else
                        tagManager = new AliasManager(resource);
                    SVNRevision pegRevision = remoteResource.getRevision();
                    SVNRevision revisionEnd = new SVNRevision.Number(0);
                    boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
                    boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
                    long limit = 0;
                    entries = getLogEntries(monitor, remoteResource, pegRevision, SVNRevision.HEAD, revisionEnd,
                            stopOnCopy, limit, tagManager, includeMergedRevisions);
                    final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision();
                    getSite().getShell().getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            if (entries != null && tableHistoryViewer != null
                                    && !tableHistoryViewer.getTable().isDisposed()) {
                                // once we got the entries, we refresh the table
                                if (entries.length > 0) {
                                    lastEntry = entries[entries.length - 1];
                                    long lastEntryNumber = lastEntry.getRevision().getNumber();
                                    revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
                                }
                                tableHistoryViewer.refresh();
                                selectRevision(revisionId);
                            }
                        }
                    });
                }
                return Status.OK_STATUS;
            } catch (TeamException e) {
                return e.getStatus();
            }
        }
    }

    private class SearchHistoryJob extends Job {

        public SearchHistoryJob() {
            super(""); //$NON-NLS-1$
        }

        public IStatus run(IProgressMonitor monitor) {
            if (!historySearchDialog.getSearchAllLogs() && (historySearchDialog.getStartRevision() != null
                    || historySearchDialog.getEndRevision() != null)) {
                final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
                if (fetchAllLogEntriesJob == null) {
                    fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
                }
                if (fetchAllLogEntriesJob.getState() != Job.NONE) {
                    fetchAllLogEntriesJob.cancel();
                }
                fetchAllLogEntriesJob.setRemoteFile(remoteResource);
                Utils.schedule(fetchAllLogEntriesJob, getSite());
            } else {
                Date startDate = historySearchDialog.getStartDate();
                setEmptyViewerFilter();

                // Fetch log entries until start date
                if (historySearchDialog.getAutoFetchLogs()) {
                    if (!historySearchDialog.getSearchAllLogs()) {
                        Date lastDate = null;
                        if (lastEntry != null) {
                            lastDate = lastEntry.getDate();
                        }
                        int numEntries = entries.length;
                        int prevNumEntries = -1;
                        while ((numEntries != prevNumEntries) && ((lastDate == null) || (startDate == null)
                                || (startDate.compareTo(lastDate) <= 0))) {

                            if (monitor.isCanceled()) {
                                getSearchAction().setEnabled(true);
                                removeEmptyViewerFilter();
                                return Status.CANCEL_STATUS;
                            }

                            final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
                            if (fetchNextLogEntriesJob == null) {
                                fetchNextLogEntriesJob = new FetchNextLogEntriesJob();
                            }
                            if (fetchNextLogEntriesJob.getState() != Job.NONE) {
                                fetchNextLogEntriesJob.cancel();
                            }
                            fetchNextLogEntriesJob.setRemoteFile(remoteResource);
                            Utils.schedule(fetchNextLogEntriesJob, getSite());
                            try {
                                fetchNextLogEntriesJob.join();
                            } catch (InterruptedException e) {
                                SVNUIPlugin.log(new SVNException(
                                        Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()), //$NON-NLS-1$
                                        e));
                            }

                            if (entries.length == 0) {
                                break;
                            }
                            lastDate = lastEntry.getDate();
                            prevNumEntries = numEntries;
                            numEntries = entries.length;
                        }
                    } else {
                        final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
                        if (fetchAllLogEntriesJob == null) {
                            fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
                        }
                        if (fetchAllLogEntriesJob.getState() != Job.NONE) {
                            fetchAllLogEntriesJob.cancel();
                        }
                        fetchAllLogEntriesJob.setRemoteFile(remoteResource);
                        Utils.schedule(fetchAllLogEntriesJob, getSite());
                    }
                }
            }

            final HistorySearchViewerFilter viewerFilter = new HistorySearchViewerFilter(
                    historySearchDialog.getUser(), historySearchDialog.getComment(),
                    historySearchDialog.getStartDate(), historySearchDialog.getEndDate(),
                    historySearchDialog.getRegExp(), historySearchDialog.getStartRevision(),
                    historySearchDialog.getEndRevision());

            getSite().getShell().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    BusyIndicator.showWhile(tableHistoryViewer.getTable().getDisplay(), new Runnable() {
                        public void run() {
                            getClearSearchAction().run();
                            tableHistoryViewer.addFilter(viewerFilter);
                            getClearSearchAction().setEnabled(true);
                            getSearchAction().setEnabled(true);
                        }
                    });
                }
            });

            removeEmptyViewerFilter();

            return Status.OK_STATUS;
        }

        private void setEmptyViewerFilter() {
            getSite().getShell().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    tableHistoryViewer.addFilter(new EmptySearchViewerFilter());
                }
            });
        }

        private void removeEmptyViewerFilter() {
            getSite().getShell().getDisplay().asyncExec(new Runnable() {
                public void run() {
                    ViewerFilter[] filters = tableHistoryViewer.getFilters();
                    for (int i = 0; i < filters.length; i++) {
                        if (filters[i] instanceof EmptySearchViewerFilter) {
                            tableHistoryViewer.removeFilter(filters[i]);
                        }
                    }
                }
            });
        }
    }

    class FetchChangePathJob extends Job {
        public ILogEntry logEntry;

        public FetchChangePathJob() {
            super(Policy.bind("HistoryView.fetchChangePathJob")); //$NON-NLS-1$;
        }

        public void setLogEntry(ILogEntry logEntry) {
            this.logEntry = logEntry;
        }

        public IStatus run(IProgressMonitor monitor) {
            if (logEntry.getResource() != null) {
                setCurrentLogEntryChangePath(logEntry.getLogEntryChangePaths());
            }
            return Status.OK_STATUS;
        }
    }

    public static class ToggleAffectedPathsOptionAction extends Action {
        private final SVNHistoryPage page;
        private final String preferenceName;
        private final int value;

        public ToggleAffectedPathsOptionAction(SVNHistoryPage page, String label, String icon,
                String preferenceName, int value) {
            super(Policy.bind(label), AS_RADIO_BUTTON);
            this.page = page;
            this.preferenceName = preferenceName;
            this.value = value;
            setImageDescriptor(SVNUIPlugin.getPlugin().getImageDescriptor(icon));

            IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore();
            setChecked(value == store.getInt(preferenceName));
        }

        public int getValue() {
            return this.value;
        }

        public void run() {
            if (isChecked()) {
                SVNUIPlugin.getPlugin().getPreferenceStore().setValue(preferenceName, value);
                page.createAffectedPathsViewer();
            }
        }

    }

    /* (non-Javadoc)
     * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#resourceSyncInfoChanged(org.eclipse.core.resources.IResource[])
     */
    public void resourceSyncInfoChanged(IResource[] changedResources) {
        for (int i = 0; i < changedResources.length; i++) {
            IResource changedResource = changedResources[i];
            if (changedResource.equals(resource)) {
                resourceChanged();
            }
        }
    }

    /**
     * This method updates the history table, highlighting the current revison
     * without refetching the log entries to preserve bandwidth.
     * The user has to a manual refresh to get the new log entries. 
     */
    private void resourceChanged() {
        getSite().getShell().getDisplay().asyncExec(new Runnable() {
            public void run() {
                // Preserve the original starting revision so that when Next is pressed
                // the correct log entries will be fetched.
                SVNRevision holdRevisionStart = revisionStart;
                revisionStart = SVNRevision.HEAD;
                ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
                try {
                    if (localResource != null && !localResource.getStatus().isAdded()) {
                        ISVNRemoteResource baseResource = localResource.getBaseResource();
                        historyTableProvider.setRemoteResource(baseResource);
                        historyTableProvider.setProjectProperties(null);
                        tableHistoryViewer.refresh();
                    }
                } catch (SVNException e) {
                    if (!e.operationInterrupted()) {
                        SVNUIPlugin.openError(getHistoryPageSite().getShell(), null, null, e);
                    }
                }
                revisionStart = holdRevisionStart;
            }
        });
    }

    /* (non-Javadoc)
     * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
     */
    public void keyPressed(KeyEvent e) {
        if (e.keyCode == 'f' && e.stateMask == SWT.CTRL) {
            getSearchAction().run();
        }
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
     */
    public void keyReleased(KeyEvent e) {
    }

    /* (non-Javadoc)
     * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#resourceModified(org.eclipse.core.resources.IResource[])
     */
    public void resourceModified(IResource[] changedResources) {
    }

    /* (non-Javadoc)
     * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#projectConfigured(org.eclipse.core.resources.IProject)
     */
    public void projectConfigured(IProject project) {
    }

    /* (non-Javadoc)
     * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#projectDeconfigured(org.eclipse.core.resources.IProject)
     */
    public void projectDeconfigured(IProject project) {
    }

    public void initialize() {
    }

    public IResource getResource() {
        return resource;
    }

    public static void setHistorySearchViewerFilter(HistorySearchViewerFilter historySearchViewerFilter) {
        SVNHistoryPage.historySearchViewerFilter = historySearchViewerFilter;
    }

}