Package org.eclipse.egit.ui.internal.dialogs

Source Code of org.eclipse.egit.ui.internal.dialogs.CommitDialog$CommitFileContentProvider

/*******************************************************************************
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com.dewire.com>
* Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2011, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
* Copyright (C) 2012, 2013 Robin Stocker <robin@nibor.org>
* Copyright (C) 2012, IBM Corporation (Markus Keller <markus_keller@ch.ibm.com>)
* Copyright (C) 2013, François Rey <eclipse.org_@_francois_._rey_._name>
*
* 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.dialogs;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.AdaptableFileTreeIterator;
import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.ui.Activator;
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.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.commit.CommitHelper;
import org.eclipse.egit.ui.internal.commit.CommitMessageHistory;
import org.eclipse.egit.ui.internal.commit.CommitProposalProcessor;
import org.eclipse.egit.ui.internal.components.CachedCheckboxTreeViewer;
import org.eclipse.egit.ui.internal.components.FilteredCheckboxTree;
import org.eclipse.egit.ui.internal.decorators.IProblemDecoratable;
import org.eclipse.egit.ui.internal.decorators.ProblemLabelDecorator;
import org.eclipse.egit.ui.internal.dialogs.CommitItem.Status;
import org.eclipse.egit.ui.internal.dialogs.CommitMessageComponent.CommitStatus;
import org.eclipse.egit.ui.internal.staging.StagingView;
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.MenuManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.IndexDiff;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.progress.WorkbenchJob;

/**
* Dialog is shown to user when they request to commit files. Changes in the
* selected portion of the tree are shown.
*/
public class CommitDialog extends TitleAreaDialog {

  private static IPreferenceStore getPreferenceStore() {
    return org.eclipse.egit.ui.Activator.getDefault().getPreferenceStore();
  }

  static class CommitFileContentProvider extends BaseWorkbenchContentProvider {
    @Override
    public Object[] getElements(Object element) {
      if (element instanceof Object[])
        return (Object[]) element;
      if (element instanceof Collection)
        return ((Collection) element).toArray();
      return new Object[0];
    }

    public Object[] getChildren(Object parentElement) {
      return new Object[0];
    }

    public Object getParent(Object element) {
      return null;
    }

    public boolean hasChildren(Object element) {
      return false;
    }
  }

  static class CommitStatusLabelProvider extends BaseLabelProvider implements
      IStyledLabelProvider {

    private Image DEFAULT = PlatformUI.getWorkbench().getSharedImages()
        .getImage(ISharedImages.IMG_OBJ_FILE);

    private ResourceManager resourceManager = new LocalResourceManager(
        JFaceResources.getResources());

    private final Image SUBMODULE = UIIcons.REPOSITORY.createImage();

    private Image getEditorImage(CommitItem item) {
      if (!item.submodule) {
        Image image = DEFAULT;
        String name = new Path(item.path).lastSegment();
        if (name != null) {
          ImageDescriptor descriptor = PlatformUI.getWorkbench()
              .getEditorRegistry().getImageDescriptor(name);
          image = (Image) this.resourceManager.get(descriptor);
        }
        return image;
      } else
        return SUBMODULE;
    }

    private Image getDecoratedImage(Image base, ImageDescriptor decorator) {
      DecorationOverlayIcon decorated = new DecorationOverlayIcon(base,
          decorator, IDecoration.BOTTOM_RIGHT);
      return (Image) this.resourceManager.get(decorated);
    }

    public StyledString getStyledText(Object element) {
      return new StyledString();
    }

    public Image getImage(Object element) {
      CommitItem item = (CommitItem) element;
      ImageDescriptor decorator = null;
      switch (item.status) {
      case UNTRACKED:
        decorator = UIIcons.OVR_UNTRACKED;
        break;
      case ADDED:
      case ADDED_INDEX_DIFF:
        decorator = UIIcons.OVR_STAGED_ADD;
        break;
      case REMOVED:
      case REMOVED_NOT_STAGED:
      case REMOVED_UNTRACKED:
        decorator = UIIcons.OVR_STAGED_REMOVE;
        break;
      default:
        break;
      }
      return decorator != null ? getDecoratedImage(getEditorImage(item),
          decorator) : getEditorImage(item);
    }

    @Override
    public void dispose() {
      SUBMODULE.dispose();
      resourceManager.dispose();
      super.dispose();
    }
  }

  static class CommitPathLabelProvider extends ColumnLabelProvider {

    public String getText(Object obj) {
      return ((CommitItem) obj).path;
    }

    public String getToolTipText(Object element) {
      return ((CommitItem) element).status.getText();
    }

  }

  class HeaderSelectionListener extends SelectionAdapter {

    private CommitItem.Order order;

    private Boolean reversed;

    public HeaderSelectionListener(CommitItem.Order order) {
      this.order = order;
    }

