package net.enilink.komma.edit.ui.action; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.WeakHashMap; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IContributionManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.SubContributionItem; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.progress.WorkbenchJob; import net.enilink.komma.common.util.ICollector; import net.enilink.komma.edit.ui.provider.ExtendedImageRegistry; import net.enilink.komma.edit.util.CollectorJob; /** * {@link ICollector} implementation that can be used to populate multiple menu * managers with actions. */ abstract public class MenuActionCollector<T> extends CollectorJob<T> { protected boolean shouldSchedule = true; private static final IContributionItem loadingIndicatorItem = new ActionContributionItem( new Action("loading ...") { }); private static final int MAX_MENU_ENTRIES = 20; protected Collection<IAction> menuActions; protected Collection<IAction> allActions; protected WeakHashMap<IMenuManager, Boolean> menuManagers = new WeakHashMap<IMenuManager, Boolean>(); protected volatile ISelection selection; protected Display display = Display.getCurrent(); protected List<Job> handlers = new ArrayList<Job>(); class ShowAllCreateActions extends Action { public ShowAllCreateActions() { super("other..."); } @Override public void run() { ElementListSelectionDialog selectionDialog = new ElementListSelectionDialog( display.getActiveShell(), new LabelProvider() { @Override public String getText(Object element) { return ((IAction) element).getText(); } @Override public Image getImage(Object element) { return ExtendedImageRegistry.getInstance() .getImage( ((IAction) element) .getImageDescriptor()); } }); selectionDialog.setHelpAvailable(false); selectionDialog.setElements(MenuActionCollector.this.allActions .toArray()); if (selectionDialog.open() == Window.OK) { IAction selected = (IAction) selectionDialog.getFirstResult(); if (selected != null) { selected.run(); } } } }; public MenuActionCollector(String name, ISelection selection) { super(name); this.selection = selection; } public void addMenuManager(IMenuManager menuManager) { if (menuManager != null && menuManagers.put(menuManager, true) == null) { populateManager(menuManager, menuActions, loadingIndicatorItem.getId()); } } @Override protected void canceling() { super.canceling(); selection = null; } /** * This removes from the specified <code>manager</code> all * {@link org.eclipse.jface.action.ActionContributionItem}s based on the * {@link org.eclipse.jface.action.IAction}s contained in the * <code>actions</code> collection. */ protected void depopulateManager(IContributionManager manager, Collection<? extends IAction> actions) { manager.remove(loadingIndicatorItem); if (actions != null) { IContributionItem[] items = manager.getItems(); for (int i = 0; i < items.length; i++) { // Look into SubContributionItems IContributionItem contributionItem = items[i]; while (contributionItem instanceof SubContributionItem) { contributionItem = ((SubContributionItem) contributionItem) .getInnerItem(); } if (contributionItem instanceof ActionContributionItem) { IAction action = ((ActionContributionItem) contributionItem) .getAction(); if (actions.contains(action)) { manager.remove(contributionItem); } } } } } public void dispose() { cancel(); synchronized (handlers) { for (Job handler : handlers) { handler.cancel(); } handlers.clear(); handlers.notifyAll(); } try { join(); } catch (InterruptedException e) { // ignore } for (IMenuManager menuManager : menuManagers.keySet()) { depopulateManager(menuManager, menuActions); } selection = null; } @Override protected void done() { super.done(); synchronized (handlers) { while (!handlers.isEmpty()) { try { handlers.wait(); } catch (InterruptedException e) { return; } } } } @Override public boolean shouldSchedule() { // force job to be only scheduled once if (shouldSchedule) { shouldSchedule = false; return true; } return false; } @Override protected void handleObjects(final Collection<T> descriptors) { final Job handler = new WorkbenchJob(display, "Create actions") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { try { if (selection != null) { int startActionsSize = allActions == null ? 0 : allActions.size(); Collection<IAction> newActions = generateActions(descriptors); if (allActions == null) { allActions = newActions; } else { allActions.addAll(newActions); } if (allActions.size() > MAX_MENU_ENTRIES) { // show only MAX_MENU_ENTRIES if (startActionsSize < MAX_MENU_ENTRIES) { newActions = new ArrayList<IAction>(newActions) .subList(0, MAX_MENU_ENTRIES - startActionsSize); newActions.add(new ShowAllCreateActions()); } else { // do not add any more actions return Status.OK_STATUS; } } if (menuActions == null) { menuActions = new ArrayList<IAction>(); } menuActions.addAll(newActions); for (IMenuManager menuManager : menuManagers.keySet()) { populateManager(menuManager, newActions, loadingIndicatorItem.getId()); menuManager.update(true); } } return Status.OK_STATUS; } finally { synchronized (handlers) { handlers.remove(this); handlers.notify(); } } } }; synchronized (handlers) { handlers.add(handler); } handler.schedule(); } abstract protected Collection<IAction> generateActions( Collection<T> descriptors); /** * This populates the specified <code>manager</code> with * {@link org.eclipse.jface.action.ActionContributionItem}s based on the * {@link org.eclipse.jface.action.IAction}s contained in the * <code>actions</code> collection, by inserting them before the specified * contribution item <code>contributionID</code>. If * <code>contributionID</code> is <code>null</code>, they are simply added. */ protected void populateManager(IContributionManager manager, Collection<? extends IAction> actions, String contributionID) { if (actions != null) { if (done) { manager.remove(loadingIndicatorItem); } for (IAction action : actions) { if (contributionID != null) { manager.insertBefore(contributionID, action); } else { manager.add(action); } } } else { manager.add(loadingIndicatorItem); } } }