diff options
Diffstat (limited to 'eclipse')
5 files changed, 249 insertions, 168 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java index ef426df..f5ad935 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java @@ -823,7 +823,7 @@ public class ConfigurationComposite extends Composite { if (configIndex != -1) { String configName = mDeviceConfigCombo.getItem(configIndex); FolderConfiguration currentConfig = mState.device.getFolderConfigByName(configName); - if (mEditedConfig.isMatchFor(currentConfig)) { + if (currentConfig != null && mEditedConfig.isMatchFor(currentConfig)) { currentConfigIsCompatible = true; // current config is compatible if (needBestMatch == false || isCurrentFileBestMatchFor(currentConfig)) { needConfigChange = false; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java index b22999e..fbac3f0 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.editors.layout.gscripts.IDragElement; import com.android.ide.eclipse.adt.editors.layout.gscripts.INode; +import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule; import com.android.ide.eclipse.adt.editors.layout.gscripts.MenuAction; import com.android.ide.eclipse.adt.editors.layout.gscripts.Point; import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect; @@ -1780,8 +1781,20 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { action.setDisabledImageDescriptor(wa.getDisabledImageDescriptor()); } + /** + * Creates the context menu for the canvas. This is called once from the canvas' constructor. + * <p/> + * The menu has a static part with actions that are always available such as + * copy, cut, paste and show in > explorer. This is created by + * {@link #setupStaticMenuActions(IMenuManager)}. + * <p/> + * There's also a dynamic part that is populated by the groovy rules of the + * selected elements. This part is created by {@link #populateDynamicContextMenu(MenuManager)} + * when the {@link MenuManager}'s <code>menuAboutToShow</code> method is invoked. + */ private void createContextMenu() { - // Create the menu manager and fill it with the static actions. + + // This manager is the root of the context menu. mMenuManager = new MenuManager() { @Override public boolean isDynamic() { @@ -1789,44 +1802,60 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { } }; - createMenuAction(mMenuManager); + // Fill the menu manager with the static actions. + setupStaticMenuActions(mMenuManager); + setupDynamicMenuActions(mMenuManager); Menu menu = mMenuManager.createContextMenu(this); setMenu(menu); + } + /** + * Setups the menu manager to receive dynamic menu contributions from the {@link IViewRule}s + * when it's about to be shown. + * <p/> + * Implementation detail: this method is package protected as it is also used by + * {@link OutlinePage2} to create the exact same dynamic context menu. This means that this + * methods and all its descendant must <em>not</em> access the local {@link #mMenuManager} + * variable. + * + * @param menuManager The menu manager to modify. + */ + /*package*/ void setupDynamicMenuActions(final MenuManager menuManager) { // Remember how many static actions we have. Then each time the menu is // shown, find dynamic contributions based on the current selection and insert // them at the beginning of the menu. - final int numStaticActions = mMenuManager.getSize(); - mMenuManager.addMenuListener(new IMenuListener() { + final int numStaticActions = menuManager.getSize(); + menuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { // Remove any previous dynamic contributions to keep only the // default static items. - int n = mMenuManager.getSize() - numStaticActions; + int n = menuManager.getSize() - numStaticActions; if (n > 0) { - IContributionItem[] items = mMenuManager.getItems(); + IContributionItem[] items = menuManager.getItems(); for (int i = 0; i < n; i++) { - mMenuManager.remove(items[i]); + menuManager.remove(items[i]); } } - populateDynamicContextMenu(); + // Now add all the dynamic menu actions depending on the current selection. + populateDynamicContextMenu(menuManager); } }); } /** - * Invoked by the constructor to create our *static* context menu. + * Invoked by {@link #createContextMenu()} to create our *static* context menu once. * <p/> * The content of the menu itself does not change. However the state of the * various items is controlled by their associated actions. * <p/> - * For cut/copy/paste/delete/select-all, we explicitely reuse the actions + * For cut/copy/paste/delete/select-all, we explicitly reuse the actions * created by {@link #setupGlobalActionHandlers()}, so this method must be * invoked after that one. */ - private void createMenuAction(IMenuManager manager) { + private void setupStaticMenuActions(IMenuManager manager) { manager.removeAll(); manager.add(mCutAction); @@ -1840,24 +1869,115 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { manager.add(new Separator()); + // Create a "Show In" sub-menu and automatically populate it using standard + // actions contributed by the workbench. String showInLabel = IDEWorkbenchMessages.Workbench_showIn; MenuManager showInSubMenu= new MenuManager(showInLabel); - showInSubMenu.add(ContributionItemFactory.VIEWS_SHOW_IN.create( - mLayoutEditor.getSite().getWorkbenchWindow())); + showInSubMenu.add( + ContributionItemFactory.VIEWS_SHOW_IN.create( + mLayoutEditor.getSite().getWorkbenchWindow())); manager.add(showInSubMenu); } - - private void populateDynamicContextMenu() { - // Collect actions for current selection - + /** + * This is invoked by <code>menuAboutToShow</code> on {@link #mMenuManager}. + * All previous dynamic menu actions have been removed and this method can now insert + * any new actions that depend on the current selection. + * @param menuManager + */ + private void populateDynamicContextMenu(MenuManager menuManager) { // Map action-id => action object (one per selected view that defined it) final TreeMap<String /*id*/, ArrayList<MenuAction>> actionsMap = new TreeMap<String, ArrayList<MenuAction>>(); // Map group-id => actions to place in this group. - TreeMap<String /*id*/, MenuAction.Group> groupMap = new TreeMap<String, MenuAction.Group>(); + TreeMap<String /*id*/, MenuAction.Group> groupsMap = new TreeMap<String, MenuAction.Group>(); + + int maxMenuSelection = collectDynamicMenuActions(actionsMap, groupsMap); + + // Now create the actual menu contributions + String endId = menuManager.getItems()[0].getId(); + + Separator sep = new Separator(); + sep.setId("-dyn-gle-sep"); //$NON-NLS-1$ + menuManager.insertBefore(endId, sep); + endId = sep.getId(); + + // First create the groups + Map<String, MenuManager> menuGroups = new HashMap<String, MenuManager>(); + for (MenuAction.Group group : groupsMap.values()) { + String id = group.getId(); + MenuManager submenu = new MenuManager(group.getTitle(), id); + menuGroups.put(id, submenu); + menuManager.insertBefore(endId, submenu); + endId = id; + } + + boolean needGroupSep = !menuGroups.isEmpty(); + + // Now fill in the actions + for (ArrayList<MenuAction> actions : actionsMap.values()) { + // Filter actions... if we have a multiple selection, only accept actions + // which are common to *all* the selection which actually returned at least + // one menu action. + if (actions == null || + actions.isEmpty() || + actions.size() != maxMenuSelection) { + continue; + } + if (!(actions.get(0) instanceof MenuAction.Action)) { + continue; + } + + final MenuAction.Action action = (MenuAction.Action) actions.get(0); + + IContributionItem contrib = null; + + if (action instanceof MenuAction.Toggle) { + contrib = createDynamicMenuToggle((MenuAction.Toggle) action, actionsMap); + + } else if (action instanceof MenuAction.Choices) { + Map<String, String> choiceMap = ((MenuAction.Choices) action).getChoices(); + if (choiceMap != null && !choiceMap.isEmpty()) { + contrib = createDynamicChoices( + (MenuAction.Choices)action, choiceMap, actionsMap); + } + } + + if (contrib != null) { + MenuManager groupMenu = menuGroups.get(action.getGroupId()); + if (groupMenu != null) { + groupMenu.add(contrib); + } else { + if (needGroupSep) { + needGroupSep = false; + + sep = new Separator(); + sep.setId("-dyn-gle-sep2"); //$NON-NLS-1$ + menuManager.insertBefore(endId, sep); + endId = sep.getId(); + } + menuManager.insertBefore(endId, contrib); + } + } + } + } + + /** + * Collects all the {@link MenuAction} contributed by the {@link IViewRule} of the + * current selection. + * This is the first step of {@link #populateDynamicContextMenu(MenuManager)}. + * + * @param outActionsMap Map that collects all the contributed actions. + * @param outGroupsMap Map that collects all the contributed groups (sub-menus). + * @return The max number of selected items that contributed the same action ID. + * This is used later to filter on multiple selections so that we can display only + * actions that are common to all selected items that contributed at least one action. + */ + private int collectDynamicMenuActions( + final TreeMap<String, ArrayList<MenuAction>> outActionsMap, + final TreeMap<String, MenuAction.Group> outGroupsMap) { int maxMenuSelection = 0; for (CanvasSelection selection : mSelections) { List<MenuAction> viewActions = null; @@ -1874,29 +1994,29 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { boolean foundAction = false; for (MenuAction action : viewActions) { if (action.getId() == null || action.getTitle() == null) { - // TODO invalid action. Log verbose error. + // TODO Log verbose error for invalid action. continue; } String id = action.getId(); if (action instanceof MenuAction.Group) { - if (!groupMap.containsKey(id)) { - groupMap.put(id, (MenuAction.Group) action); + if (!outGroupsMap.containsKey(id)) { + outGroupsMap.put(id, (MenuAction.Group) action); } continue; } - ArrayList<MenuAction> actions = actionsMap.get(id); + ArrayList<MenuAction> actions = outActionsMap.get(id); if (actions == null) { actions = new ArrayList<MenuAction>(); - actionsMap.put(id, actions); + outActionsMap.put(id, actions); } // All the actions for the same id should have be equal if (!actions.isEmpty()) { if (action.equals(actions.get(0))) { - // TODO invalid type mismatch. Log verbose error. + // TODO Log verbose error for invalid type mismatch. continue; } } @@ -1909,152 +2029,117 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { maxMenuSelection++; } } + return maxMenuSelection; + } - // Now create the actual menu contributions - String endId = mMenuManager.getItems()[0].getId(); + /** + * Invoked by {@link #populateDynamicContextMenu(MenuManager)} to create a new menu item + * for a {@link MenuAction.Toggle}. + * <p/> + * Toggles are represented by a checked menu item. + * + * @param action The toggle action to convert to a menu item. + * @param actionsMap Map of all contributed actions. + * @return a new {@link IContributionItem} to add to the context menu + */ + private IContributionItem createDynamicMenuToggle( + final MenuAction.Toggle action, + final TreeMap<String, ArrayList<MenuAction>> actionsMap) { + final boolean isChecked = action.isChecked(); + Action a = new Action(action.getTitle(), IAction.AS_CHECK_BOX) { + @Override + public void run() { + // Invoke the closures of all the actions using the same action-id + for (MenuAction a2 : actionsMap.get(action.getId())) { + if (a2 instanceof MenuAction.Action) { + Closure c = ((MenuAction.Action) a2).getAction(); + if (c != null) { + mRulesEngine.callClosure( + ((MenuAction.Action) a2).getAction(), + // Closure parameters are action, valueId, newValue + action, + null, // no valueId for a toggle + !isChecked); + } + } + } + } + }; + a.setId(action.getId()); + a.setChecked(isChecked); - Separator sep = new Separator(); - sep.setId("-dyn-gle-sep"); //$NON-NLS-1$ - mMenuManager.insertBefore(endId, sep); - endId = sep.getId(); + return new ActionContributionItem(a); + } - // First create the groups - Map<String, MenuManager> menuGroups = new HashMap<String, MenuManager>(); - for (MenuAction.Group group : groupMap.values()) { - String id = group.getId(); - MenuManager submenu = new MenuManager(group.getTitle(), id); - menuGroups.put(id, submenu); - mMenuManager.insertBefore(endId, submenu); - endId = id; + /** + * Invoked by {@link #populateDynamicContextMenu(MenuManager)} to create a new menu item + * for a {@link MenuAction.Choices}. + * <p/> + * Multiple-choices are represented by a sub-menu containing checked items. + * + * @param action The choices action to convert to a menu item. + * @param actionsMap Map of all contributed actions. + * @return a new {@link IContributionItem} to add to the context menu + */ + private IContributionItem createDynamicChoices( + final MenuAction.Choices action, + Map<String, String> choiceMap, + final TreeMap<String, ArrayList<MenuAction>> actionsMap) { + MenuManager submenu = new MenuManager(action.getTitle(), action.getId()); + + // Convert to a tree map as needed so that keys be naturally ordered. + if (!(choiceMap instanceof TreeMap<?, ?>)) { + choiceMap = new TreeMap<String, String>(choiceMap); } - boolean needGroupSep = !menuGroups.isEmpty(); + String current = action.getCurrent(); + Set<String> currents = null; + if (current.indexOf(MenuAction.Choices.CHOICE_SEP) >= 0) { + currents = new HashSet<String>( + Arrays.asList(current.split( + Pattern.quote(MenuAction.Choices.CHOICE_SEP)))); + current = null; + } - // Now fill in the actions - for (ArrayList<MenuAction> actions : actionsMap.values()) { - // Filter actions... if we have a multiple selection, only accept actions - // which are common to *all* the selection which actually returned at least - // one menu action. - if (actions == null || - actions.isEmpty() || - actions.size() != maxMenuSelection) { + for (Entry<String, String> entry : choiceMap.entrySet() ) { + final String key = entry.getKey(); + String title = entry.getValue(); + + if (key == null || title == null) { continue; } - if (!(actions.get(0) instanceof MenuAction.Action)) { + if (MenuAction.Choices.SEPARATOR.equals(title)) { + submenu.add(new Separator()); continue; } - final MenuAction.Action action = (MenuAction.Action) actions.get(0); - - IContributionItem contrib = null; - - if (action instanceof MenuAction.Toggle) { - - final boolean isChecked = ((MenuAction.Toggle) action).isChecked(); - Action a = new Action(action.getTitle(), IAction.AS_CHECK_BOX) { - @Override - public void run() { - // Invoke the closures of all the actions using the same action-id - for (MenuAction a2 : actionsMap.get(action.getId())) { - if (a2 instanceof MenuAction.Action) { - Closure c = ((MenuAction.Action) a2).getAction(); - if (c != null) { - mRulesEngine.callClosure( - ((MenuAction.Action) a2).getAction(), - // Closure parameters are action, valueId, newValue - action, - null, // no valueId for a toggle - !isChecked); - } - } - } - } - }; - a.setId(action.getId()); - a.setChecked(isChecked); - - contrib = new ActionContributionItem(a); - - } else if (action instanceof MenuAction.Choices) { - - Map<String, String> choiceMap = ((MenuAction.Choices) action).getChoices(); - if (choiceMap != null && !choiceMap.isEmpty()) { - MenuManager submenu = new MenuManager(action.getTitle(), action.getId()); - - // Convert to a tree map as needed so that keys be naturally ordered. - if (!(choiceMap instanceof TreeMap<?, ?>)) { - choiceMap = new TreeMap<String, String>(choiceMap); - } + final boolean isChecked = + (currents != null && currents.contains(key)) || + key.equals(current); - String current = ((MenuAction.Choices) action).getCurrent(); - Set<String> currents = null; - if (current.indexOf(MenuAction.Choices.CHOICE_SEP) >= 0) { - currents = new HashSet<String>( - Arrays.asList(current.split( - Pattern.quote(MenuAction.Choices.CHOICE_SEP)))); - current = null; - } - - for (Entry<String, String> entry : choiceMap.entrySet() ) { - final String key = entry.getKey(); - String title = entry.getValue(); - - if (key == null || title == null) { - continue; - } - - if (MenuAction.Choices.SEPARATOR.equals(title)) { - submenu.add(new Separator()); - continue; + Action a = new Action(title, IAction.AS_CHECK_BOX) { + @Override + public void run() { + // Invoke the closures of all the actions using the same action-id + for (MenuAction a2 : actionsMap.get(action.getId())) { + if (a2 instanceof MenuAction.Action) { + mRulesEngine.callClosure( + ((MenuAction.Action) a2).getAction(), + // Closure parameters are action, valueId, newValue + action, + key, + !isChecked); } - - final boolean isChecked = - (currents != null && currents.contains(key)) || - key.equals(current); - - Action a = new Action(title, IAction.AS_CHECK_BOX) { - @Override - public void run() { - // Invoke the closures of all the actions using the same action-id - for (MenuAction a2 : actionsMap.get(action.getId())) { - if (a2 instanceof MenuAction.Action) { - mRulesEngine.callClosure( - ((MenuAction.Action) a2).getAction(), - // Closure parameters are action, valueId, newValue - action, - key, - !isChecked); - } - } - } - }; - a.setId(String.format("%s_%s", action.getId(), key)); //$NON-NLS-1$ - a.setChecked(isChecked); - submenu.add(a); } - - contrib = submenu; } - } - - if (contrib != null) { - MenuManager groupMenu = menuGroups.get(action.getGroupId()); - if (groupMenu != null) { - groupMenu.add(contrib); - } else { - if (needGroupSep) { - needGroupSep = false; - - sep = new Separator(); - sep.setId("-dyn-gle-sep2"); //$NON-NLS-1$ - mMenuManager.insertBefore(endId, sep); - endId = sep.getId(); - } - mMenuManager.insertBefore(endId, contrib); - } - } + }; + a.setId(String.format("%s_%s", action.getId(), key)); //$NON-NLS-1$ + a.setChecked(isChecked); + submenu.add(a); } + + return submenu; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage2.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage2.java index ec945b6..01decfd 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage2.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage2.java @@ -417,12 +417,10 @@ public class OutlinePage2 extends ContentOutlinePage mMenuManager.add(new DelegateAction(prefix + ActionFactory.DELETE.getId())); mMenuManager.add(new DelegateAction(prefix + ActionFactory.SELECT_ALL.getId())); - getControl().setMenu(mMenuManager.createContextMenu(getControl())); - mMenuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { // Update all actions to match their LayoutCanvas counterparts - for (IContributionItem contrib : mMenuManager.getItems()) { + for (IContributionItem contrib : manager.getItems()) { if (contrib instanceof ActionContributionItem) { IAction action = ((ActionContributionItem) contrib).getAction(); if (action instanceof DelegateAction) { @@ -432,6 +430,10 @@ public class OutlinePage2 extends ContentOutlinePage } } }); + + mGraphicalEditorPart.getCanvasControl().setupDynamicMenuActions(mMenuManager); + + getControl().setMenu(mMenuManager.createContextMenu(getControl())); } /** @@ -445,6 +447,7 @@ public class OutlinePage2 extends ContentOutlinePage public DelegateAction(String canvasActionId) { super(canvasActionId); + setId(canvasActionId); mCanvasActionId = canvasActionId; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java index 9c5fd1e..402e24a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java @@ -22,7 +22,6 @@ import com.android.ide.eclipse.adt.internal.sdk.LoadStatus; import com.android.ide.eclipse.adt.internal.sdk.ProjectState; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkConstants; import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import org.eclipse.core.resources.IMarker; @@ -179,16 +178,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // first make sure the target has loaded its data Sdk.getCurrent().checkAndLoadTargetData(target, null /*project*/); - // now do a quick check to make sure the project's target is compatible - // with library (if applicable). - if (state.hasLibraries() && - target.getProperty( - SdkConstants.PROP_SDK_SUPPORT_LIBRARY, false) == false) { - AdtPlugin.printErrorToConsole(iProject, String.format( - "Target '%1$s' does not support building project with libraries.", - target.getFullName())); - } - String targetName = target.getClasspathName(); return new AndroidClasspathContainer( diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java index aa0dc25..3a37634 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java @@ -518,6 +518,10 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration * @return true if the configuration matches. */ public boolean isMatchFor(FolderConfiguration referenceConfig) { + if (referenceConfig == null) { + return false; + } + for (int i = 0 ; i < INDEX_COUNT ; i++) { ResourceQualifier testQualifier = mQualifiers[i]; ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i]; |