    @Override
    public void widgetSelected(SelectionEvent e) {
      TreeColumn column = (TreeColumn) e.widget;
      Tree tree = column.getParent();

      if (column == tree.getSortColumn()) {
        int currentDirection = tree.getSortDirection();
        switch (currentDirection) {
        case SWT.NONE:
          reversed = Boolean.FALSE;
          break;
        case SWT.UP:
          reversed = Boolean.TRUE;
          break;
        case SWT.DOWN:
          // fall through
        default:
          reversed = null;
          break;
        }
      } else
        reversed = Boolean.FALSE;

      if (reversed == null) {
        tree.setSortColumn(null);
        tree.setSortDirection(SWT.NONE);
        filesViewer.setComparator(null);
        return;
      }
      tree.setSortColumn(column);

      Comparator<CommitItem> comparator;
      if (reversed.booleanValue()) {
        comparator = order.descending();
        tree.setSortDirection(SWT.DOWN);
      } else {
        comparator = order;
        tree.setSortDirection(SWT.UP);
      }

      filesViewer.setComparator(new CommitViewerComparator(comparator));
    }

  }

  class CommitItemSelectionListener extends SelectionAdapter {

    public void widgetDefaultSelected(SelectionEvent e) {
      IStructuredSelection selection = (IStructuredSelection) filesViewer.getSelection();
      CommitItem commitItem = (CommitItem) selection.getFirstElement();
      if (commitItem == null) {
        return;
      }
      compare(commitItem);
    }

  }

  private final class CommitItemFilter extends ViewerFilter {
    @Override
    public boolean select(Viewer viewer, Object parentElement,
        Object element) {
      boolean result = true;
      if (!showUntracked || !allowToChangeSelection) {
        if (element instanceof CommitItem) {
          CommitItem item = (CommitItem) element;
          if (item.status == Status.UNTRACKED)
            result = false;
        }
      }
      return result;
    }
  }

  private static final String SHOW_UNTRACKED_PREF = "CommitDialog.showUntracked"; //$NON-NLS-1$

  private static final String DIALOG_SETTINGS_SECTION_NAME = Activator
      .getPluginId() + ".COMMIT_DIALOG_SECTION"; //$NON-NLS-1$

  /**
   * A constant used for the 'commit and push button' button
   */
  public static final int COMMIT_AND_PUSH_ID = 30;

  FormToolkit toolkit;

  CommitMessageComponent commitMessageComponent;

  SpellcheckableMessageArea commitText;

  Text authorText;

  Text committerText;

  ToolItem amendingItem;

  ToolItem signedOffItem;

  ToolItem changeIdItem;

  ToolItem showUntrackedItem;

  CachedCheckboxTreeViewer filesViewer;

  Section filesSection;

  Button commitButton;

  Button commitAndPushButton;

  ArrayList<CommitItem> items = new ArrayList<CommitItem>();

  private String commitMessage = null;

  private String author = null;

  private String committer = null;

  /**
   * A collection of files that should be already checked in the table.
   */
  private Set<String> preselectedFiles = Collections.emptySet();

  private boolean preselectAll = false;

  private ArrayList<String> selectedFiles = new ArrayList<String>();

  private boolean amending = false;

  private boolean amendAllowed = true;

  private boolean showUntracked = true;

  private boolean createChangeId = false;

  private boolean allowToChangeSelection = true;

  private Repository repository;

  private boolean isPushRequested = false;

  /**
   * @param parentShell
   */
  public CommitDialog(Shell parentShell) {
    super(parentShell);
  }

  /**
   * @return The message the user entered
   */
  public String getCommitMessage() {
    return commitMessage;
  }

  /**
   * Preset a commit message. This might be for amending a commit.
   * @param s the commit message
   */
  public void setCommitMessage(String s) {
    this.commitMessage = s;
  }

  /**
   * @return the files selected by the user to commit.
   */
  public Collection<String> getSelectedFiles() {
    return selectedFiles;
  }

  /**
   * Sets the files that should be checked in this table.
   *
   * @param preselectedFiles
   *            the files to be checked in the dialog's table, must not be
   *            <code>null</code>
   */
  public void setPreselectedFiles(Set<String> preselectedFiles) {
    Assert.isNotNull(preselectedFiles);
    this.preselectedFiles = preselectedFiles;
  }

  /**
   * Preselect all changed files in the commit dialog.
   * Untracked files are not preselected.
   * @param preselectAll
   */
  public void setPreselectAll(boolean preselectAll) {
    this.preselectAll = preselectAll;
  }

  /**
   * Set the total set of changed files, including additions and
   * removals
   * @param repository
   * @param paths paths of files potentially affected by a new commit
   * @param indexDiff IndexDiff of the related repository
   */
  public void setFiles(Repository repository, Set<String> paths,
      IndexDiff indexDiff) {
    this.repository = repository;
    items.clear();
    for (String path : paths) {
      CommitItem item = new CommitItem();
      item.status = getFileStatus(path, indexDiff);
      item.submodule = FileMode.GITLINK == indexDiff.getIndexMode(path);
      item.path = path;
      item.problemSeverity = getProblemSeverity(repository, path);
      items.add(item);
    }

    // initially, we sort by status plus path
    Collections.sort(items, new Comparator<CommitItem>() {
      public int compare(CommitItem o1, CommitItem o2) {
        int diff = o1.status.ordinal() - o2.status.ordinal();
        if (diff != 0)
          return diff;
        return o1.path.compareTo(o2.path);
      }
    });
  }

  /**
   * @return The author to set for the commit
   */
  public String getAuthor() {
    return author;
  }

  /**
   * Pre-set author for the commit
   *
   * @param author
   */
  public void setAuthor(String author) {
    this.author = author;
  }

