Package org.eclipse.egit.ui.internal.commit

Source Code of org.eclipse.egit.ui.internal.commit.CommitEditorPage

/*******************************************************************************
*  Copyright (c) 2011, 2012 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.commit;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.GitLabelProvider;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.dialogs.SpellcheckableMessageArea;
import org.eclipse.egit.ui.internal.history.CommitFileDiffViewer;
import org.eclipse.egit.ui.internal.history.FileDiff;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.RevWalkUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
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.Text;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.part.ShowInContext;

/**
* Commit editor page class displaying author, committer, parent commits,
* message, and file information in form sections.
*/
public class CommitEditorPage extends FormPage implements ISchedulingRule {

  private static final String SIGNED_OFF_BY = "Signed-off-by: {0} <{1}>"; //$NON-NLS-1$

  /**
   * Abbreviated length of parent id links displayed
   */
  public static final int PARENT_LENGTH = 20;

  private LocalResourceManager resources = new LocalResourceManager(
      JFaceResources.getResources());

  private Composite tagLabelArea;

  private Section branchSection;

  private TableViewer branchViewer;

  private Section diffSection;

  private CommitFileDiffViewer diffViewer;

  /**
   * Create commit editor page
   *
   * @param editor
   */
  public CommitEditorPage(FormEditor editor) {
    this(editor, "commitPage", UIText.CommitEditorPage_Title); //$NON-NLS-1$
  }

  /**
   * Create commit editor page
   *
   * @param editor
   * @param id
   * @param title
   */
  public CommitEditorPage(FormEditor editor, String id, String title) {
    super(editor, id, title);
  }

  private void hookExpansionGrabbing(final Section section) {
    section.addExpansionListener(new ExpansionAdapter() {

      public void expansionStateChanged(ExpansionEvent e) {
        ((GridData) section.getLayoutData()).grabExcessVerticalSpace = e
            .getState();
        getManagedForm().getForm().getBody().layout(true, true);
      }
    });
  }

  private Image getImage(ImageDescriptor descriptor) {
    return (Image) this.resources.get(descriptor);
  }

  Section createSection(Composite parent, FormToolkit toolkit,
      int span) {
    Section section = toolkit.createSection(parent,
        ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE
            | ExpandableComposite.EXPANDED);
    GridDataFactory.fillDefaults().span(span, 1).grab(true, true)
        .applyTo(section);
    return section;
  }

