Hi, I introduce GitAction as base class for all actions. It allows to disable the action menu item if the selection does not live in a Git controlled repository. The current code is quite "gruik". It is mainly based on copy/paste from other plugins. A better solution could be to avoid inserting EGit actions in menus if the selection does not live in a Git repo. (some plugin.xml hack) -- Guilhem BONNEFILLE -=- #UIN: 15146515 JID: guyou@xxxxxxxxxxxx MSN: guilhem_bonnefille@xxxxxxxxxxx -=- mailto:guilhem.bonnefille@xxxxxxxxx -=- http://nathguil.free.fr/
diff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF index b7301a6..b7dd10d 100644 --- a/org.spearce.egit.core/META-INF/MANIFEST.MF +++ b/org.spearce.egit.core/META-INF/MANIFEST.MF @@ -17,6 +17,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.ui.ide, org.eclipse.ui.workbench, org.eclipse.ui.workbench.texteditor -Export-Package: org.spearce.egit.core.op, +Export-Package: org.spearce.egit.core, + org.spearce.egit.core.internal.mapping;x-internal:=true, + org.spearce.egit.core.op, org.spearce.egit.core.project Eclipse-LazyStart: true diff --git a/org.spearce.egit.ui/META-INF/MANIFEST.MF b/org.spearce.egit.ui/META-INF/MANIFEST.MF index f188478..bf90784 100644 --- a/org.spearce.egit.ui/META-INF/MANIFEST.MF +++ b/org.spearce.egit.ui/META-INF/MANIFEST.MF @@ -14,5 +14,6 @@ Require-Bundle: org.eclipse.core.runtime, org.spearce.jgit, org.spearce.egit.core, org.eclipse.compare, - org.eclipse.core.filesystem + org.eclipse.core.filesystem, + org.eclipse.jface.text Eclipse-LazyStart: true diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java index d1a8d7c..c68e158 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java @@ -20,8 +20,12 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.team.core.TeamException; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; +import org.spearce.egit.core.GitProvider; public class Activator extends AbstractUIPlugin { private static Activator plugin; @@ -34,6 +38,28 @@ public class Activator extends AbstractUIPlugin { return getDefault().getBundle().getSymbolicName(); } + /** + * This is the provider ID of the plugin as defined in the plugin.xml + * FIXME Inspired/Copied from FileSystem example. Specialize to EGit + */ + public static String getPluginProviderId() { + return GitProvider.class.getName(); + } + + /** + * Convenience method to get the currently active workbench page. Note that + * the active page may not be the one that the usr perceives as active in + * some situations so this method of obtaining the activae page should only + * be used if no other method is available. + * + * @return the active workbench page + */ + public static IWorkbenchPage getActivePage() { + IWorkbenchWindow window = getDefault().getWorkbench().getActiveWorkbenchWindow(); + if (window == null) return null; + return window.getActivePage(); + } + public static CoreException error(final String message, final Throwable thr) { return new CoreException(new Status(IStatus.ERROR, getPluginId(), 0, message, thr)); @@ -43,6 +69,10 @@ public class Activator extends AbstractUIPlugin { getDefault().getLog().log( new Status(IStatus.ERROR, getPluginId(), 0, message, thr)); } + + public static void log(TeamException e) { + getDefault().getLog().log(new Status(e.getStatus().getSeverity(), getPluginId(), 0, "simpleInternal", e)); //$NON-NLS-1$ + } private static boolean isOptionSet(final String optionId) { final String option = getPluginId() + optionId; diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/AbstractOperationAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/AbstractOperationAction.java index 759189e..d2af9de 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/AbstractOperationAction.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/AbstractOperationAction.java @@ -17,7 +17,6 @@ package org.spearce.egit.ui.internal.actions; import java.lang.reflect.InvocationTargetException; -import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IWorkspaceRunnable; @@ -28,29 +27,15 @@ import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbenchPart; import org.spearce.egit.ui.Activator; import org.spearce.egit.ui.UIText; -public abstract class AbstractOperationAction implements IObjectActionDelegate { +public abstract class AbstractOperationAction extends GitAction { private IWorkbenchPart wp; private IWorkspaceRunnable op; - public void selectionChanged(final IAction act, final ISelection sel) { - final List selection; - if (sel instanceof IStructuredSelection && !sel.isEmpty()) { - selection = ((IStructuredSelection) sel).toList(); - } else { - selection = Collections.EMPTY_LIST; - } - op = createOperation(act, selection); - act.setEnabled(op != null && wp != null); - } - public void setActivePart(final IAction act, final IWorkbenchPart part) { wp = part; } @@ -62,6 +47,7 @@ public abstract class AbstractOperationAction implements IObjectActionDelegate { } public void run(final IAction act) { + op = createOperation(act, getSelection().toList()); if (op != null) { try { try { diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CompareWithRevisionAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CompareWithRevisionAction.java index bb7fbc7..205536c 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CompareWithRevisionAction.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CompareWithRevisionAction.java @@ -22,12 +22,10 @@ import java.util.Hashtable; import org.eclipse.core.resources.IResource; import org.eclipse.jface.action.IAction; import org.eclipse.swt.widgets.Shell; -import org.eclipse.team.core.TeamException; -import org.eclipse.team.internal.ui.actions.TeamAction; import org.eclipse.team.ui.TeamUI; import org.eclipse.team.ui.history.HistoryPageSaveablePart; -public class CompareWithRevisionAction extends TeamAction { +public class CompareWithRevisionAction extends GitAction { public void run(IAction action) { super.run(action); @@ -45,9 +43,4 @@ public class CompareWithRevisionAction extends TeamAction { protected void showCompareInDialog(Shell shell, Object object) { HistoryPageSaveablePart.showHistoryInDialog(shell, object); } - - protected boolean isEnabled() throws TeamException { - return !getSelection().isEmpty(); - } - } diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitAction.java new file mode 100644 index 0000000..0769e38 --- /dev/null +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitAction.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (C) 2006 Guilhem Bonnefille + * 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.spearce.egit.ui.internal.actions; + +import org.eclipse.core.resources.mapping.ResourceMapping; +import org.spearce.egit.ui.Activator; + +/** + * An abstract class that acts as a super class for FileSystemProvider actions. + * It provides some general methods applicable to multiple actions. + */ +public abstract class GitAction extends TeamAction { + + /** + * @see TeamAction#isEnabled() + */ + protected boolean isEnabled() { + return getSelectedMappings().length > 0; + } + + /** + * Return the selected resource mappings that are associated with the + * file system provider. + * @return the selected resource mappings that are associated with the + * file system provider. + */ + protected ResourceMapping[] getSelectedMappings() { + return getSelectedResourceMappings(Activator.getPluginProviderId()); + } +} diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ShowResourceInHistoryAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ShowResourceInHistoryAction.java index 3e6b870..f48c5e5 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ShowResourceInHistoryAction.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ShowResourceInHistoryAction.java @@ -18,12 +18,10 @@ package org.spearce.egit.ui.internal.actions; import org.eclipse.jface.action.IAction; import org.eclipse.swt.widgets.Shell; -import org.eclipse.team.core.TeamException; -import org.eclipse.team.internal.ui.actions.TeamAction; import org.eclipse.team.ui.TeamUI; import org.eclipse.team.ui.history.HistoryPageSaveablePart; -public class ShowResourceInHistoryAction extends TeamAction { +public class ShowResourceInHistoryAction extends GitAction { public void run(IAction action) { TeamUI.getHistoryView().showHistoryFor(getSelectedResources()[0]); @@ -32,8 +30,9 @@ public class ShowResourceInHistoryAction extends TeamAction { protected void showCompareInDialog(Shell shell, Object object) { HistoryPageSaveablePart.showHistoryInDialog(shell, object); } - +/* protected boolean isEnabled() throws TeamException { return !getSelection().isEmpty(); } + */ } diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/TeamAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/TeamAction.java new file mode 100644 index 0000000..fbcfcb7 --- /dev/null +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/TeamAction.java @@ -0,0 +1,418 @@ +/******************************************************************************* + * copied from: org.eclipse.team.internal.ui.actions.TeamAction + * + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.spearce.egit.ui.internal.actions; + + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.resources.mapping.ResourceMapping; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.team.core.RepositoryProvider; +import org.eclipse.team.core.TeamException; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.internal.LegacyResourceSupport; +import org.spearce.egit.ui.Activator; + +/** + * The abstract superclass of all Team actions. This class contains some convenience + * methods for getting selected objects and mapping selected objects to their + * providers. + * + * Team providers may subclass this class when creating their actions. + * Team providers may also instantiate or subclass any of the + * subclasses of TeamAction provided in this package. + */ +public abstract class TeamAction extends ActionDelegate implements IObjectActionDelegate, IViewActionDelegate { + // The current selection + protected IStructuredSelection selection; + + // The shell, required for the progress dialog + protected Shell shell; + + // Constants for determining the type of progress. Subclasses may + // pass one of these values to the run method. + public final static int PROGRESS_DIALOG = 1; + public final static int PROGRESS_BUSYCURSOR = 2; + + private IWorkbenchPart targetPart; + + /** + * Creates an array of the given class type containing all the + * objects in the selection that adapt to the given class. + * + * @param selection + * @param c + * @return + */ + public static Object[] getSelectedAdaptables(ISelection selection, Class c) { + ArrayList result = null; + if (!selection.isEmpty()) { + result = new ArrayList(); + Iterator elements = ((IStructuredSelection) selection).iterator(); + while (elements.hasNext()) { + Object adapter = getAdapter(elements.next(), c); + if (c.isInstance(adapter)) { + result.add(adapter); + } + } + } + if (result != null && !result.isEmpty()) { + return result.toArray((Object[])Array.newInstance(c, result.size())); + } + return (Object[])Array.newInstance(c, 0); + } + + /** + * Find the object associated with the given object when it is adapted to + * the provided class. Null is returned if the given object does not adapt + * to the given class + * + * @param selection + * @param c + * @return Object + */ + public static Object getAdapter(Object adaptable, Class c) { + if (c.isInstance(adaptable)) { + return adaptable; + } + if (adaptable instanceof IAdaptable) { + IAdaptable a = (IAdaptable) adaptable; + Object adapter = a.getAdapter(c); + if (c.isInstance(adapter)) { + return adapter; + } + } + return null; + } + + /** + * Returns the selected projects. + * + * @return the selected projects + */ + protected IProject[] getSelectedProjects() { + IResource[] selectedResources = getSelectedResources(); + if (selectedResources.length == 0) return new IProject[0]; + ArrayList projects = new ArrayList(); + for (int i = 0; i < selectedResources.length; i++) { + IResource resource = selectedResources[i]; + if (resource.getType() == IResource.PROJECT) { + projects.add(resource); + } + } + return (IProject[]) projects.toArray(new IProject[projects.size()]); + } + + /** + * Returns an array of the given class type c that contains all + * instances of c that are either contained in the selection or + * are adapted from objects contained in the selection. + * + * @param c + * @return + */ + protected Object[] getSelectedResources(Class c) { + return getSelectedAdaptables(selection, c); + } + + /** + * Returns the selected resources. + * + * @return the selected resources + */ + protected IResource[] getSelectedResources() { + return (IResource[])getSelectedResources(IResource.class); + } + + /** + * Convenience method for getting the current shell. + * + * @return the shell + */ + public Shell getShell() { + if (shell != null) { + return shell; + } else { + IWorkbench workbench = Activator.getDefault().getWorkbench(); + if (workbench == null) return null; + IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); + if (window == null) return null; + return window.getShell(); + } + } + /** + * Convenience method for running an operation with progress and + * error feedback. + * + * @param runnable the runnable which executes the operation + * @param problemMessage the message to display in the case of errors + * @param progressKind one of PROGRESS_BUSYCURSOR or PROGRESS_DIALOG + */ + final protected void run(final IRunnableWithProgress runnable, final String problemMessage, int progressKind) { + final Exception[] exceptions = new Exception[] {null}; + switch (progressKind) { + case PROGRESS_BUSYCURSOR : + BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { + public void run() { + try { + runnable.run(new NullProgressMonitor()); + } catch (InvocationTargetException e) { + exceptions[0] = e; + } catch (InterruptedException e) { + exceptions[0] = null; + } + } + }); + break; + default : + case PROGRESS_DIALOG : + try { + new ProgressMonitorDialog(getShell()).run(true, true, runnable); + } catch (InvocationTargetException e) { + exceptions[0] = e; + } catch (InterruptedException e) { + exceptions[0] = null; + } + break; + } + if (exceptions[0] != null) { + handle(exceptions[0], null, problemMessage); + } + } + + /* + * Method declared on IActionDelegate. + */ + public void selectionChanged(IAction action, ISelection sel) { + if (sel instanceof IStructuredSelection) { + this.selection = (IStructuredSelection) sel; + if (action != null) { + setActionEnablement(action); + } + } + if (sel instanceof ITextSelection){ + IEditorPart part = getTargetPage().getActiveEditor(); + if (part != null) { + IEditorInput input = part.getEditorInput(); + IResource r = (IResource) input.getAdapter(IResource.class); + if (r != null) { + switch(r.getType()){ + case IResource.FILE: + this.selection = new StructuredSelection(r); + if (action != null) { + setActionEnablement(action); + } + break; + } + } // set selection to current editor file; + } + } + } + + /** + * Method invoked from <code>selectionChanged(IAction, ISelection)</code> + * to set the enablement status of the action. The instance variable + * <code>selection</code> will contain the latest selection so the methods + * <code>getSelectedResources()</code> and <code>getSelectedProjects()</code> + * will provide the proper objects. + * + * This method can be overridden by subclasses but should not be invoked by them. + */ + protected void setActionEnablement(IAction action) { + try { + action.setEnabled(isEnabled()); + } catch (TeamException e) { + if (e.getStatus().getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL) { + // Enable the action to allow the user to discover the problem + action.setEnabled(true); + } else { + action.setEnabled(false); + // We should not open a dialog when determining menu enablements so log it instead + Activator.log(e); + } + } + } + + /* + * Method declared on IObjectActionDelegate. + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + if(targetPart != null) { + this.shell = targetPart.getSite().getShell(); + this.targetPart = targetPart; + } + } + /** + * Shows the given errors to the user. + * + * @param status the status containing the error + * @param title the title of the error dialog + * @param message the message for the error dialog + * @param shell the shell to open the error dialog in + */ + protected void handle(Exception exception, String title, String message) { + // FIXME Utils.handleError(getShell(), exception, title, message); + } + + /** + * Concrete action enablement code. + * Subclasses must implement. + * + * @return whether the action is enabled + * @throws TeamException if an error occurs during enablement detection + */ + abstract protected boolean isEnabled() throws TeamException; + + /** + * Convenience method that maps the selected resources to their providers. + * The returned Hashtable has keys which are ITeamProviders, and values + * which are Lists of IResources that are shared with that provider. + * + * @return a hashtable mapping providers to their selected resources + */ + protected Hashtable getProviderMapping() { + return getProviderMapping(getSelectedResources()); + } + /** + * Convenience method that maps the given resources to their providers. + * The returned Hashtable has keys which are ITeamProviders, and values + * which are Lists of IResources that are shared with that provider. + * + * @return a hashtable mapping providers to their resources + */ + protected Hashtable getProviderMapping(IResource[] resources) { + Hashtable result = new Hashtable(); + for (int i = 0; i < resources.length; i++) { + RepositoryProvider provider = RepositoryProvider.getProvider(resources[i].getProject()); + List list = (List)result.get(provider); + if (list == null) { + list = new ArrayList(); + result.put(provider, list); + } + list.add(resources[i]); + } + return result; + } + + protected IStructuredSelection getSelection() { + if (selection == null) + selection = StructuredSelection.EMPTY; + return selection; + } + +/** + * Return the selected resource mappins that contain resources in + * projects that are associated with a repository of the given id. + * @param providerId the repository provider id + * @return the resource mappings that contain resources associated with the given provider + */ + protected ResourceMapping[] getSelectedResourceMappings(String providerId) { + Object[] elements = getSelection().toArray(); + ArrayList providerMappings = new ArrayList(); + for (int i = 0; i < elements.length; i++) { + Object object = elements[i]; + Object adapted = getResourceMapping(object); + if (adapted instanceof ResourceMapping) { + ResourceMapping mapping = (ResourceMapping) adapted; + if (providerId == null || isMappedToProvider(mapping, providerId)) { + providerMappings.add(mapping); + } + } + } + return (ResourceMapping[]) providerMappings.toArray(new ResourceMapping[providerMappings.size()]); + } + + private Object getResourceMapping(Object object) { + if (object instanceof ResourceMapping) + return (ResourceMapping)object; + return LegacyResourceSupport.getAdaptedContributorResourceMapping(object); + } + + private boolean isMappedToProvider(ResourceMapping element, String providerId) { + IProject[] projects = element.getProjects(); + for (int k = 0; k < projects.length; k++) { + IProject project = projects[k]; + RepositoryProvider provider = RepositoryProvider.getProvider(project); + if (provider != null && provider.getID().equals(providerId)) { + return true; + } + } + return false; + } + /** + * @return IWorkbenchPart + */ + protected IWorkbenchPart getTargetPart() { + return targetPart; + } + + /** + * Return the path that was active when the menu item was selected. + * @return IWorkbenchPage + */ + protected IWorkbenchPage getTargetPage() { + if (getTargetPart() == null) return Activator.getActivePage(); + return getTargetPart().getSite().getPage(); + } + + /** + * Show the view with the given ID in the perspective from which the action + * was executed. Returns null if the view is not registered. + * + * @param viewId + * @return IViewPart + */ + protected IViewPart showView(String viewId) { + try { + return getTargetPage().showView(viewId); + } catch (PartInitException pe) { + return null; + } + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart) + */ + public void init(IViewPart view) { + targetPart = view; + } +}