  /**
   * @return The committer to set for the commit
   */
  public String getCommitter() {
    return committer;
  }

  /**
   * Pre-set committer for the commit
   *
   * @param committer
   */
  public void setCommitter(String committer) {
    this.committer = committer;
  }

  /**
   * @return whether the last commit is to be amended
   */
  public boolean isAmending() {
    return amending;
  }

  /**
   * Pre-set whether the last commit is going to be amended
   *
   * @param amending
   */
  public void setAmending(boolean amending) {
    this.amending = amending;
  }

  /**
   * Set whether the previous commit may be amended
   *
   * @param amendAllowed
   */
  public void setAmendAllowed(boolean amendAllowed) {
    this.amendAllowed = amendAllowed;
  }

  /**
   * Set whether is is allowed to change the set of selected files
   * @param allowToChangeSelection
   */
  public void setAllowToChangeSelection(boolean allowToChangeSelection) {
    this.allowToChangeSelection = allowToChangeSelection;
  }

  /**
   * @return true if a Change-Id line for Gerrit should be created
   */
  public boolean getCreateChangeId() {
    return createChangeId;
  }

  /**
   * @return true if push shall be executed
   */
  public boolean isPushRequested() {
    return isPushRequested;
  }

  @Override
  protected void createButtonsForButtonBar(Composite parent) {
    toolkit.adapt(parent, false, false);
    commitAndPushButton = createButton(parent, COMMIT_AND_PUSH_ID,
        UIText.CommitDialog_CommitAndPush, false);
    commitButton = createButton(parent, IDialogConstants.OK_ID,
        UIText.CommitDialog_Commit, true);
    createButton(parent, IDialogConstants.CANCEL_ID,
        IDialogConstants.CANCEL_LABEL, false);
    updateMessage();
  }

  protected void buttonPressed(int buttonId) {
    if (IDialogConstants.OK_ID == buttonId)
      okPressed();
    else if (COMMIT_AND_PUSH_ID == buttonId) {
      isPushRequested = true;
      okPressed();
    } else if (IDialogConstants.CANCEL_ID == buttonId)
      cancelPressed();
  }

  @Override
  protected Control createButtonBar(Composite parent) {
    toolkit.adapt(parent, false, false);
    return super.createButtonBar(parent);
  }

