Package org.eclipse.egit.ui.internal.blame

Source Code of org.eclipse.egit.ui.internal.blame.BlameInformationControl

/******************************************************************************
*  Copyright (c) 2011, 2014 GitHub Inc 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:
*    Kevin Sawicki (GitHub Inc.) - initial API and implementation
*    Marc-Andre Laperle (Ericsson) - Set the input to null when not visible
*****************************************************************************/
package org.eclipse.egit.ui.internal.blame;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;

import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.egit.core.internal.job.JobUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.CompareUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.blame.BlameOperation.BlameHistoryPageInput;
import org.eclipse.egit.ui.internal.blame.BlameRevision.Diff;
import org.eclipse.egit.ui.internal.commit.CommitEditor;
import org.eclipse.egit.ui.internal.commit.DiffStyleRangeFormatter;
import org.eclipse.egit.ui.internal.commit.DiffViewer;
import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
import org.eclipse.egit.ui.internal.history.HistoryPageInput;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.AbstractInformationControl;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.ui.history.IHistoryView;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

/**
* Annotation information control
*/
public class BlameInformationControl extends AbstractInformationControl
    implements IInformationControlExtension2 {

  private IInformationControlCreator creator;

  private IVerticalRulerInfo rulerInfo;

  private BlameInformationControl hoverInformationControl;

  private BlameRevision revision;

  private ScrolledComposite scrolls;

  private Composite displayArea;

  private Label commitLabel;

  private Label authorLabel;

  private Label committerLabel;

  private StyledText messageText;

  /**
   * 0-based line number
   */
  private int revisionRulerLineNumber;

  private Composite diffComposite;

  private Link showAnnotationsLink;

  private SelectionAdapter showAnnotationsLinkSelectionAdapter;

  /**
   * Create the information control for showing details on hover.
   *
   * @param parentShell
   * @param creator
   * @param rulerInfo
   */
  public BlameInformationControl(Shell parentShell,
      IInformationControlCreator creator, IVerticalRulerInfo rulerInfo) {
    super(parentShell, false);
    this.creator = creator;
    this.rulerInfo = rulerInfo;
    create();
  }

  /**
   * Create the enriched information control that is shown when moving the
   * mouse over the control that was shown on hover (making it sticky).
   *
   * @param parentShell
   * @param hoverInformationControl
   *            the control that was used on hover (used for getting the
   *            correct line that was hovered)
   */
  public BlameInformationControl(Shell parentShell,
      BlameInformationControl hoverInformationControl) {
    // Make resizable and have a bottom bar for moving around
    super(parentShell, new ToolBarManager());
    this.hoverInformationControl = hoverInformationControl;
    create();
  }

  public IInformationControlCreator getInformationPresenterControlCreator() {
    return this.creator;
  }

  public boolean hasContents() {
    return true;
  }

  protected void createContent(Composite parent) {
    scrolls = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL);
    scrolls.setExpandHorizontal(true);
    scrolls.setExpandVertical(true);
    displayArea = new Composite(scrolls, SWT.NONE);
    scrolls.setContent(displayArea);
    displayArea.setForeground(parent.getForeground());
    displayArea.setBackground(parent.getBackground());
    displayArea.setBackgroundMode(SWT.INHERIT_FORCE);
    GridLayoutFactory.swtDefaults().equalWidth(true).applyTo(displayArea);

    Composite commitHeader = new Composite(displayArea, SWT.NONE);
    commitHeader.setLayout(GridLayoutFactory.fillDefaults().numColumns(3)
        .create());
    GridDataFactory.fillDefaults().grab(true, false).applyTo(commitHeader);

    commitLabel = new Label(commitHeader, SWT.READ_ONLY);
    commitLabel.setFont(JFaceResources.getBannerFont());

    Link openCommitLink = new Link(commitHeader, SWT.NONE);
    openCommitLink.setText(UIText.BlameInformationControl_OpenCommitLink);
    openCommitLink.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        openCommit();
      }
    });

    Link showInHistoryLink = new Link(commitHeader, SWT.NONE);
    showInHistoryLink.setText(UIText.BlameInformationControl_ShowInHistoryLink);
    showInHistoryLink.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        showCommitInHistory();
      }
    });

    authorLabel = new Label(displayArea, SWT.NONE);
    authorLabel.setForeground(parent.getForeground());
    authorLabel.setBackground(parent.getBackground());
    authorLabel.setFont(UIUtils.getItalicFont(JFaceResources.DEFAULT_FONT));
    GridDataFactory.fillDefaults().grab(true, false).applyTo(authorLabel);

    committerLabel = new Label(displayArea, SWT.NONE);
    committerLabel.setForeground(parent.getForeground());
    committerLabel.setBackground(parent.getBackground());
    committerLabel.setFont(UIUtils
        .getItalicFont(JFaceResources.DEFAULT_FONT));
    GridDataFactory.fillDefaults().grab(true, false)
        .applyTo(committerLabel);

    Label separator = new Label(displayArea, SWT.HORIZONTAL | SWT.SEPARATOR);
    GridDataFactory.fillDefaults().grab(true, false).applyTo(separator);

    messageText = new StyledText(displayArea, SWT.NONE);
    messageText.setForeground(parent.getForeground());
    messageText.setBackground(parent.getBackground());
    messageText.setEditable(false);
    messageText.setFont(UIUtils
        .getFont(UIPreferences.THEME_CommitMessageFont));
    GridDataFactory.fillDefaults().grab(true, true).applyTo(messageText);
  }

  public Point computeSizeHint() {
    Point computed = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);

    Point constraints = getSizeConstraints();
    if (constraints == null)
      return computed;

    Point constrainedSize = getShell().computeSize(constraints.x,
        SWT.DEFAULT, true);
    int width = Math.min(computed.x, constrainedSize.x);
    int height = Math.max(computed.y, constrainedSize.y);
    return new Point(width, height);
  }

  private void setControlVisible(Control control, boolean visible) {
    control.setVisible(visible);
    ((GridData) control.getLayoutData()).exclude = !visible;
  }

  public void setInput(Object input) {
    if (input == null) {
      // Make sure we don't hold a reference to this when nothing is
      // shown, it can be big
      this.revision = null;
      if (showAnnotationsLink != null) {
        // This listener can also hold a reference because of a final
        // parameter, see createDiffLinkAndText(final RevCommit
        // parent...)
        if (!showAnnotationsLink.isDisposed())
          showAnnotationsLink
              .removeSelectionListener(showAnnotationsLinkSelectionAdapter);
        showAnnotationsLink = null;
        showAnnotationsLinkSelectionAdapter = null;
      }
      return;
    }

    this.revision = (BlameRevision) input;

    // Remember line number that was hovered over when the input was set.
    // Used for showing the diff hunk of this line instead of the full diff.
    if (rulerInfo != null)
      revisionRulerLineNumber = rulerInfo
          .getLineOfLastMouseButtonActivity();

    RevCommit commit = this.revision.getCommit();

    String linkText = MessageFormat.format(
        UIText.BlameInformationControl_Commit, commit.abbreviate(7)
            .name());
    commitLabel.setText(linkText);

    PersonIdent author = commit.getAuthorIdent();
    if (author != null) {
      setControlVisible(authorLabel, true);
      authorLabel.setText(MessageFormat.format(
          UIText.BlameInformationControl_Author, author.getName(),
          author.getEmailAddress(), author.getWhen()));
    } else
      setControlVisible(authorLabel, false);

    PersonIdent committer = commit.getCommitterIdent();
    setControlVisible(authorLabel, author != null);
    if (committer != null && !committer.equals(author)) {
      setControlVisible(committerLabel, true);
      committerLabel.setText(MessageFormat.format(
          UIText.BlameInformationControl_Committer,
          committer.getName(), committer.getEmailAddress(),
          committer.getWhen()));
    } else
      setControlVisible(committerLabel, false);

    messageText.setText(commit.getFullMessage());

    createDiffs();

    displayArea.layout();
    scrolls.setMinSize(displayArea.computeSize(SWT.DEFAULT, SWT.DEFAULT));
  }

  private void createDiffs() {
    if (diffComposite != null)
      diffComposite.dispose();

    RevCommit commit = revision.getCommit();
    if (commit.getParentCount() == 0)
      return;

    createDiffComposite();

    for (int i = 0; i < commit.getParentCount(); i++) {
      RevCommit parent = commit.getParent(i);
      createDiff(parent);
    }
  }

  private void createDiffComposite() {
    diffComposite = new Composite(displayArea, SWT.NONE);
    diffComposite.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, true).create());
    diffComposite.setLayout(GridLayoutFactory.fillDefaults().create());
  }

  private void createDiff(RevCommit parent) {
    Diff diff = revision.getDiffToParent(parent);
    if (diff != null) {
      try {
        createDiffLinkAndText(parent, diff);
      } catch (IOException e) {
        String msg = "Error creating diff in blame information control for commit " //$NON-NLS-1$
            + parent.toObjectId();
        Activator.logError(msg, e);
      }
    }
  }

  private void createDiffLinkAndText(final RevCommit parent, final Diff diff)
      throws IOException {
    String parentId = parent.toObjectId().abbreviate(7).name();
    String parentMessage = parent.getShortMessage();

    EditList interestingDiff = getInterestingDiff(diff.getEditList());

    final Integer parentLine;
    if (!interestingDiff.isEmpty())
      parentLine = Integer.valueOf(interestingDiff.get(0).getBeginA());
    else
      parentLine = null;

    Composite header = new Composite(diffComposite, SWT.NONE);
    header.setLayout(GridLayoutFactory.fillDefaults().numColumns(2)
        .create());

    Label diffHeaderLabel = new Label(header, SWT.NONE);
    diffHeaderLabel.setText(NLS.bind(
        UIText.BlameInformationControl_DiffHeaderLabel, parentId,
        parentMessage));

    showAnnotationsLink = new Link(header, SWT.NONE);
    showAnnotationsLink
        .setText(UIText.BlameInformationControl_ShowAnnotationsLink);
    showAnnotationsLinkSelectionAdapter = new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        blameParent(parent, diff, parentLine);
      }
    };
    showAnnotationsLink
        .addSelectionListener(showAnnotationsLinkSelectionAdapter);

    DiffViewer diffText = new DiffViewer(diffComposite, null, SWT.NONE,
        false);
    diffText.setEditable(false);
    diffText.getControl().setLayoutData(
        GridDataFactory.fillDefaults().grab(true, true).create());

    IDocument document = new Document();
    DiffStyleRangeFormatter diffFormatter = new DiffStyleRangeFormatter(
        document);
    diffFormatter.setContext(1);
    diffFormatter.setRepository(revision.getRepository());
    diffFormatter.format(interestingDiff, diff.getOldText(),
        diff.getNewText());

    diffText.setDocument(document);
    diffText.setFormatter(diffFormatter);
  }

  private EditList getInterestingDiff(EditList fullDiff) {
    int hoverLineNumber = getHoverLineNumber();
    Integer sourceLine = revision.getSourceLine(hoverLineNumber);

    if (sourceLine == null)
      // Fall back to whole diff
      return fullDiff;

    int line = sourceLine.intValue();
    EditList interestingDiff = new EditList(1);
    for (Edit edit : fullDiff) {
      if (line >= edit.getBeginB() && line <= edit.getEndB())
        interestingDiff.add(edit);
    }
    return interestingDiff;
  }

  /**
   * @return 0-based line number of hover
   */
  private int getHoverLineNumber() {
    // If this is the enriched control, we have to use the line number of
    // the original hover control, otherwise the line number may have
    // changed if the mouse moved over another area.
    if (hoverInformationControl != null)
      return hoverInformationControl.getHoverLineNumber();
    else
      return revisionRulerLineNumber;
  }

  private void openCommit() {
    try {
      getShell().dispose();
      CommitEditor.open(new RepositoryCommit(revision.getRepository(),
          revision.getCommit()));
    } catch (PartInitException pie) {
      Activator.logError(pie.getLocalizedMessage(), pie);
    }
  }

  private void showCommitInHistory() {
    getShell().dispose();
    IHistoryView part;
    try {
      part = (IHistoryView) PlatformUI.getWorkbench()
          .getActiveWorkbenchWindow().getActivePage()
          .showView(IHistoryView.VIEW_ID);
    } catch (PartInitException e) {
      Activator.logError(e.getLocalizedMessage(), e);
      return;
    }

    if (part == null)
      return;

    Repository repository = revision.getRepository();
    if (!repository.isBare()) {
      String sourcePath = revision.getSourcePath();
      File file = new File(repository.getWorkTree(), sourcePath);
      BlameHistoryPageInput input = new BlameHistoryPageInput(repository,
          revision.getCommit(), file);
      part.showHistoryFor(input);
    } else {
      HistoryPageInput input = new BlameHistoryPageInput(repository,
          revision.getCommit());
      part.showHistoryFor(input);
    }
  }

  private void blameParent(RevCommit parent, Diff diff, Integer sourceLine) {
    try {
      String path = diff.getOldPath();
      IFileRevision rev = CompareUtils.getFileRevision(path, parent,
          revision.getRepository(), null);
      int line = sourceLine == null ? -1 : sourceLine.intValue();
      IStorage storage = rev.getStorage(new NullProgressMonitor());
      IWorkbenchPage page = PlatformUI.getWorkbench()
          .getActiveWorkbenchWindow().getActivePage();
      BlameOperation operation = new BlameOperation(
          revision.getRepository(), storage, path, parent,
          getShell(), page, line);
      JobUtil.scheduleUserJob(operation, UIText.ShowBlameHandler_JobName,
          JobFamilies.BLAME);
    } catch (IOException e) {
      Activator.logError(UIText.ShowBlameHandler_errorMessage, e);
    } catch (CoreException e) {
      Activator.logError(UIText.ShowBlameHandler_errorMessage, e);
    }
  }

  public void setVisible(boolean visible) {
    super.setVisible(visible);
    if (!visible)
      setInput(null);
  }
}
TOP

Related Classes of org.eclipse.egit.ui.internal.blame.BlameInformationControl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.