Package org.eclipse.egit.ui.internal.history

Source Code of org.eclipse.egit.ui.internal.history.CommitMessageViewer$ObjectLink

/*******************************************************************************
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2011, Jens Baumgart <jens.baumgart@sap.com>
* Copyright (C) 2011, Stefan Lay <stefan.lay@sap.com>
* Copyright (C) 2014, Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
*
* 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.history;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.actions.BooleanPrefAction;
import org.eclipse.egit.ui.internal.history.FormatJob.FormatResult;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.DefaultTextDoubleClickStrategy;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revplot.PlotCommit;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;

class CommitMessageViewer extends SourceViewer {

  private static final Color SYS_LINKCOLOR = PlatformUI.getWorkbench()
      .getDisplay().getSystemColor(SWT.COLOR_BLUE);

  private static final Color SYS_DARKGRAY = PlatformUI.getWorkbench()
      .getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);

  private static final Cursor SYS_LINK_CURSOR = PlatformUI.getWorkbench()
      .getDisplay().getSystemCursor(SWT.CURSOR_HAND);

  private final Cursor sys_normalCursor;

  // notified when clicking on a link in the message (branch, commit...)
  private final ListenerList navListeners = new ListenerList();

  // listener to detect changes in the wrap and fill preferences
  private final IPropertyChangeListener listener;

  // the current repository
  private Repository db;

  // the "input" (set by setInput())
  private PlotCommit<?> commit;

  // formatting option to fill the lines
  private boolean fill;

  private FormatJob formatJob;

  private final IWorkbenchPartSite partSite;

  private List<Ref> allRefs;

  private ListenerHandle refsChangedListener;

  private BooleanPrefAction showTagSequencePrefAction;

  private BooleanPrefAction wrapCommentsPrefAction;

  private BooleanPrefAction fillParagraphsPrefAction;

  CommitMessageViewer(final Composite parent, final IPageSite site, IWorkbenchPartSite partSite) {
    super(parent, null, SWT.READ_ONLY);
    this.partSite = partSite;

    final StyledText t = getTextWidget();
    t.setFont(UIUtils.getFont(UIPreferences.THEME_CommitMessageFont));

    sys_normalCursor = t.getCursor();

    // set the cursor when hovering over a link
    t.addListener(SWT.MouseMove, new Listener() {
      public void handleEvent(final Event e) {
        StyleRange styleRange = getStyleRange(e.x, e.y);
        if (styleRange != null && styleRange.underline)
          t.setCursor(SYS_LINK_CURSOR);
        else
          t.setCursor(sys_normalCursor);
      }
    });
    // react on link click
    t.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseDown(final MouseEvent e) {
        // only process the hyper link if it was a primary mouse click
        if (e.button != 1)
          return;

        final StyleRange r = getStyleRange(e.x, e.y);
        if (r instanceof ObjectLink) {
          final RevCommit c = ((ObjectLink) r).targetCommit;
          for (final Object l : navListeners.getListeners())
            ((CommitNavigationListener) l).showCommit(c);
        }
      }
    });
    setTextDoubleClickStrategy(new DefaultTextDoubleClickStrategy(),
        IDocument.DEFAULT_CONTENT_TYPE);
    activatePlugins();

    // react on changes in the fill and wrap preferences
    listener = new IPropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent event) {
        if (event.getProperty().equals(
            UIPreferences.RESOURCEHISTORY_SHOW_COMMENT_FILL)) {
          setFill(((Boolean) event.getNewValue()).booleanValue());
          return;
        }
        if (event.getProperty().equals(UIPreferences.HISTORY_SHOW_TAG_SEQUENCE)) {
          format();
          return;
        }
      }
    };

    IPreferenceStore store = Activator.getDefault().getPreferenceStore();
    store.addPropertyChangeListener(listener);
    fill = store
        .getBoolean(UIPreferences.RESOURCEHISTORY_SHOW_COMMENT_FILL);

    // global action handlers for select all and copy
    final IAction selectAll = new Action() {
      @Override
      public void run() {
        doOperation(ITextOperationTarget.SELECT_ALL);
      }

      @Override
      public boolean isEnabled() {
        return canDoOperation(ITextOperationTarget.SELECT_ALL);
      }
    };

    final IAction copy = new Action() {
      @Override
      public void run() {
        doOperation(ITextOperationTarget.COPY);
      }

      @Override
      public boolean isEnabled() {
        return canDoOperation(ITextOperationTarget.COPY);
      }
    };
    // register and unregister the global actions upon focus events
    getControl().addFocusListener(new FocusListener() {
      public void focusLost(FocusEvent e) {
        site.getActionBars().setGlobalActionHandler(
            ActionFactory.SELECT_ALL.getId(), null);
        site.getActionBars().setGlobalActionHandler(
            ActionFactory.COPY.getId(), null);
        site.getActionBars().updateActionBars();
      }

      public void focusGained(FocusEvent e) {
        site.getActionBars().setGlobalActionHandler(
            ActionFactory.SELECT_ALL.getId(), selectAll);
        site.getActionBars().setGlobalActionHandler(
            ActionFactory.COPY.getId(), copy);
        site.getActionBars().updateActionBars();
      }
    });

    final MenuManager mgr = new MenuManager();
    Control c = getControl();
    c.setMenu(mgr.createContextMenu(c));

    IPersistentPreferenceStore pstore = (IPersistentPreferenceStore) store;

    showTagSequencePrefAction = new BooleanPrefAction(pstore,
        UIPreferences.HISTORY_SHOW_TAG_SEQUENCE,
        UIText.ResourceHistory_ShowTagSequence) {
      @Override
      protected void apply(boolean value) {
        // nothing, just toggle
      }
    };
    mgr.add(showTagSequencePrefAction);

    wrapCommentsPrefAction = new BooleanPrefAction(pstore,
        UIPreferences.RESOURCEHISTORY_SHOW_COMMENT_WRAP,
        UIText.ResourceHistory_toggleCommentWrap) {
      @Override
      protected void apply(boolean value) {
        // nothing, just toggle
      }
    };
    mgr.add(wrapCommentsPrefAction);

    fillParagraphsPrefAction = new BooleanPrefAction(pstore,
        UIPreferences.RESOURCEHISTORY_SHOW_COMMENT_FILL,
        UIText.ResourceHistory_toggleCommentFill) {
      @Override
      protected void apply(boolean value) {
        // nothing, just toggle
      }
    };
    mgr.add(fillParagraphsPrefAction);

  }

  void addDoneListenerToFormatJob() {
    formatJob.addJobChangeListener(new JobChangeAdapter() {

      public void done(IJobChangeEvent event) {
        if (!event.getResult().isOK())
          return;
        final StyledText text = getTextWidget();
        if (text == null || text.isDisposed())
          return;
        final FormatJob job = (FormatJob) event.getJob();
        text.getDisplay().asyncExec(new Runnable() {
          public void run() {
            applyFormatJobResultInUI(job.getFormatResult());
          }
        });
      }
    });
  }

  @Override
  protected void handleDispose() {
    if (formatJob != null) {
      formatJob.cancel();
      formatJob = null;
    }
    Activator.getDefault().getPreferenceStore()
        .removePropertyChangeListener(listener);
    if (refsChangedListener != null)
      refsChangedListener.remove();
    refsChangedListener = null;
    showTagSequencePrefAction.dispose();
    wrapCommentsPrefAction.dispose();
    fillParagraphsPrefAction.dispose();

    super.handleDispose();
  }

  void addCommitNavigationListener(final CommitNavigationListener l) {
    navListeners.add(l);
  }

  void removeCommitNavigationListener(final CommitNavigationListener l) {
    navListeners.remove(l);
  }

  @Override
  public void setInput(final Object input) {
    // right-clicking on a commit will fire selection change events,
    // so we only rebuild this when the commit did in fact change
    if (input == commit)
      return;
    commit = (PlotCommit<?>) input;
    if (refsChangedListener != null) {
      refsChangedListener.remove();
      refsChangedListener = null;
    }

    if (db != null) {
      allRefs = getBranches(db);
      refsChangedListener = db.getListenerList().addRefsChangedListener(
          new RefsChangedListener() {

            public void onRefsChanged(RefsChangedEvent event) {
              allRefs = getBranches(db);
            }
          });
    }
    format();
  }

  public Object getInput() {
    return commit;
  }

  void setRepository(final Repository repository) {
    this.db = repository;
  }

  private static List<Ref> getBranches(Repository repo)  {
    List<Ref> ref = new ArrayList<Ref>();
    try {
      RefDatabase refDb = repo.getRefDatabase();
      ref.addAll(refDb.getRefs(Constants.R_HEADS).values());
      ref.addAll(refDb.getRefs(Constants.R_REMOTES).values());
    } catch (IOException e) {
      Activator.logError(e.getMessage(), e);
    }
    return ref;
  }

  private Repository getRepository() {
    if (db == null)
      throw new IllegalStateException("Repository has not been set"); //$NON-NLS-1$
    return db;
  }

  private void format() {
    if (db == null || commit == null) {
      setDocument(new Document("")); //$NON-NLS-1$
      return;
    }
    if (formatJob != null && formatJob.getState() != Job.NONE)
      formatJob.cancel();
    scheduleFormatJob();
  }

  private void scheduleFormatJob() {
    IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService) partSite
        .getAdapter(IWorkbenchSiteProgressService.class);
    if (siteService == null)
      return;
    FormatJob.FormatRequest formatRequest = new FormatJob.FormatRequest(
        getRepository(), commit, fill, SYS_LINKCOLOR, SYS_DARKGRAY,
        allRefs);
    formatJob = new FormatJob(formatRequest);
    addDoneListenerToFormatJob();
    siteService.schedule(formatJob, 0 /* now */, true /*
                             * use the half-busy
                             * cursor in the part
                             */);
  }

  private void applyFormatJobResultInUI(FormatResult formatResult) {
    StyledText text = getTextWidget();
    if (!UIUtils.isUsable(text))
      return;

    setDocument(new Document(formatResult.getCommitInfo()));

    // Set style ranges from format job. We know that they are already
    // ordered and don't overlap.
    text.setStyleRanges(formatResult.getStyleRange());

    StyleRange[] hyperlinkStyleRanges = UIUtils
        .getHyperlinkDetectorStyleRanges(CommitMessageViewer.this,
            fHyperlinkDetectors);

    // Apply hyperlink style ranges one by one. setStyleRange takes care to
    // do the right thing in case they overlap with an existing style range.
    // If we combined them with the above style ranges and set them all at
    // once, we would have to manually remove overlapping ones.
    for (StyleRange styleRange : hyperlinkStyleRanges)
      text.setStyleRange(styleRange);
  }

  static final class ObjectLink extends StyleRange {
    RevCommit targetCommit;

    public boolean similarTo(final StyleRange style) {
      if (!(style instanceof ObjectLink))
        return false;
      if (targetCommit != ((ObjectLink) style).targetCommit)
        return false;
      return super.similarTo(style);
    }

    @Override
    public boolean equals(Object object) {
      return super.equals(object)
          && targetCommit.equals(((ObjectLink) object).targetCommit);
    }

    @Override
    public int hashCode() {
      return super.hashCode() ^ targetCommit.hashCode();
    }
  }

  private void setFill(boolean fill) {
    this.fill = fill;
    format();
  }

  /**
   * Get style range at x/y coordinates
   *
   * @param x
   * @param y
   * @return style range, will be null when no style range exists at given
   *         coordinates
   */
  private StyleRange getStyleRange(final int x, final int y) {
    final StyledText t = getTextWidget();
    final int offset;
    try {
      offset = t.getOffsetAtLocation(new Point(x, y));
    } catch (IllegalArgumentException e) {
      return null;
    }
    if (offset < t.getCharCount())
      return t.getStyleRangeAtOffset(offset);
    else
      return null;
  }

}
TOP

Related Classes of org.eclipse.egit.ui.internal.history.CommitMessageViewer$ObjectLink

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.