  @Override
  protected Control createHelpControl(Composite parent) {
    toolkit.adapt(parent, false, false);
    Link link = new Link(parent, SWT.WRAP | SWT.NO_FOCUS);
    ((GridLayout) parent.getLayout()).numColumns++;
    link.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
    link.setText(UIText.CommitDialog_OpenStagingViewLink);
    link.setToolTipText(UIText.CommitDialog_OpenStagingViewToolTip);
    link.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        openStagingViewLinkClicked();
      }
    });

    toolkit.adapt(link, false, false);
    return link;
  }

  private void openStagingViewLinkClicked() {
    IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench()
        .getActiveWorkbenchWindow();
    IWorkbenchPage workbenchPage = workbenchWindow.getActivePage();
    try {
      StagingView view = (StagingView) workbenchPage
          .showView(StagingView.VIEW_ID);
      view.reload(repository);
      String message = commitMessageComponent.getCommitMessage();
      if (message != null && message.length() > 0)
        view.setCommitMessage(message);
      setReturnCode(CANCEL);
      close();
    } catch (PartInitException e) {
      Activator.handleError(UIText.CommitDialog_OpenStagingViewError, e,
          true);
    }
  }

  @Override
  protected IDialogSettings getDialogBoundsSettings() {
    IDialogSettings settings = Activator.getDefault().getDialogSettings();
    IDialogSettings section = settings
        .getSection(DIALOG_SETTINGS_SECTION_NAME);
    if (section == null)
      section = settings.addNewSection(DIALOG_SETTINGS_SECTION_NAME);
    return section;
  }

  /**
   * Add message drop down toolbar item
   *
   * @param parent
   * @return toolbar
   */
  protected ToolBar addMessageDropDown(Composite parent) {
    final ToolBar dropDownBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT);
    final ToolItem dropDownItem = new ToolItem(dropDownBar, SWT.PUSH);
    dropDownItem.setImage(PlatformUI.getWorkbench().getSharedImages()
        .getImage("IMG_LCL_RENDERED_VIEW_MENU")); //$NON-NLS-1$
    final Menu menu = new Menu(dropDownBar);
    dropDownItem.addDisposeListener(new DisposeListener() {

      public void widgetDisposed(DisposeEvent e) {
        menu.dispose();
      }
    });
    MenuItem preferencesItem = new MenuItem(menu, SWT.PUSH);
    preferencesItem.setText(UIText.CommitDialog_ConfigureLink);
    preferencesItem.addSelectionListener(new SelectionAdapter() {

      public void widgetSelected(SelectionEvent e) {
        String[] pages = new String[] { UIPreferences.PAGE_COMMIT_PREFERENCES };
        PreferencesUtil.createPreferenceDialogOn(getShell(), pages[0],
            pages, null).open();
      }

    });
    dropDownItem.addSelectionListener(new SelectionAdapter() {

      public void widgetSelected(SelectionEvent e) {
        Rectangle b = dropDownItem.getBounds();
        Point p = dropDownItem.getParent().toDisplay(
            new Point(b.x, b.y + b.height));
        menu.setLocation(p.x, p.y);
        menu.setVisible(true);
      }

    });
    return dropDownBar;
  }

  @Override
  protected Control createContents(Composite parent) {
    toolkit = new FormToolkit(parent.getDisplay());
    parent.addDisposeListener(new DisposeListener() {

      public void widgetDisposed(DisposeEvent e) {
        toolkit.dispose();
      }
    });
    return super.createContents(parent);
  }

  @Override
  protected Control createDialogArea(Composite parent) {
    Composite container = (Composite) super.createDialogArea(parent);
    parent.getShell().setText(UIText.CommitDialog_CommitChanges);

    container = toolkit.createComposite(container);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
    toolkit.paintBordersFor(container);
    GridLayoutFactory.swtDefaults().applyTo(container);

    final SashForm sashForm= new SashForm(container, SWT.VERTICAL
        | SWT.FILL);
    toolkit.adapt(sashForm, true, true);
    sashForm.setLayoutData(GridDataFactory.fillDefaults().grab(true, true)
        .create());
    createMessageAndPersonArea(sashForm);
    filesSection = createFileSection(sashForm);
    sashForm.setWeights(new int[] { 50, 50 });

    applyDialogFont(container);
    container.pack();
    commitText.setFocus();
    Image titleImage = UIIcons.WIZBAN_CONNECT_REPO.createImage();
    UIUtils.hookDisposal(parent, titleImage);
    setTitleImage(titleImage);
    setTitle(UIText.CommitDialog_Title);
    setMessage(UIText.CommitDialog_Message, IMessageProvider.INFORMATION);

    filesViewer.addCheckStateListener(new ICheckStateListener() {

      public void checkStateChanged(CheckStateChangedEvent event) {
        updateMessage();
      }
    });

    updateFileSectionText();
    return container;
  }

  private Section createFileSection(Composite container) {
    Section filesSection = toolkit.createSection(container,
        ExpandableComposite.TITLE_BAR
            | ExpandableComposite.CLIENT_INDENT);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(filesSection);
    Composite filesArea = toolkit.createComposite(filesSection);
    filesSection.setClient(filesArea);
    toolkit.paintBordersFor(filesArea);
    GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2)
        .applyTo(filesArea);

    ToolBar filesToolbar = new ToolBar(filesSection, SWT.FLAT);

    filesSection.setTextClient(filesToolbar);

    PatternFilter patternFilter = new PatternFilter() {
      @Override
      protected boolean isLeafMatch(Viewer viewer, Object element) {
        if(element instanceof CommitItem) {
          CommitItem commitItem = (CommitItem) element;
          return wordMatches(commitItem.path);
        }
        return super.isLeafMatch(viewer, element);
      }
    };
    patternFilter.setIncludeLeadingWildcard(true);
    FilteredCheckboxTree resourcesTreeComposite = new FilteredCheckboxTree(
        filesArea, toolkit, SWT.FULL_SELECTION, patternFilter) {
      @Override
      protected WorkbenchJob doCreateRefreshJob() {
        // workaround for file filter not having an explicit change
        // listener
        WorkbenchJob filterJob = super.doCreateRefreshJob();
        filterJob.addJobChangeListener(new JobChangeAdapter() {
          public void done(IJobChangeEvent event) {
            if (event.getResult().isOK()) {
              getDisplay().asyncExec(new Runnable() {
                public void run() {
                  updateFileSectionText();
                }
              });
            }
          }
        });
        return filterJob;
      }
    };
    Tree resourcesTree = resourcesTreeComposite.getViewer().getTree();
    resourcesTree.setData(FormToolkit.KEY_DRAW_BORDER,
        FormToolkit.TREE_BORDER);
    resourcesTreeComposite.setLayoutData(GridDataFactory.fillDefaults()
        .hint(600, 200).grab(true, true).create());

    resourcesTree.addSelectionListener(new CommitItemSelectionListener());

    resourcesTree.setHeaderVisible(true);
    TreeColumn statCol = new TreeColumn(resourcesTree, SWT.LEFT);
    statCol.setText(UIText.CommitDialog_Status);
    statCol.setWidth(150);
    statCol.addSelectionListener(new HeaderSelectionListener(
        CommitItem.Order.ByStatus));

    TreeColumn resourceCol = new TreeColumn(resourcesTree, SWT.LEFT);
    resourceCol.setText(UIText.CommitDialog_Path);
    resourceCol.setWidth(415);
    resourceCol.addSelectionListener(new HeaderSelectionListener(
        CommitItem.Order.ByFile));

    filesViewer = resourcesTreeComposite.getCheckboxTreeViewer();
    new TreeViewerColumn(filesViewer, statCol)
        .setLabelProvider(createStatusLabelProvider());
    new TreeViewerColumn(filesViewer, resourceCol)
        .setLabelProvider(new CommitPathLabelProvider());
    ColumnViewerToolTipSupport.enableFor(filesViewer);
    filesViewer.setContentProvider(new CommitFileContentProvider());
    filesViewer.setUseHashlookup(true);
    IDialogSettings settings = org.eclipse.egit.ui.Activator.getDefault()
        .getDialogSettings();
    if (settings.get(SHOW_UNTRACKED_PREF) != null) {
      // note, no matter how the dialog settings are, if
      // the preferences force us to include untracked files
      // we must show them
      showUntracked = Boolean.valueOf(settings.get(SHOW_UNTRACKED_PREF))
          .booleanValue()
          || getPreferenceStore().getBoolean(
              UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED);
    }

    filesViewer.addFilter(new CommitItemFilter());
    filesViewer.setInput(items.toArray());
    MenuManager menuManager = new MenuManager();
    menuManager.setRemoveAllWhenShown(true);
    menuManager.addMenuListener(createContextMenuListener());
    filesViewer.getTree().setMenu(
        menuManager.createContextMenu(filesViewer.getTree()));
    filesViewer.addCheckStateListener(new ICheckStateListener() {

      public void checkStateChanged(CheckStateChangedEvent event) {
        updateFileSectionText();
      }
    });

    showUntrackedItem = new ToolItem(filesToolbar, SWT.CHECK);
    Image showUntrackedImage = UIIcons.UNTRACKED_FILE.createImage();
    UIUtils.hookDisposal(showUntrackedItem, showUntrackedImage);
    showUntrackedItem.setImage(showUntrackedImage);
    showUntrackedItem
        .setToolTipText(UIText.CommitDialog_ShowUntrackedFiles);
    showUntrackedItem.setSelection(showUntracked);
    showUntrackedItem.addSelectionListener(new SelectionAdapter() {

      public void widgetSelected(SelectionEvent e) {
        showUntracked = showUntrackedItem.getSelection();
        filesViewer.refresh(true);
        updateFileSectionText();
        updateMessage();
      }

    });

    ToolItem checkAllItem = new ToolItem(filesToolbar, SWT.PUSH);
    Image checkImage = UIIcons.CHECK_ALL.createImage();
    UIUtils.hookDisposal(checkAllItem, checkImage);
    checkAllItem.setImage(checkImage);
    checkAllItem.setToolTipText(UIText.CommitDialog_SelectAll);
    checkAllItem.addSelectionListener(new SelectionAdapter() {

      public void widgetSelected(SelectionEvent e) {
        filesViewer.setAllChecked(true);
        updateFileSectionText();
        updateMessage();
      }

    });

    ToolItem uncheckAllItem = new ToolItem(filesToolbar, SWT.PUSH);
    Image uncheckImage = UIIcons.UNCHECK_ALL.createImage();
    UIUtils.hookDisposal(uncheckAllItem, uncheckImage);
    uncheckAllItem.setImage(uncheckImage);
    uncheckAllItem.setToolTipText(UIText.CommitDialog_DeselectAll);
    uncheckAllItem.addSelectionListener(new SelectionAdapter() {

      public void widgetSelected(SelectionEvent e) {
        filesViewer.setAllChecked(false);
        updateFileSectionText();
        updateMessage();
      }

    });

    if (!allowToChangeSelection) {
      amendingItem.setSelection(false);
      amendingItem.setEnabled(false);
      showUntrackedItem.setSelection(false);
      showUntrackedItem.setEnabled(false);
      checkAllItem.setEnabled(false);
      uncheckAllItem.setEnabled(false);

      filesViewer.addCheckStateListener(new ICheckStateListener() {

        public void checkStateChanged(CheckStateChangedEvent event) {
          if (!event.getChecked())
            filesViewer.setAllChecked(true);
          updateFileSectionText();
        }
      });
      filesViewer.setAllChecked(true);
    } else {
      final boolean includeUntracked = getPreferenceStore().getBoolean(
          UIPreferences.COMMIT_DIALOG_INCLUDE_UNTRACKED);
      for (CommitItem item : items) {
        if (!preselectAll && !preselectedFiles.contains(item.path))
          continue;
        if (item.status == Status.ASSUME_UNCHANGED)
          continue;
        if (!includeUntracked && item.status == Status.UNTRACKED)
          continue;
        filesViewer.setChecked(item, true);
      }
    }
    statCol.pack();
    resourceCol.pack();
    return filesSection;
  }

  private Composite createMessageAndPersonArea(Composite container) {

    Composite messageAndPersonArea = toolkit.createComposite(container);
    GridDataFactory.fillDefaults().grab(true, true)
        .applyTo(messageAndPersonArea);
    GridLayoutFactory.swtDefaults().margins(0, 0).spacing(0, 0)
        .applyTo(messageAndPersonArea);

    Section messageSection = toolkit.createSection(messageAndPersonArea,
        ExpandableComposite.TITLE_BAR
            | ExpandableComposite.CLIENT_INDENT);
    messageSection.setText(UIText.CommitDialog_CommitMessage);
    Composite messageArea = toolkit.createComposite(messageSection);
    GridLayoutFactory.fillDefaults().spacing(0, 0)
        .extendedMargins(2, 2, 2, 2).applyTo(messageArea);
    toolkit.paintBordersFor(messageArea);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(messageSection);
    GridLayoutFactory.swtDefaults().applyTo(messageSection);

    Composite headerArea = new Composite(messageSection, SWT.NONE);
    GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(2)
        .applyTo(headerArea);

    ToolBar messageToolbar = new ToolBar(headerArea, SWT.FLAT
        | SWT.HORIZONTAL);
    GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL)
        .grab(true, false).applyTo(messageToolbar);

    addMessageDropDown(headerArea);

    messageSection.setTextClient(headerArea);

    final CommitProposalProcessor commitProposalProcessor = new CommitProposalProcessor() {
      @Override
      protected Collection<String> computeFileNameProposals() {
        return getFileList();
      }

      @Override
      protected Collection<String> computeMessageProposals() {
        return CommitMessageHistory.getCommitHistory();
      }
    };
    commitText = new CommitMessageArea(messageArea, commitMessage, SWT.NONE) {
      @Override
      protected CommitProposalProcessor getCommitProposalProcessor() {
        return commitProposalProcessor;
      }
    };
    commitText
        .setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
    messageSection.setClient(messageArea);
    Point size = commitText.getTextWidget().getSize();
    int minHeight = commitText.getTextWidget().getLineHeight() * 3;
    commitText.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, true).hint(size).minSize(size.x, minHeight)
        .align(SWT.FILL, SWT.FILL).create());

    UIUtils.addBulbDecorator(commitText.getTextWidget(),
        UIText.CommitDialog_ContentAssist);

    Composite personArea = toolkit.createComposite(messageAndPersonArea);
    toolkit.paintBordersFor(personArea);
    GridLayoutFactory.swtDefaults().numColumns(2).applyTo(personArea);
    GridDataFactory.fillDefaults().grab(true, false).applyTo(personArea);

    toolkit.createLabel(personArea, UIText.CommitDialog_Author)
        .setForeground(
            toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
    authorText = toolkit.createText(personArea, null);
    authorText
        .setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
    authorText.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, false).create());
    if (repository != null
        && repository.getRepositoryState().equals(
            RepositoryState.CHERRY_PICKING_RESOLVED))
      authorText.setEnabled(false);

    toolkit.createLabel(personArea, UIText.CommitDialog_Committer)
        .setForeground(
            toolkit.getColors().getColor(IFormColors.TB_TOGGLE));
    committerText = toolkit.createText(personArea, null);
    committerText.setLayoutData(GridDataFactory.fillDefaults()
        .grab(true, false).create());
    if (committer != null)
      committerText.setText(committer);

    amendingItem = new ToolItem(messageToolbar, SWT.CHECK);
    amendingItem.setSelection(amending);
    if (amending)
      amendingItem.setEnabled(false); // if already set, don't allow any
                      // changes
    else if (!amendAllowed)
      amendingItem.setEnabled(false);
    amendingItem.setToolTipText(UIText.CommitDialog_AmendPreviousCommit);
    Image amendImage = UIIcons.AMEND_COMMIT.createImage();
    UIUtils.hookDisposal(amendingItem, amendImage);
    amendingItem.setImage(amendImage);

    signedOffItem = new ToolItem(messageToolbar, SWT.CHECK);

    signedOffItem.setToolTipText(UIText.CommitDialog_AddSOB);
    Image signedOffImage = UIIcons.SIGNED_OFF.createImage();
    UIUtils.hookDisposal(signedOffItem, signedOffImage);
    signedOffItem.setImage(signedOffImage);

    changeIdItem = new ToolItem(messageToolbar, SWT.CHECK);
    Image changeIdImage = UIIcons.GERRIT.createImage();
    UIUtils.hookDisposal(changeIdItem, changeIdImage);
    changeIdItem.setImage(changeIdImage);
    changeIdItem.setToolTipText(UIText.CommitDialog_AddChangeIdLabel);

    final ICommitMessageComponentNotifications listener = new ICommitMessageComponentNotifications() {

      public void updateSignedOffToggleSelection(boolean selection) {
        signedOffItem.setSelection(selection);
      }

      public void updateChangeIdToggleSelection(boolean selection) {
        changeIdItem.setSelection(selection);
      }

      public void statusUpdated() {
        updateMessage();
      }
    };

    commitMessageComponent = new CommitMessageComponent(repository,
        listener);
    commitMessageComponent.enableListeners(false);
    commitMessageComponent.setDefaults();
    commitMessageComponent.attachControls(commitText, authorText,
        committerText);
    commitMessageComponent.setCommitMessage(commitMessage);
    commitMessageComponent.setAuthor(author);
    commitMessageComponent.setCommitter(committer);
    commitMessageComponent.setAmending(amending);
    commitMessageComponent.setFilesToCommit(getFileList());

    amendingItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent arg0) {
        commitMessageComponent.setAmendingButtonSelection(amendingItem
            .getSelection());
      }
    });

    changeIdItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent arg0) {
        commitMessageComponent.setChangeIdButtonSelection(changeIdItem
            .getSelection());
      }
    });

    signedOffItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent arg0) {
        commitMessageComponent
            .setSignedOffButtonSelection(signedOffItem
                .getSelection());
      }
    });

    commitMessageComponent.updateUI();
    commitMessageComponent.enableListeners(true);

    return messageAndPersonArea;
  }

  private static CellLabelProvider createStatusLabelProvider() {
    CommitStatusLabelProvider baseProvider = new CommitStatusLabelProvider();
    ProblemLabelDecorator decorator = new ProblemLabelDecorator(null);
    return new DecoratingStyledCellLabelProvider(baseProvider, decorator, null) {
      @Override
      public String getToolTipText(Object element) {
        return ((CommitItem) element).status.getText();
      }
    };
  }

  private void updateMessage() {
    if (commitButton == null)
      // Not yet fully initialized.
      return;

    String message = null;
    int type = IMessageProvider.NONE;

    String commitMsg = commitMessageComponent.getCommitMessage();
    if (commitMsg == null || commitMsg.trim().length() == 0) {
      message = UIText.CommitDialog_Message;
      type = IMessageProvider.INFORMATION;
    } else if (!isCommitWithoutFilesAllowed()) {
      message = UIText.CommitDialog_MessageNoFilesSelected;
      type = IMessageProvider.INFORMATION;
    } else {
      CommitStatus status = commitMessageComponent.getStatus();
      message = status.getMessage();
      type = status.getMessageType();
    }

    setMessage(message, type);
    boolean commitEnabled = type == IMessageProvider.WARNING
        || type == IMessageProvider.NONE;
    commitButton.setEnabled(commitEnabled);
    commitAndPushButton.setEnabled(commitEnabled);
  }

  private boolean isCommitWithoutFilesAllowed() {
    if (filesViewer.getCheckedElements().length > 0)
      return true;

    if (amendingItem.getSelection())
      return true;

    return CommitHelper.isCommitWithoutFilesAllowed(repository);
  }

  private Collection<String> getFileList() {
    Collection<String> result = new ArrayList<String>();
    for (CommitItem item : items) {
      result.add(item.path);
    }
    return result;
  }

  private void updateFileSectionText() {
    filesSection.setText(MessageFormat.format(UIText.CommitDialog_Files,
        Integer.valueOf(filesViewer.getCheckedElements().length),
        Integer.valueOf(filesViewer.getTree().getItemCount())));
  }

  private IMenuListener createContextMenuListener() {
    return new IMenuListener() {
      public void menuAboutToShow(IMenuManager manager) {
        if (!allowToChangeSelection)
          return;

        final IStructuredSelection selection = (IStructuredSelection) filesViewer
            .getSelection();
        if (selection.isEmpty())
          return;

        if (selection.size() == 1
            && selection.getFirstElement() instanceof CommitItem) {
          CommitItem commitItem = (CommitItem) selection
              .getFirstElement();
          manager.add(createCompareAction(commitItem));
        }

        boolean hasUnselected = false;
        for (Object element : selection.toList()) {
          if (!filesViewer.getChecked(element)) {
            hasUnselected = true;
            break;
          }
        }

        if (hasUnselected)
          manager.add(createSelectAction(selection));
        manager.add(createAddAction(selection));
      }
    };
  }

  private Action createCompareAction(final CommitItem commitItem) {
    return new Action(UIText.CommitDialog_CompareWithHeadRevision) {
      @Override
      public void run() {
        compare(commitItem);
      }
    };
  }

  private Action createAddAction(final IStructuredSelection selection) {
    return new Action(UIText.CommitDialog_AddFileOnDiskToIndex) {
      public void run() {
        AddCommand addCommand = new Git(repository).add();
        for (Iterator<?> it = selection.iterator(); it.hasNext();) {
          CommitItem commitItem = (CommitItem) it.next();
          addCommand.addFilepattern(commitItem.path);
        }
        try {
          addCommand.call();
        } catch (Exception e) {
          Activator.logError(UIText.CommitDialog_ErrorAddingFiles, e);
        }
        for (Iterator<?> it = selection.iterator(); it.hasNext();) {
          CommitItem commitItem = (CommitItem) it.next();
          try {
            commitItem.status = getFileStatus(commitItem.path);
          } catch (IOException e) {
            Activator.logError(
                UIText.CommitDialog_ErrorAddingFiles, e);
          }
        }
        filesViewer.refresh(true);
      }
    };
  }

  private IAction createSelectAction(final IStructuredSelection selection) {
    return new Action(UIText.CommitDialog_SelectForCommit) {
      @Override
      public void run() {
        for (Object item : selection.toList())
          filesViewer.setChecked(item, true);
      }
    };
  }

  /** Retrieve file status
   * @param path
   * @return file status
   * @throws IOException
   */
  private Status getFileStatus(String path) throws IOException {
    AdaptableFileTreeIterator fileTreeIterator = new AdaptableFileTreeIterator(
        repository, ResourcesPlugin.getWorkspace().getRoot());
    IndexDiff indexDiff = new IndexDiff(repository, Constants.HEAD, fileTreeIterator);
    Set<String> repositoryPaths = Collections.singleton(path);
    indexDiff.setFilter(PathFilterGroup.createFromStrings(repositoryPaths));
    indexDiff.diff(null, 0, 0, ""); //$NON-NLS-1$
    return getFileStatus(path, indexDiff);
  }

  /** Retrieve file status from an already calculated IndexDiff
   * @param path
   * @param indexDiff
   * @return file status
   */
  private static Status getFileStatus(String path, IndexDiff indexDiff) {
    if (indexDiff.getAssumeUnchanged().contains(path)) {
      return Status.ASSUME_UNCHANGED;
    } else if (indexDiff.getAdded().contains(path)) {
      // added
      if (indexDiff.getModified().contains(path))
        return Status.ADDED_INDEX_DIFF;
      else
        return Status.ADDED;
    } else if (indexDiff.getChanged().contains(path)) {
      // changed
      if (indexDiff.getModified().contains(path))
        return Status.MODIFIED_INDEX_DIFF;
      else
        return Status.MODIFIED;
    } else if (indexDiff.getUntracked().contains(path)) {
      // untracked
      if (indexDiff.getRemoved().contains(path))
        return Status.REMOVED_UNTRACKED;
      else
        return Status.UNTRACKED;
    } else if (indexDiff.getRemoved().contains(path)) {
      // removed
      return Status.REMOVED;
    } else if (indexDiff.getMissing().contains(path)) {
      // missing
      return Status.REMOVED_NOT_STAGED;
    } else if (indexDiff.getModified().contains(path)) {
      // modified (and not changed!)
      return Status.MODIFIED_NOT_STAGED;
    }
    return Status.UNKNOWN;
  }

  private static int getProblemSeverity(Repository repository, String path) {
    IFile file = ResourceUtil.getFileForLocation(repository, path);
    if (file != null) {
      try {
        int severity = file.findMaxProblemSeverity(IMarker.PROBLEM, true, IResource.DEPTH_ONE);
        return severity;
      } catch (CoreException e) {
        // Fall back to below
      }
    }
    return IProblemDecoratable.SEVERITY_NONE;
  }

  @Override
  protected void okPressed() {
    if (!isCommitWithoutFilesAllowed()) {
      MessageDialog.openWarning(getShell(), UIText.CommitDialog_ErrorNoItemsSelected, UIText.CommitDialog_ErrorNoItemsSelectedToBeCommitted);
      return;
    }

    if (!commitMessageComponent.checkCommitInfo())
      return;

    Object[] checkedElements = filesViewer.getCheckedElements();
    selectedFiles.clear();
    for (Object obj : checkedElements)
      selectedFiles.add(((CommitItem) obj).path);

    amending = commitMessageComponent.isAmending();
    commitMessage = commitMessageComponent.getCommitMessage();
    author = commitMessageComponent.getAuthor();
    committer = commitMessageComponent.getCommitter();
    createChangeId = changeIdItem.getSelection();

    IDialogSettings settings = org.eclipse.egit.ui.Activator
      .getDefault().getDialogSettings();
    settings.put(SHOW_UNTRACKED_PREF, showUntracked);
    CommitMessageHistory.saveCommitHistory(getCommitMessage());
    super.okPressed();
  }

  @Override
  protected int getShellStyle() {
    return super.getShellStyle() | SWT.RESIZE;
  }

  private void compare(CommitItem commitItem) {
    IFile file = findFile(commitItem.path);
    if (file == null
        || RepositoryProvider.getProvider(file.getProject()) == null)
      CompareUtils
          .compareHeadWithWorkingTree(repository, commitItem.path);
    else
      CompareUtils.compareHeadWithWorkspace(repository, file);
  }

  private IFile findFile(String path) {
    return ResourceUtil.getFileForLocation(repository, path);
  }
}

