package com.aptana.rdt.internal.rake.actions; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.events.MenuAdapter; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbenchPart; import com.aptana.rdt.rake.IRakeHelper; import com.aptana.rdt.rake.RakePlugin; public class RakeAction implements IObjectActionDelegate, IMenuCreator { private static final String RAKE_NAMESPACE_DELIMETER = ":"; private boolean fFillMenu; private IAction fDelegateAction; private IStructuredSelection fSelection; private HashMap<String, MenuManager> fNamespaces; private Menu menu; public RakeAction() { super(); } public void setActivePart(IAction action, IWorkbenchPart targetPart) { // We don't have a need for the active part. } public void run(IAction action) { // Never called because we become a menu. } public void selectionChanged(IAction action, ISelection selection) { // if the selection is an IResource, save it and enable our action if (selection instanceof IStructuredSelection) { fFillMenu = true; if (fDelegateAction != action) { fDelegateAction = action; fDelegateAction.setMenuCreator(this); } // save selection and enable our menu fSelection = (IStructuredSelection) selection; action.setEnabled(true); return; } action.setEnabled(false); } public void dispose() { if (menu != null) menu.dispose(); menu = null; } public Menu getMenu(Control parent) { // never called return null; } public Menu getMenu(Menu parent) { // Create the new menu. The menu will get filled when it is about to be shown. see fillMenu(Menu). menu = new Menu(parent); /** * Add listener to re-populate the menu each time * it is shown because MenuManager.update(boolean, boolean) * doesn't dispose pull-down ActionContribution items for each popup menu. */ menu.addMenuListener(new MenuAdapter() { public void menuShown(MenuEvent e) { if (fFillMenu) { Menu m = (Menu)e.widget; MenuItem[] items = m.getItems(); for (int i=0; i < items.length; i++) { items[i].dispose(); } fillMenu(m); fFillMenu = false; } } }); return menu; } /** * Fills the menu with applicable launch shortcuts * @param menu The menu to fill */ protected void fillMenu(Menu menu) { if (fSelection == null) { return; } IResource resource = (IResource) fSelection.getFirstElement(); IProject project = resource.getProject(); Map<String, String> tasks = getRakeHelper().getTasks(project, new NullProgressMonitor()); fNamespaces = new HashMap<String, MenuManager>(); // Please note that tehre's a lot of code mixed up in here to ensure that the menus, items and sub-menus all appear alphabetically List<String> values = new ArrayList<String>(tasks.keySet()); Collections.sort(values); for (String task : values) { String[] paths = task.split(RAKE_NAMESPACE_DELIMETER); if (paths.length == 1) { IAction action = new RunRakeAction(project, task, tasks.get(task)); ActionContributionItem item= new ActionContributionItem(action); item.fill(menu, -1); } else { MenuManager manager = getOrCreate(paths); manager.add(new RunRakeAction(project, task, tasks.get(task))); } } values = new ArrayList<String>(fNamespaces.keySet()); Collections.sort(values); Collections.reverse(values); for (String path : values) { MenuManager manager = fNamespaces.get(path); String[] parts = path.split(RAKE_NAMESPACE_DELIMETER); if (parts.length == 1) { int index = getInsertIndex(menu, manager); manager.fill(menu, index); } else { MenuManager parent = getParent(parts); if (parent != null) { int index = getInsertIndex(parent, manager); parent.insert(index, manager); } else { int index = getInsertIndex(menu, manager); manager.fill(menu, index); } } } } /** * For inserting submenus under submenus * @param parent * @param item * @return */ private int getInsertIndex(MenuManager parent, MenuManager item) { if (parent == null || item == null) return 0; String text = item.getMenuText(); if (text == null) return 0; IContributionItem[] items = parent.getItems(); if (items == null) return 0; int index = 0; for (int i = 0; i < items.length; i++) { if (items[i] == null) continue; if (items[i] instanceof ActionContributionItem) { ActionContributionItem actionItem = (ActionContributionItem) items[i]; IAction action = actionItem.getAction(); if (action == null) continue; String other = action.getText(); if (text.compareTo(other) >= 0) { index = i + 1; } else { break; } } } return index; } /** * For inserting submenus at first level. * * @param parent * @param item * @return */ private int getInsertIndex(Menu parent, MenuManager item) { String text = item.getMenuText(); MenuItem[] items = parent.getItems(); int index = 0; for (int i = 0; i < items.length; i++) { String other = items[i].getText(); if (text.compareTo(other) >= 0) { index = i + 1; } else { break; } } return index; } protected IRakeHelper getRakeHelper() { return RakePlugin.getDefault().getRakeHelper(); } private MenuManager getParent(String[] parts) { String[] part = stripLastItem(parts); return fNamespaces.get(join(part)); } private String join(String[] part) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < part.length; i++) { if (i != 0) buffer.append(RAKE_NAMESPACE_DELIMETER); buffer.append(part[i]); } return buffer.toString(); } private MenuManager getOrCreate(String[] paths) { String[] part = stripLastItem(paths); MenuManager manager = fNamespaces.get(join(part)); if (manager == null) { manager = new MenuManager(part[part.length - 1]); fNamespaces.put(join(part), manager); } return manager; } private String[] stripLastItem(String[] paths) { String[] part = new String[paths.length - 1]; System.arraycopy(paths, 0, part, 0, part.length); return part; } }