  Composite createSectionClient(Section parent, FormToolkit toolkit) {
    Composite client = toolkit.createComposite(parent);
    GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2)
        .applyTo(client);
    return client;
  }

  private boolean isSignedOffBy(PersonIdent person) {
    RevCommit commit = getCommit().getRevCommit();
    return commit.getFullMessage().indexOf(getSignedOffByLine(person)) != -1;
  }

  private String getSignedOffByLine(PersonIdent person) {
    return MessageFormat.format(SIGNED_OFF_BY, person.getName(),
        person.getEmailAddress());
  }

  private String replaceSignedOffByLine(String message, PersonIdent person) {
    Pattern pattern = Pattern.compile(
        "^\\s*" + Pattern.quote(getSignedOffByLine(person)) //$NON-NLS-1$
            + "\\s*$", Pattern.MULTILINE); //$NON-NLS-1$
    return pattern.matcher(message).replaceAll(""); //$NON-NLS-1$
  }

  private Composite createUserArea(Composite parent, FormToolkit toolkit,
      PersonIdent person, boolean author) {
    Composite userArea = toolkit.createComposite(parent);
    GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(3)
        .applyTo(userArea);

    Label userLabel = toolkit.createLabel(userArea, null);
    userLabel.setImage(getImage(author ? UIIcons.ELCL16_AUTHOR
        : UIIcons.ELCL16_COMMITTER));
    if (author)
      userLabel.setToolTipText(UIText.CommitEditorPage_TooltipAuthor);
    else
      userLabel.setToolTipText(UIText.CommitEditorPage_TooltipCommitter);

    boolean signedOff = isSignedOffBy(person);

    Text userText = new Text(userArea, SWT.FLAT | SWT.READ_ONLY);
    userText.setText(MessageFormat.format(
        author ? UIText.CommitEditorPage_LabelAuthor
            : UIText.CommitEditorPage_LabelCommitter, person
            .getName(), person.getEmailAddress(), person.getWhen()));
    toolkit.adapt(userText, false, false);
    userText.setData(FormToolkit.KEY_DRAW_BORDER, Boolean.FALSE);

    GridDataFactory.fillDefaults().span(signedOff ? 1 : 2, 1)
        .applyTo(userText);
    if (signedOff) {
      Label signedOffLabel = toolkit.createLabel(userArea, null);
      signedOffLabel.setImage(getImage(UIIcons.SIGNED_OFF));
      if (author)
        signedOffLabel
            .setToolTipText(UIText.CommitEditorPage_TooltipSignedOffByAuthor);
      else
        signedOffLabel
            .setToolTipText(UIText.CommitEditorPage_TooltipSignedOffByCommitter);
    }

    return userArea;
  }

  void updateSectionClient(Section section, Composite client,
      FormToolkit toolkit) {
    hookExpansionGrabbing(section);
    toolkit.paintBordersFor(client);
    section.setClient(client);
  }

  private void createHeaderArea(Composite parent, FormToolkit toolkit,
      int span) {
    RevCommit commit = getCommit().getRevCommit();
    Composite top = toolkit.createComposite(parent);
    GridDataFactory.fillDefaults().grab(true, false).span(span, 1)
        .applyTo(top);
    GridLayoutFactory.fillDefaults().numColumns(2).applyTo(top);

    Composite userArea = toolkit.createComposite(top);
    GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(1)
        .applyTo(userArea);
    GridDataFactory.fillDefaults().grab(true, false).applyTo(userArea);

    PersonIdent author = commit.getAuthorIdent();
    if (author != null)
      createUserArea(userArea, toolkit, author, true);

    PersonIdent committer = commit.getCommitterIdent();
    if (committer != null && !committer.equals(author))
      createUserArea(userArea, toolkit, committer, false);

    int count = commit.getParentCount();
    if (count > 0)
      createParentsArea(top, toolkit, commit);

    createTagsArea(userArea, toolkit, 2);
  }

  private void createParentsArea(Composite parent, FormToolkit toolkit,
      RevCommit commit) {
    Composite parents = toolkit.createComposite(parent);
    GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(2)
        .applyTo(parents);
    GridDataFactory.fillDefaults().grab(false, false).applyTo(parents);

    for (int i = 0; i < commit.getParentCount(); i++) {
      final RevCommit parentCommit = commit.getParent(i);
      toolkit.createLabel(parents, getParentCommitLabel(i))
          .setForeground(
              toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
      final Hyperlink link = toolkit
          .createHyperlink(parents,
              parentCommit.abbreviate(PARENT_LENGTH).name(),
              SWT.NONE);
      link.addHyperlinkListener(new HyperlinkAdapter() {
        public void linkActivated(HyperlinkEvent e) {
          try {
            CommitEditor.open(new RepositoryCommit(getCommit()
                .getRepository(), parentCommit));
            if ((e.getStateMask() & SWT.MOD1) != 0)
              getEditor().close(false);
          } catch (PartInitException e1) {
            Activator.logError(
                "Error opening commit editor", e1);//$NON-NLS-1$
          }
        }
      });
    }
  }

  @SuppressWarnings("unused")
  String getParentCommitLabel(int i) {
    return UIText.CommitEditorPage_LabelParent;
  }

  private List<Ref> getTags() {
    Repository repository = getCommit().getRepository();
    List<Ref> tags = new ArrayList<Ref>(repository.getTags().values());
    Collections.sort(tags, new Comparator<Ref>() {

      public int compare(Ref r1, Ref r2) {
        return Repository.shortenRefName(r1.getName())
            .compareToIgnoreCase(
                Repository.shortenRefName(r2.getName()));
      }
    });
    return tags;
  }

  void createTagsArea(Composite parent, FormToolkit toolkit,
      int span) {
    Composite tagArea = toolkit.createComposite(parent);
    GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false)
        .applyTo(tagArea);
    GridDataFactory.fillDefaults().span(span, 1).grab(true, false)
        .applyTo(tagArea);
    toolkit.createLabel(tagArea, UIText.CommitEditorPage_LabelTags)
        .setForeground(
            toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
    tagLabelArea = toolkit.createComposite(tagArea);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(tagLabelArea);
    GridLayoutFactory.fillDefaults().spacing(1, 1).applyTo(tagLabelArea);
  }

  void fillDiffs(FileDiff[] diffs) {
    diffViewer.setInput(diffs);
    diffSection.setText(getDiffSectionTitle(Integer.valueOf(diffs.length)));
    setSectionExpanded(diffSection, diffs.length != 0);
  }

  static void setSectionExpanded(Section section, boolean expanded) {
    section.setExpanded(expanded);
    ((GridData) section.getLayoutData()).grabExcessVerticalSpace = expanded;
  }

  String getDiffSectionTitle(Integer numChanges) {
    return MessageFormat.format(UIText.CommitEditorPage_SectionFiles,
        numChanges);
  }

  void fillTags(FormToolkit toolkit, List<Ref> tags) {
    for (Control child : tagLabelArea.getChildren())
      child.dispose();

    // Hide "Tags" area if no tags to show
    ((GridData) tagLabelArea.getParent().getLayoutData()).exclude = tags
        .isEmpty();

    GridLayoutFactory.fillDefaults().spacing(1, 1).numColumns(tags.size())
        .applyTo(tagLabelArea);

    for (Ref tag : tags) {
      ObjectId id = tag.getPeeledObjectId();
      boolean annotated = id != null;
      if (id == null)
        id = tag.getObjectId();
      CLabel tagLabel = new CLabel(tagLabelArea, SWT.NONE);
      toolkit.adapt(tagLabel, false, false);
      if (annotated)
        tagLabel.setImage(getImage(UIIcons.TAG_ANNOTATED));
      else
        tagLabel.setImage(getImage(UIIcons.TAG));
      tagLabel.setText(Repository.shortenRefName(tag.getName()));
    }
  }

  private void createMessageArea(Composite parent, FormToolkit toolkit,
      int span) {
    Section messageSection = createSection(parent, toolkit, span);
    Composite messageArea = createSectionClient(messageSection, toolkit);

    messageSection.setText(UIText.CommitEditorPage_SectionMessage);

    RevCommit commit = getCommit().getRevCommit();
    String message = commit.getFullMessage();

    PersonIdent author = commit.getAuthorIdent();
    if (author != null)
      message = replaceSignedOffByLine(message, author);
    PersonIdent committer = commit.getCommitterIdent();
    if (committer != null)
      message = replaceSignedOffByLine(message, committer);

    SpellcheckableMessageArea textContent = new SpellcheckableMessageArea(
        messageArea, message, true, toolkit.getBorderStyle()) {

      @Override
      protected IAdaptable getDefaultTarget() {
        return new PlatformObject() {
          public Object getAdapter(Class adapter) {
            return Platform.getAdapterManager().getAdapter(
                getEditorInput(), adapter);
          }
        };
      }

      protected void createMarginPainter() {
        // Disabled intentionally
      }

    };

    if ((toolkit.getBorderStyle() & SWT.BORDER) == 0)
      textContent.setData(FormToolkit.KEY_DRAW_BORDER,
          FormToolkit.TEXT_BORDER);

    Point size = textContent.getTextWidget().computeSize(SWT.DEFAULT,
        SWT.DEFAULT);
    int yHint = size.y > 80 ? 80 : SWT.DEFAULT;
    GridDataFactory.fillDefaults().hint(SWT.DEFAULT, yHint).minSize(1, 20)
        .grab(true, true).applyTo(textContent);

    updateSectionClient(messageSection, messageArea, toolkit);
  }

  private void createBranchesArea(Composite parent, FormToolkit toolkit,
      int span) {
    branchSection = createSection(parent, toolkit, span);
    branchSection.setText(UIText.CommitEditorPage_SectionBranchesEmpty);
    Composite branchesArea = createSectionClient(branchSection, toolkit);

    branchViewer = new TableViewer(toolkit.createTable(branchesArea,
        SWT.V_SCROLL | SWT.H_SCROLL));
    GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 50)
        .applyTo(branchViewer.getControl());
    branchViewer.setSorter(new ViewerSorter());
    branchViewer.setLabelProvider(new GitLabelProvider() {

      public String getText(Object element) {
        return Repository.shortenRefName(super.getText(element));
      }

    });
    branchViewer.setContentProvider(ArrayContentProvider.getInstance());
    branchViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TREE_BORDER);

    updateSectionClient(branchSection, branchesArea, toolkit);
  }

  private void fillBranches(List<Ref> result) {
    branchViewer.setInput(result);
    branchSection.setText(MessageFormat.format(
        UIText.CommitEditorPage_SectionBranches,
        Integer.valueOf(result.size())));
  }

  void createDiffArea(Composite parent, FormToolkit toolkit, int span) {
    diffSection = createSection(parent, toolkit, span);
    diffSection.setText(UIText.CommitEditorPage_SectionFilesEmpty);
    Composite filesArea = createSectionClient(diffSection, toolkit);

    diffViewer = new CommitFileDiffViewer(filesArea, getSite(), SWT.MULTI
        | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION
        | toolkit.getBorderStyle());
    diffViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TREE_BORDER);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(diffViewer.getControl());
    diffViewer.setContentProvider(ArrayContentProvider.getInstance());
    diffViewer.setTreeWalk(getCommit().getRepository(), null);

    updateSectionClient(diffSection, filesArea, toolkit);
  }

  RepositoryCommit getCommit() {
    return (RepositoryCommit) getEditor()
        .getAdapter(RepositoryCommit.class);
  }

  /**
   * @see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm)
   */
  protected void createFormContent(IManagedForm managedForm) {
    Composite body = managedForm.getForm().getBody();
    body.addDisposeListener(new DisposeListener() {

      public void widgetDisposed(DisposeEvent e) {
        resources.dispose();
      }
    });
    FillLayout bodyLayout = new FillLayout();
    bodyLayout.marginHeight = 5;
    bodyLayout.marginWidth = 5;
    body.setLayout(bodyLayout);

    FormToolkit toolkit = managedForm.getToolkit();

    Composite displayArea = toolkit.createComposite(body);
    GridLayoutFactory.fillDefaults().numColumns(2).applyTo(displayArea);

    createHeaderArea(displayArea, toolkit, 2);
    createMessageArea(displayArea, toolkit, 2);
    createChangesArea(displayArea, toolkit);

    loadSections();
  }

  void createChangesArea(Composite displayArea, FormToolkit toolkit) {
    createDiffArea(displayArea, toolkit, 1);
    createBranchesArea(displayArea, toolkit, 1);
  }

  private List<Ref> loadTags() {
    RepositoryCommit repoCommit = getCommit();
    RevCommit commit = repoCommit.getRevCommit();
    Repository repository = repoCommit.getRepository();
    List<Ref> tags = new ArrayList<Ref>();
    for (Ref tag : getTags()) {
      tag = repository.peel(tag);
      ObjectId id = tag.getPeeledObjectId();
      if (id == null)
        id = tag.getObjectId();
      if (!commit.equals(id))
        continue;
      tags.add(tag);
    }
    return tags;
  }

  private List<Ref> loadBranches() {
    Repository repository = getCommit().getRepository();
    RevCommit commit = getCommit().getRevCommit();
    RevWalk revWalk = new RevWalk(repository);
    try {
      Map<String, Ref> refsMap = new HashMap<String, Ref>();
      refsMap.putAll(repository.getRefDatabase().getRefs(
          Constants.R_HEADS));
      refsMap.putAll(repository.getRefDatabase().getRefs(
          Constants.R_REMOTES));
      return RevWalkUtils.findBranchesReachableFrom(commit, revWalk, refsMap.values());
    } catch (IOException e) {
      Activator.handleError(e.getMessage(), e, false);
      return Collections.emptyList();
    }
  }

  void loadSections() {
    RepositoryCommit commit = getCommit();
    Job refreshJob = new Job(MessageFormat.format(
        UIText.CommitEditorPage_JobName, commit.getRevCommit().name())) {

      @Override
      protected IStatus run(IProgressMonitor monitor) {
        final List<Ref> tags = loadTags();
        final List<Ref> branches = loadBranches();
        final FileDiff[] diffs = getCommit().getDiffs();

        final ScrolledForm form = getManagedForm().getForm();
        if (UIUtils.isUsable(form))
          form.getDisplay().syncExec(new Runnable() {

            public void run() {
              if (!UIUtils.isUsable(form))
                return;

              fillTags(getManagedForm().getToolkit(), tags);
              fillDiffs(diffs);
              fillBranches(branches);
              form.reflow(true);
              form.layout(true, true);
            }
          });

        return Status.OK_STATUS;
      }
    };
    refreshJob.setRule(this);
    refreshJob.schedule();
  }

  /**
   * Refresh the editor page
   */
  public void refresh() {
    loadSections();
  }

  public boolean contains(ISchedulingRule rule) {
    return rule == this;
  }

  public boolean isConflicting(ISchedulingRule rule) {
    return rule == this;
  }

  ShowInContext getShowInContext() {
    if (diffViewer != null && diffViewer.getControl().isFocusControl())
      return diffViewer.getShowInContext();
    else
      return null;
  }

}
TOP

Related Classes of org.eclipse.egit.ui.internal.commit.CommitEditorPage

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.