class CommitItem implements IProblemDecoratable {

  Status status;

  String path;

  boolean submodule;

  int problemSeverity;

  public int getProblemSeverity() {
    return problemSeverity;
  }

  /** The ordinal of this {@link Enum} is used to provide the "native" sorting of the list */
  public static enum Status {
    /** */
    ADDED(UIText.CommitDialog_StatusAdded),
    /** */
    MODIFIED(UIText.CommitDialog_StatusModified),
    /** */
    REMOVED(UIText.CommitDialog_StatusRemoved),
    /** */
    ADDED_INDEX_DIFF(UIText.CommitDialog_StatusAddedIndexDiff),
    /** */
    MODIFIED_INDEX_DIFF(UIText.CommitDialog_StatusModifiedIndexDiff),
    /** */
    MODIFIED_NOT_STAGED(UIText.CommitDialog_StatusModifiedNotStaged),
    /** */
    REMOVED_NOT_STAGED(UIText.CommitDialog_StatusRemovedNotStaged),
    /** */
    UNTRACKED(UIText.CommitDialog_StatusUntracked),
    /** */
    REMOVED_UNTRACKED(UIText.CommitDialog_StatusRemovedUntracked),
    /** */
    ASSUME_UNCHANGED(UIText.CommitDialog_StatusAssumeUnchaged),
    /** */
    UNKNOWN(UIText.CommitDialog_StatusUnknown);

    public String getText() {
      return myText;
    }

    private final String myText;

    private Status(String text) {
      myText = text;
    }
  }

  public static enum Order implements Comparator<CommitItem> {
    ByStatus() {

      public int compare(CommitItem o1, CommitItem o2) {
        return o1.status.compareTo(o2.status);
      }

    },

    ByFile() {

      public int compare(CommitItem o1, CommitItem o2) {
        return o1.path.compareTo(
            o2.path);
      }

    };

    public Comparator<CommitItem> ascending() {
      return this;
    }

    public Comparator<CommitItem> descending() {
      return Collections.reverseOrder(this);
    }
  }
}

class CommitViewerComparator extends ViewerComparator {

  public CommitViewerComparator(Comparator comparator){
    super(comparator);
  }

  @Override
  public int compare(Viewer viewer, Object e1, Object e2) {
    return getComparator().compare(e1, e2);
  }

}
TOP

Related Classes of org.eclipse.egit.ui.internal.dialogs.CommitDialog$CommitFileContentProvider

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.