| /******************************************************************************* |
| * Copyright (c) 2013, 2015 SAP AG 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: |
| * Tobias Pfeifer (SAP AG) - initial implementation |
| * Tobias Baumann (tobbaumann@gmail.com) - Bug 473950 |
| * Thomas Wolf <thomas.wolf@paranor.ch> - Bug 477248 |
| *******************************************************************************/ |
| package org.eclipse.egit.ui.internal.rebase; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.egit.core.AdapterUtils; |
| import org.eclipse.egit.core.RepositoryUtil; |
| import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan; |
| import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.ElementAction; |
| import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.ElementType; |
| import org.eclipse.egit.core.internal.rebase.RebaseInteractivePlan.PlanElement; |
| import org.eclipse.egit.core.project.RepositoryMapping; |
| import org.eclipse.egit.ui.Activator; |
| import org.eclipse.egit.ui.UIPreferences; |
| import org.eclipse.egit.ui.UIUtils; |
| import org.eclipse.egit.ui.internal.CommonUtils; |
| import org.eclipse.egit.ui.internal.PreferenceBasedDateFormatter; |
| import org.eclipse.egit.ui.internal.UIIcons; |
| import org.eclipse.egit.ui.internal.UIText; |
| import org.eclipse.egit.ui.internal.actions.BooleanPrefAction; |
| import org.eclipse.egit.ui.internal.commands.shared.AbortRebaseCommand; |
| import org.eclipse.egit.ui.internal.commands.shared.AbstractRebaseCommandHandler; |
| import org.eclipse.egit.ui.internal.commands.shared.ContinueRebaseCommand; |
| import org.eclipse.egit.ui.internal.commands.shared.ProcessStepsRebaseCommand; |
| import org.eclipse.egit.ui.internal.commands.shared.SkipRebaseCommand; |
| import org.eclipse.egit.ui.internal.commit.CommitEditor; |
| import org.eclipse.egit.ui.internal.commit.RepositoryCommit; |
| import org.eclipse.egit.ui.internal.repository.RepositoriesView; |
| import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.layout.GridLayoutFactory; |
| import org.eclipse.jface.preference.IPersistentPreferenceStore; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.resource.LocalResourceManager; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.LocalSelectionTransfer; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ColumnLabelProvider; |
| import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; |
| import org.eclipse.jface.viewers.DoubleClickEvent; |
| import org.eclipse.jface.viewers.IDoubleClickListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.TreeViewerColumn; |
| import org.eclipse.jface.window.ToolTip; |
| import org.eclipse.jgit.lib.AbbreviatedObjectId; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.RepositoryState; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.util.GitDateFormatter; |
| import org.eclipse.jgit.util.GitDateFormatter.Format; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.Transfer; |
| 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.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.RowLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.IActionBars; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.ISelectionListener; |
| import org.eclipse.ui.ISelectionService; |
| import org.eclipse.ui.IViewSite; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.forms.widgets.ExpandableComposite; |
| import org.eclipse.ui.forms.widgets.Form; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.forms.widgets.Section; |
| import org.eclipse.ui.part.ViewPart; |
| |
| /** |
| * View visualizing git interactive rebase |
| */ |
| public class RebaseInteractiveView extends ViewPart implements |
| RebaseInteractivePlan.RebaseInteractivePlanChangeListener { |
| |
| /** |
| * interactive rebase view id |
| */ |
| public static final String VIEW_ID = "org.eclipse.egit.ui.InteractiveRebaseView"; //$NON-NLS-1$ |
| |
| TreeViewer planTreeViewer; |
| |
| private RebaseInteractivePlan currentPlan; |
| |
| private Repository currentRepository; |
| |
| private RebaseInteractiveStepActionToolBarProvider actionToolBarProvider; |
| |
| private ToolItem startItem; |
| |
| private ToolItem abortItem; |
| |
| private ToolItem skipItem; |
| |
| private ToolItem continueItem; |
| |
| private ToolItem refreshItem; |
| |
| private boolean listenOnRepositoryViewSelection = true; |
| |
| private ISelectionListener selectionChangedListener; |
| |
| private boolean dndEnabled = false; |
| |
| private Form form; |
| |
| private LocalResourceManager resources = new LocalResourceManager( |
| JFaceResources.getResources()); |
| |
| private GitDateFormatter dateFormatter = getNewDateFormatter(); |
| |
| /** these columns are dynamically resized to fit their contents */ |
| private TreeViewerColumn[] dynamicColumns; |
| |
| private List<PlanContextMenuAction> contextMenuItems; |
| |
| private RebasePlanIndexer planIndexer; |
| |
| private IPreferenceChangeListener prefListener; |
| |
| private IPropertyChangeListener uiPrefsListener; |
| |
| private InitialSelection initialSelection; |
| |
| @Override |
| public void init(IViewSite site) throws PartInitException { |
| super.init(site); |
| setPartName(UIText.InteractiveRebaseView_this_partName); |
| initInitialSelection(site); |
| } |
| |
| private void initInitialSelection(IViewSite site) { |
| this.initialSelection = new InitialSelection( |
| site.getWorkbenchWindow().getSelectionService().getSelection()); |
| if (!isViewInputDerivableFromSelection(initialSelection.selection)) { |
| this.initialSelection.activeEditor = site.getPage() |
| .getActiveEditor(); |
| } |
| } |
| |
| private static boolean isViewInputDerivableFromSelection(Object o) { |
| return o instanceof StructuredSelection |
| && ((StructuredSelection) o).size() == 1; |
| } |
| |
| /** |
| * Set the view input if the passed object can be used to determine the |
| * current repository |
| * |
| * @param o |
| */ |
| public void setInput(Object o) { |
| if (o == null) |
| return; |
| |
| if (isViewInputDerivableFromSelection(o)) { |
| o = ((StructuredSelection) o).getFirstElement(); |
| } |
| Repository repo = null; |
| if (o instanceof RepositoryTreeNode<?>) |
| repo = ((RepositoryTreeNode) o).getRepository(); |
| else if (o instanceof Repository) |
| repo = (Repository) o; |
| else if (o instanceof IAdaptable) { |
| IResource resource = CommonUtils.getAdapter(((IAdaptable) o), IResource.class); |
| if (resource != null) { |
| RepositoryMapping mapping = RepositoryMapping |
| .getMapping(resource); |
| if (mapping == null) |
| return; |
| |
| repo = mapping.getRepository(); |
| } |
| } |
| if (repo == null) |
| repo = AdapterUtils.adapt(o, Repository.class); |
| |
| currentRepository = repo; |
| showRepository(repo); |
| } |
| |
| /** |
| * @return {@link RebaseInteractiveView#currentPlan} |
| */ |
| public RebaseInteractivePlan getCurrentPlan() { |
| return currentPlan; |
| } |
| |
| @Override |
| public void dispose() { |
| removeListeners(); |
| resources.dispose(); |
| super.dispose(); |
| } |
| |
| private void removeListeners() { |
| ISelectionService srv = CommonUtils.getService(getSite(), ISelectionService.class); |
| srv.removePostSelectionListener(RepositoriesView.VIEW_ID, |
| selectionChangedListener); |
| if (currentPlan != null) |
| currentPlan.removeRebaseInteractivePlanChangeListener(this); |
| |
| if (planIndexer != null) |
| planIndexer.dispose(); |
| |
| InstanceScope.INSTANCE.getNode( |
| org.eclipse.egit.core.Activator.getPluginId()) |
| .removePreferenceChangeListener(prefListener); |
| Activator.getDefault().getPreferenceStore() |
| .removePropertyChangeListener(uiPrefsListener); |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| GridLayoutFactory.fillDefaults().applyTo(parent); |
| final FormToolkit toolkit = new FormToolkit(parent.getDisplay()); |
| parent.addDisposeListener(new DisposeListener() { |
| |
| @Override |
| public void widgetDisposed(DisposeEvent e) { |
| toolkit.dispose(); |
| } |
| }); |
| form = createForm(parent, toolkit); |
| createCommandToolBar(form, toolkit); |
| SashForm sashForm = createRebasePlanSashForm(form, toolkit); |
| |
| Section rebasePlanSection = toolkit.createSection(sashForm, |
| ExpandableComposite.TITLE_BAR); |
| planTreeViewer = createPlanTreeViewer(rebasePlanSection, toolkit); |
| |
| createColumns(); |
| createStepActionToolBar(rebasePlanSection, toolkit); |
| createPopupMenu(planTreeViewer); |
| |
| setupListeners(); |
| createLocalDragandDrop(); |
| planTreeViewer.addDoubleClickListener(new IDoubleClickListener() { |
| |
| @Override |
| public void doubleClick(DoubleClickEvent event) { |
| PlanElement element = (PlanElement) ((IStructuredSelection) event |
| .getSelection()).getFirstElement(); |
| if (element == null) |
| return; |
| |
| RepositoryCommit commit = loadCommit(element.getCommit()); |
| if (commit != null) |
| CommitEditor.openQuiet(commit); |
| |
| } |
| |
| private RepositoryCommit loadCommit( |
| AbbreviatedObjectId abbreviatedObjectId) { |
| if (abbreviatedObjectId != null) { |
| try (RevWalk walk = new RevWalk( |
| RebaseInteractiveView.this.currentRepository)) { |
| Collection<ObjectId> resolved = walk.getObjectReader() |
| .resolve(abbreviatedObjectId); |
| if (resolved.size() == 1) { |
| RevCommit commit = walk.parseCommit(resolved |
| .iterator().next()); |
| return new RepositoryCommit( |
| RebaseInteractiveView.this.currentRepository, |
| commit); |
| } |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| return null; |
| } |
| }); |
| |
| prefListener = new IPreferenceChangeListener() { |
| @Override |
| public void preferenceChange(PreferenceChangeEvent event) { |
| if (!RepositoryUtil.PREFS_DIRECTORIES.equals(event.getKey())) |
| return; |
| |
| final Repository repo = currentRepository; |
| if (repo == null) |
| return; |
| |
| if (Activator.getDefault().getRepositoryUtil().contains(repo)) |
| return; |
| |
| // Unselect repository as it has been removed |
| Display.getDefault().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| currentRepository = null; |
| showRepository(null); |
| } |
| }); |
| } |
| }; |
| |
| InstanceScope.INSTANCE.getNode( |
| org.eclipse.egit.core.Activator.getPluginId()) |
| .addPreferenceChangeListener(prefListener); |
| |
| uiPrefsListener = new IPropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent event) { |
| String property = event.getProperty(); |
| if (UIPreferences.DATE_FORMAT.equals(property) |
| || UIPreferences.DATE_FORMAT_CHOICE.equals(property) |
| || UIPreferences.RESOURCEHISTORY_SHOW_RELATIVE_DATE |
| .equals(property)) { |
| refresh(); |
| } |
| } |
| }; |
| |
| Activator.getDefault().getPreferenceStore() |
| .addPropertyChangeListener(uiPrefsListener); |
| |
| IActionBars actionBars = getViewSite().getActionBars(); |
| IToolBarManager toolbar = actionBars.getToolBarManager(); |
| |
| listenOnRepositoryViewSelection = RebaseInteractivePreferences |
| .isReactOnSelection(); |
| |
| // link with selection |
| Action linkSelectionAction = new BooleanPrefAction( |
| (IPersistentPreferenceStore) Activator.getDefault() |
| .getPreferenceStore(), |
| UIPreferences.REBASE_INTERACTIVE_SYNC_SELECTION, |
| UIText.InteractiveRebaseView_LinkSelection) { |
| @Override |
| public void apply(boolean value) { |
| listenOnRepositoryViewSelection = value; |
| } |
| }; |
| linkSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED); |
| toolbar.add(linkSelectionAction); |
| |
| reactOnInitalSelection(); |
| } |
| |
| private void createCommandToolBar(Form theForm, FormToolkit toolkit) { |
| ToolBar toolBar = new ToolBar(theForm.getHead(), SWT.FLAT); |
| toolBar.setOrientation(SWT.RIGHT_TO_LEFT); |
| theForm.setHeadClient(toolBar); |
| |
| toolkit.adapt(toolBar); |
| toolkit.paintBordersFor(toolBar); |
| |
| startItem = new ToolItem(toolBar, SWT.NONE); |
| startItem.setImage(UIIcons.getImage(resources, |
| UIIcons.REBASE_PROCESS_STEPS)); |
| startItem.addSelectionListener(new RebaseCommandItemSelectionListener( |
| new ProcessStepsRebaseCommand())); |
| startItem.setEnabled(false); |
| startItem.setText(UIText.InteractiveRebaseView_startItem_text); |
| |
| continueItem = new ToolItem(toolBar, SWT.NONE); |
| continueItem.setImage(UIIcons.getImage(resources, |
| UIIcons.REBASE_CONTINUE)); |
| continueItem |
| .addSelectionListener(new RebaseCommandItemSelectionListener( |
| new ContinueRebaseCommand())); |
| continueItem.setEnabled(false); |
| continueItem.setText(UIText.InteractiveRebaseView_continueItem_text); |
| |
| skipItem = new ToolItem(toolBar, SWT.NONE); |
| skipItem.setImage(UIIcons.getImage(resources, UIIcons.REBASE_SKIP)); |
| skipItem.addSelectionListener(new RebaseCommandItemSelectionListener( |
| new SkipRebaseCommand())); |
| skipItem.setText(UIText.InteractiveRebaseView_skipItem_text); |
| skipItem.setEnabled(false); |
| |
| abortItem = new ToolItem(toolBar, SWT.NONE); |
| abortItem.setImage(UIIcons.getImage(resources, UIIcons.REBASE_ABORT)); |
| abortItem.addSelectionListener(new RebaseCommandItemSelectionListener( |
| new AbortRebaseCommand())); |
| abortItem.setText(UIText.InteractiveRebaseView_abortItem_text); |
| abortItem.setEnabled(false); |
| |
| createSeparator(toolBar); |
| |
| refreshItem = new ToolItem(toolBar, SWT.NONE); |
| refreshItem.setImage(UIIcons |
| .getImage(resources, UIIcons.ELCL16_REFRESH)); |
| refreshItem.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| refresh(); |
| } |
| }); |
| refreshItem.setText(UIText.InteractiveRebaseView_refreshItem_text); |
| } |
| |
| private static ToolItem createSeparator(ToolBar toolBar) { |
| return new ToolItem(toolBar, SWT.SEPARATOR); |
| } |
| |
| private TreeViewer createPlanTreeViewer(Section rebasePlanSection, |
| FormToolkit toolkit) { |
| |
| Composite rebasePlanTableComposite = toolkit |
| .createComposite(rebasePlanSection); |
| toolkit.paintBordersFor(rebasePlanTableComposite); |
| rebasePlanSection.setClient(rebasePlanTableComposite); |
| GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2) |
| .applyTo(rebasePlanTableComposite); |
| |
| Composite toolbarComposite = toolkit.createComposite(rebasePlanSection); |
| toolbarComposite.setBackground(null); |
| RowLayout toolbarRowLayout = new RowLayout(); |
| toolbarRowLayout.marginHeight = 0; |
| toolbarRowLayout.marginWidth = 0; |
| toolbarRowLayout.marginTop = 0; |
| toolbarRowLayout.marginBottom = 0; |
| toolbarRowLayout.marginLeft = 0; |
| toolbarRowLayout.marginRight = 0; |
| toolbarComposite.setLayout(toolbarRowLayout); |
| |
| GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2) |
| .applyTo(rebasePlanTableComposite); |
| |
| final Tree planTree = toolkit.createTree(rebasePlanTableComposite, |
| SWT.FULL_SELECTION | SWT.MULTI); |
| planTree.setHeaderVisible(true); |
| planTree.setLinesVisible(false); |
| |
| TreeViewer viewer = new TreeViewer(planTree); |
| viewer.addSelectionChangedListener(new PlanViewerSelectionChangedListener()); |
| GridDataFactory.fillDefaults().grab(true, true) |
| .applyTo(viewer.getControl()); |
| viewer.getTree().setData(FormToolkit.KEY_DRAW_BORDER, |
| FormToolkit.TREE_BORDER); |
| viewer.setContentProvider(RebaseInteractivePlanContentProvider.INSTANCE); |
| return viewer; |
| } |
| |
| private SashForm createRebasePlanSashForm(final Form parent, |
| final FormToolkit toolkit) { |
| SashForm sashForm = new SashForm(parent.getBody(), SWT.NONE); |
| toolkit.adapt(sashForm, true, true); |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(sashForm); |
| return sashForm; |
| } |
| |
| private Form createForm(Composite parent, final FormToolkit toolkit) { |
| Form newForm = toolkit.createForm(parent); |
| |
| Image repoImage = UIIcons.REPOSITORY.createImage(); |
| UIUtils.hookDisposal(newForm, repoImage); |
| newForm.setImage(repoImage); |
| newForm.setText(UIText.RebaseInteractiveView_NoSelection); |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(newForm); |
| toolkit.decorateFormHeading(newForm); |
| GridLayoutFactory.swtDefaults().applyTo(newForm.getBody()); |
| |
| return newForm; |
| } |
| |
| private void setupListeners() { |
| setupRepositoryViewSelectionChangeListener(); |
| refreshUI(); |
| } |
| |
| private void setupRepositoryViewSelectionChangeListener() { |
| selectionChangedListener = new ISelectionListener() { |
| |
| @Override |
| public void selectionChanged(IWorkbenchPart part, |
| ISelection selection) { |
| if (!listenOnRepositoryViewSelection |
| || part == getSite().getPart()) |
| return; |
| |
| // this may happen if we switch between editors |
| if (part instanceof IEditorPart) { |
| IEditorInput input = ((IEditorPart) part).getEditorInput(); |
| if (input instanceof IFileEditorInput) |
| setInput(new StructuredSelection( |
| ((IFileEditorInput) input).getFile())); |
| } else |
| setInput(selection); |
| } |
| }; |
| |
| ISelectionService srv = CommonUtils.getService(getSite(), ISelectionService.class); |
| srv.addPostSelectionListener(selectionChangedListener); |
| } |
| |
| private void reactOnInitalSelection() { |
| selectionChangedListener.selectionChanged(initialSelection.activeEditor, |
| initialSelection.selection); |
| this.initialSelection = null; |
| } |
| |
| private static final class InitialSelection { |
| ISelection selection; |
| |
| IEditorPart activeEditor; |
| |
| InitialSelection(ISelection selection) { |
| this.selection = selection; |
| } |
| } |
| |
| private class RebaseCommandItemSelectionListener extends SelectionAdapter { |
| |
| private final AbstractRebaseCommandHandler command; |
| |
| public RebaseCommandItemSelectionListener( |
| AbstractRebaseCommandHandler command) { |
| super(); |
| this.command = command; |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent sEvent) { |
| try { |
| command.execute(currentPlan.getRepository()); |
| } catch (ExecutionException e) { |
| Activator.showError(e.getMessage(), e); |
| } |
| } |
| } |
| |
| private class PlanViewerSelectionChangedListener implements |
| ISelectionChangedListener { |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| if (event == null) |
| return; |
| ISelection selection = event.getSelection(); |
| actionToolBarProvider.mapActionItemsToSelection(selection); |
| } |
| } |
| |
| private void createLocalDragandDrop() { |
| planTreeViewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY |
| | DND.DROP_LINK, |
| new Transfer[] { LocalSelectionTransfer.getTransfer() }, |
| new RebaseInteractiveDragSourceListener(this)); |
| planTreeViewer.addDropSupport(DND.DROP_MOVE, |
| new Transfer[] { LocalSelectionTransfer.getTransfer() }, |
| new RebaseInteractiveDropTargetListener(this, planTreeViewer)); |
| } |
| |
| private void createStepActionToolBar(Section rebasePlanSection, |
| final FormToolkit toolkit) { |
| actionToolBarProvider = new RebaseInteractiveStepActionToolBarProvider( |
| rebasePlanSection, SWT.FLAT | SWT.WRAP, this); |
| toolkit.adapt(actionToolBarProvider.getTheToolbar()); |
| toolkit.paintBordersFor(actionToolBarProvider.getTheToolbar()); |
| rebasePlanSection.setTextClient(actionToolBarProvider.getTheToolbar()); |
| } |
| |
| private static RebaseInteractivePlan.ElementType getType(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getElementType(); |
| } else |
| return null; |
| } |
| |
| private static class HighlightingColumnLabelProvider extends |
| ColumnLabelProvider { |
| |
| @Override |
| public Font getFont(Object element) { |
| ElementType t = RebaseInteractiveView.getType(element); |
| if (t != null && t == ElementType.DONE_CURRENT) |
| return UIUtils.getBoldFont(JFaceResources.DIALOG_FONT); |
| return super.getFont(element); |
| } |
| } |
| |
| // TODO: How to set column width to fit the treeViewer (maximize to not |
| // show empty space) |
| private void createColumns() { |
| String[] headings = { UIText.RebaseInteractiveView_HeadingStatus, |
| UIText.RebaseInteractiveView_HeadingStep, |
| UIText.RebaseInteractiveView_HeadingAction, |
| UIText.RebaseInteractiveView_HeadingCommitId, |
| UIText.RebaseInteractiveView_HeadingMessage, |
| UIText.RebaseInteractiveView_HeadingAuthor, |
| UIText.RebaseInteractiveView_HeadingAuthorDate, |
| UIText.RebaseInteractiveView_HeadingCommitter, |
| UIText.RebaseInteractiveView_HeadingCommitDate }; |
| |
| ColumnViewerToolTipSupport.enableFor(planTreeViewer, |
| ToolTip.NO_RECREATE); |
| |
| TreeViewerColumn infoColumn = createColumn(headings[0], 70); |
| infoColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| |
| @Override |
| public Image getImage(Object element) { |
| ElementType t = getType(element); |
| if (t != null) { |
| switch (t) { |
| case DONE_CURRENT: |
| return UIIcons |
| .getImage(resources, UIIcons.CURRENT_STEP); |
| case DONE: |
| return UIIcons.getImage(resources, UIIcons.DONE_STEP); |
| default: |
| // fall through |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public String getToolTipText(Object element) { |
| ElementType t = getType(element); |
| if (t != null) { |
| switch (t) { |
| case DONE: |
| return UIText.RebaseInteractiveView_StatusDone; |
| case DONE_CURRENT: |
| return UIText.RebaseInteractiveView_StatusCurrent; |
| case TODO: |
| return UIText.RebaseInteractiveView_StatusTodo; |
| default: |
| // fall through |
| } |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getText(Object element) { |
| return ""; //$NON-NLS-1$ |
| } |
| }); |
| |
| TreeViewerColumn stepColumn = createColumn(headings[1], 55); |
| stepColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return (planIndexer.indexOf(planLine) + 1) + "."; //$NON-NLS-1$ |
| } |
| return super.getText(element); |
| } |
| }); |
| stepColumn.getColumn().addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| Tree tree = planTreeViewer.getTree(); |
| |
| boolean orderReversed = tree.getSortDirection() == SWT.DOWN; |
| |
| RebaseInteractivePreferences.setOrderReversed(!orderReversed); |
| |
| int newDirection = (orderReversed ? SWT.UP : SWT.DOWN); |
| tree.setSortDirection(newDirection); |
| |
| TreeItem topmostVisibleItem = tree.getTopItem(); |
| refreshUI(); |
| if (topmostVisibleItem != null) |
| tree.showItem(topmostVisibleItem); |
| } |
| }); |
| |
| int direction = (RebaseInteractivePreferences.isOrderReversed() ? SWT.DOWN |
| : SWT.UP); |
| |
| Tree planTree = planTreeViewer.getTree(); |
| planTree.setSortColumn(stepColumn.getColumn()); |
| planTree.setSortDirection(direction); |
| |
| TreeViewerColumn actionColumn = createColumn(headings[2], 90); |
| actionColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| |
| @Override |
| public Image getImage(Object element) { |
| ElementAction a = getAction(element); |
| if (a != null) { |
| switch (a) { |
| case EDIT: |
| return UIIcons.getImage(resources, UIIcons.EDITCONFIG); |
| case FIXUP: |
| if (RebaseInteractivePreferences.isOrderReversed()) |
| return UIIcons.getImage(resources, |
| UIIcons.FIXUP_DOWN); |
| else |
| return UIIcons.getImage(resources, |
| UIIcons.FIXUP_UP); |
| case PICK: |
| return UIIcons.getImage(resources, UIIcons.CHERRY_PICK); |
| case REWORD: |
| return UIIcons.getImage(resources, UIIcons.REWORD); |
| case SKIP: |
| return UIIcons.getImage(resources, UIIcons.REBASE_SKIP); |
| case SQUASH: |
| if (RebaseInteractivePreferences.isOrderReversed()) |
| return UIIcons.getImage(resources, |
| UIIcons.SQUASH_DOWN); |
| else |
| return UIIcons.getImage(resources, |
| UIIcons.SQUASH_UP); |
| default: |
| // fall through |
| } |
| } |
| return super.getImage(element); |
| } |
| |
| @Override |
| public String getText(Object element) { |
| ElementAction a = getAction(element); |
| return (a != null) ? a.name() : super.getText(element); |
| } |
| |
| private ElementAction getAction(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getPlanElementAction(); |
| } else |
| return null; |
| } |
| }); |
| |
| TreeViewerColumn commitIDColumn = createColumn(headings[3], 70); |
| commitIDColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getCommit().name(); |
| } |
| return super.getText(element); |
| } |
| }); |
| |
| TreeViewerColumn commitMessageColumn = createColumn(headings[4], 200); |
| commitMessageColumn |
| .setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getShortMessage(); |
| } |
| return super.getText(element); |
| } |
| }); |
| |
| TreeViewerColumn authorColumn = createColumn(headings[5], 120); |
| authorColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getAuthor(); |
| } |
| return super.getText(element); |
| } |
| }); |
| |
| TreeViewerColumn authoredDateColumn = createColumn(headings[6], 80); |
| authoredDateColumn |
| .setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getAuthoredDate(dateFormatter); |
| } |
| return super.getText(element); |
| } |
| }); |
| |
| TreeViewerColumn committerColumn = createColumn(headings[7], 120); |
| committerColumn.setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getCommitter(); |
| } |
| return super.getText(element); |
| } |
| }); |
| |
| TreeViewerColumn commitDateColumn = createColumn(headings[8], 80); |
| commitDateColumn |
| .setLabelProvider(new HighlightingColumnLabelProvider() { |
| @Override |
| public String getText(Object element) { |
| if (element instanceof PlanElement) { |
| PlanElement planLine = (PlanElement) element; |
| return planLine.getCommittedDate(dateFormatter); |
| } |
| return super.getText(element); |
| } |
| }); |
| dynamicColumns = new TreeViewerColumn[] { commitMessageColumn, |
| authorColumn, authoredDateColumn, committerColumn, |
| commitDateColumn }; |
| } |
| |
| private TreeViewerColumn createColumn(String text, int width) { |
| TreeViewerColumn column = new TreeViewerColumn(planTreeViewer, SWT.NONE); |
| column.getColumn().setText(text); |
| column.getColumn().setMoveable(false); |
| column.getColumn().setResizable(true); |
| column.getColumn().setWidth(width); |
| return column; |
| } |
| |
| private void asyncExec(Runnable runnable) { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(runnable); |
| } |
| |
| private static String getRepositoryName(Repository repository) { |
| String repoName = Activator.getDefault().getRepositoryUtil() |
| .getRepositoryName(repository); |
| RepositoryState state = repository.getRepositoryState(); |
| if (state != RepositoryState.SAFE) |
| return repoName + '|' + state.getDescription(); |
| else |
| return repoName; |
| } |
| |
| private void showRepository(final Repository repository) { |
| if (form.isDisposed()) |
| return; |
| |
| if (currentPlan != null) |
| currentPlan.removeRebaseInteractivePlanChangeListener(this); |
| |
| if (planIndexer != null) |
| planIndexer.dispose(); |
| |
| if (isValidRepo(repository)) { |
| currentPlan = RebaseInteractivePlan.getPlan(repository); |
| planIndexer = new RebasePlanIndexer(currentPlan); |
| currentPlan.addRebaseInteractivePlanChangeListener(this); |
| form.setText(getRepositoryName(repository)); |
| } else { |
| currentPlan = null; |
| planIndexer = null; |
| form.setText(UIText.RebaseInteractiveView_NoSelection); |
| } |
| refresh(); |
| } |
| |
| private boolean isValidRepo(final Repository repository) { |
| return repository != null && !repository.isBare() |
| && repository.getWorkTree().exists(); |
| } |
| |
| void refresh() { |
| if (!isReady()) |
| return; |
| asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| Tree t = planTreeViewer.getTree(); |
| if (t.isDisposed()) |
| return; |
| t.setRedraw(false); |
| try { |
| planTreeViewer.setInput(currentPlan); |
| refreshUI(); |
| } finally { |
| t.setRedraw(true); |
| } |
| } |
| }); |
| |
| } |
| |
| private boolean isReady() { |
| IWorkbenchPartSite site = this.getSite(); |
| if (site == null) |
| return false; |
| return !site.getShell().isDisposed(); |
| } |
| |
| private void refreshUI() { |
| dateFormatter = getNewDateFormatter(); |
| if (planTreeViewer != null) { |
| planTreeViewer.refresh(true); |
| // make column widths match the contents |
| for (TreeViewerColumn col : dynamicColumns) |
| col.getColumn().pack(); |
| } |
| |
| startItem.setEnabled(false); |
| continueItem.setEnabled(false); |
| skipItem.setEnabled(false); |
| abortItem.setEnabled(false); |
| dndEnabled = false; |
| |
| actionToolBarProvider.getTheToolbar().setEnabled(false); |
| |
| if (currentPlan == null || !currentPlan.isRebasingInteractive()) { |
| if (currentRepository == null) |
| form.setText(UIText.RebaseInteractiveView_NoSelection); |
| else |
| form.setText(getRepositoryName(currentRepository)); |
| return; |
| } |
| |
| actionToolBarProvider.mapActionItemsToSelection(planTreeViewer |
| .getSelection()); |
| if (!currentPlan.hasRebaseBeenStartedYet()) { |
| if (!planTreeViewer.getSelection().isEmpty()) |
| actionToolBarProvider.getTheToolbar().setEnabled(true); |
| |
| startItem.setEnabled(true); |
| abortItem.setEnabled(true); |
| dndEnabled = true; |
| } else { |
| continueItem.setEnabled(true); |
| skipItem.setEnabled(true); |
| abortItem.setEnabled(true); |
| } |
| |
| if (RebaseInteractivePreferences.isOrderReversed()) { |
| Tree tree = planTreeViewer.getTree(); |
| int itemCount = tree.getItemCount(); |
| if (itemCount > 0) { |
| TreeItem bottomItem = tree.getItem(itemCount - 1); |
| tree.showItem(bottomItem); |
| } |
| } |
| } |
| |
| private void createPopupMenu(final TreeViewer planViewer) { |
| createContextMenuItems(planViewer); |
| |
| MenuManager manager = new MenuManager(); |
| manager.addMenuListener(new IMenuListener() { |
| @Override |
| public void menuAboutToShow(IMenuManager menuManager) { |
| boolean selectionNotEmpty = !planViewer.getSelection() |
| .isEmpty(); |
| boolean rebaseNotStarted = !currentPlan |
| .hasRebaseBeenStartedYet(); |
| boolean menuEnabled = selectionNotEmpty && rebaseNotStarted; |
| for (PlanContextMenuAction item : contextMenuItems) |
| item.setEnabled(menuEnabled); |
| } |
| }); |
| |
| for (PlanContextMenuAction item : contextMenuItems) |
| manager.add(item); |
| |
| Menu menu = manager.createContextMenu(planViewer.getControl()); |
| planViewer.getControl().setMenu(menu); |
| } |
| |
| private void createContextMenuItems(final TreeViewer planViewer) { |
| contextMenuItems = new ArrayList<PlanContextMenuAction>(); |
| |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_PickText, |
| UIIcons.CHERRY_PICK, RebaseInteractivePlan.ElementAction.PICK, |
| planViewer, actionToolBarProvider)); |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_SkipText, |
| UIIcons.REBASE_SKIP, RebaseInteractivePlan.ElementAction.SKIP, |
| planViewer, actionToolBarProvider)); |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_EditText, |
| UIIcons.EDITCONFIG, RebaseInteractivePlan.ElementAction.EDIT, |
| planViewer, actionToolBarProvider)); |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_SquashText, |
| UIIcons.SQUASH_UP, RebaseInteractivePlan.ElementAction.SQUASH, |
| planViewer, actionToolBarProvider)); |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_FixupText, |
| UIIcons.FIXUP_UP, RebaseInteractivePlan.ElementAction.FIXUP, |
| planViewer, actionToolBarProvider)); |
| contextMenuItems.add(new PlanContextMenuAction( |
| UIText.RebaseInteractiveStepActionToolBarProvider_RewordText, |
| UIIcons.REWORD, RebaseInteractivePlan.ElementAction.REWORD, |
| planViewer, actionToolBarProvider)); |
| } |
| |
| private static GitDateFormatter getNewDateFormatter() { |
| boolean useRelativeDates = Activator.getDefault().getPreferenceStore() |
| .getBoolean(UIPreferences.RESOURCEHISTORY_SHOW_RELATIVE_DATE); |
| if (useRelativeDates) |
| return new GitDateFormatter(Format.RELATIVE); |
| else |
| return PreferenceBasedDateFormatter.create(); |
| } |
| |
| @Override |
| public void setFocus() { |
| planTreeViewer.getControl().setFocus(); |
| } |
| |
| boolean isDragAndDropEnabled() { |
| return dndEnabled; |
| } |
| |
| @Override |
| public void planWasUpdatedFromRepository(final RebaseInteractivePlan plan) { |
| refresh(); |
| } |
| |
| @Override |
| public void planElementTypeChanged( |
| RebaseInteractivePlan rebaseInteractivePlan, PlanElement element, |
| ElementAction oldType, ElementAction newType) { |
| planTreeViewer.refresh(element, true); |
| } |
| |
| @Override |
| public void planElementsOrderChanged( |
| RebaseInteractivePlan rebaseInteractivePlan, PlanElement element, |
| int oldIndex, int newIndex) { |
| planTreeViewer.refresh(true); |
| } |
| } |