/*******************************************************************************
* Copyright (c) 2011 GitHub Inc.
* 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:
* Kevin Sawicki (GitHub Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.egit.ui.internal.commit;
import static java.util.Arrays.asList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.history.FileDiff;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.IUpdate;
/**
* Diff editor page class for displaying a {@link DiffViewer}.
*/
public class DiffEditorPage extends FormPage {
private static class TextViewerAction extends Action implements IUpdate {
private int code = -1;
private ITextOperationTarget target;
public TextViewerAction(ITextViewer viewer, int operationCode) {
code = operationCode;
target = viewer.getTextOperationTarget();
update();
}
public void update() {
if (code == ITextOperationTarget.REDO)
return;
boolean wasEnabled = isEnabled();
boolean isEnabled = target.canDoOperation(code);
setEnabled(isEnabled);
if (wasEnabled != isEnabled)
firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE
: Boolean.FALSE, isEnabled ? Boolean.TRUE
: Boolean.FALSE);
}
public void run() {
if (code != -1)
target.doOperation(code);
}
}
private DiffViewer viewer;
private DiffStyleRangeFormatter formatter;
/**
* @param editor
* @param id
* @param title
*/
public DiffEditorPage(FormEditor editor, String id, String title) {
super(editor, id, title);
}
/**
* @param editor
*/
public DiffEditorPage(FormEditor editor) {
this(editor, "diffPage", UIText.DiffEditorPage_Title); //$NON-NLS-1$
}
/**
* @param commit
* @return diffs for changes of of a commit
*/
protected FileDiff[] getDiffs(RepositoryCommit commit) {
List<FileDiff> diffResult = new ArrayList<FileDiff>();
diffResult.addAll(asList(commit.getDiffs()));
if (commit.getRevCommit().getParentCount() > 2) {
RevCommit untrackedCommit = commit.getRevCommit().getParent(
StashEditorPage.PARENT_COMMIT_UNTRACKED);
diffResult.addAll(asList(new RepositoryCommit(commit
.getRepository(), untrackedCommit).getDiffs()));
}
Collections.sort(diffResult, FileDiff.PATH_COMPARATOR);
return diffResult.toArray(new FileDiff[0]);
}
private void formatDiff() {
final IDocument document = new Document();
formatter = new DiffStyleRangeFormatter(document);
viewer.setFormatter(formatter);
Job job = new Job(UIText.DiffEditorPage_TaskGeneratingDiff) {
protected IStatus run(IProgressMonitor monitor) {
RepositoryCommit commit = (RepositoryCommit) getEditor()
.getAdapter(RepositoryCommit.class);
FileDiff diffs[] = getDiffs(commit);
monitor.beginTask("", diffs.length); //$NON-NLS-1$
Repository repository = commit.getRepository();
for (FileDiff diff : diffs) {
if (monitor.isCanceled())
break;
monitor.setTaskName(diff.getPath());
try {
formatter.write(repository, diff);
} catch (IOException ignore) {
// Ignored
}
monitor.worked(1);
}
monitor.done();
new UIJob(UIText.DiffEditorPage_TaskUpdatingViewer) {
public IStatus runInUIThread(IProgressMonitor uiMonitor) {
if (UIUtils.isUsable(viewer)) {
viewer.setDocument(document);
viewer.refreshStyleRanges();
}
return Status.OK_STATUS;
}
}.schedule();
return Status.OK_STATUS;
}
};
job.schedule();
}
/**
* Add editor actions to menu manager
*
* @param manager
*/
protected void addEditorActions(MenuManager manager) {
final TextViewerAction copyAction = new TextViewerAction(viewer,
ITextOperationTarget.COPY);
copyAction.setText(UIText.SpellCheckingMessageArea_copy);
copyAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
final TextViewerAction selectAllAction = new TextViewerAction(viewer,
ITextOperationTarget.SELECT_ALL);
selectAllAction.setText(UIText.SpellCheckingMessageArea_selectAll);
selectAllAction
.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
manager.add(copyAction);
manager.add(selectAllAction);
manager.add(new Separator());
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
copyAction.update();
selectAllAction.update();
}
});
}
/**
* @see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm)
*/
protected void createFormContent(IManagedForm managedForm) {
Composite body = managedForm.getForm().getBody();
GridLayoutFactory.fillDefaults().numColumns(1).applyTo(body);
viewer = new DiffViewer(body, new CompositeRuler(), SWT.V_SCROLL
| SWT.H_SCROLL, true);
viewer.setEditable(false);
GridDataFactory.fillDefaults().grab(true, true)
.applyTo(viewer.getControl());
MenuManager manager = new MenuManager();
addEditorActions(manager);
Menu menu = manager.createContextMenu(viewer.getTextWidget());
IEditorSite site = getEditorSite();
site.setSelectionProvider(viewer);
site.registerContextMenu(
AbstractTextEditor.COMMON_EDITOR_CONTEXT_MENU_ID, manager,
viewer, true);
site.registerContextMenu(
AbstractTextEditor.DEFAULT_EDITOR_CONTEXT_MENU_ID, manager,
viewer, true);
viewer.getTextWidget().setMenu(menu);
formatDiff();
}
}