ADT Manifest Editor: Fix for uses-sdk dup that won't go away.

It was a display issue only, meaning the actual XML node was
being deleted but the Ui model was not updated properly. The
actual cause is that this is a mandatory node, and mandatory
nodes must stay as "fake nodes" in the Ui model. However
only one such node needs to stay in the hierarchy.

SDK BUG 2147112

Change-Id: I68e3d343bb95169865ad8dcad77004cbd51fafc3
This commit is contained in:
Raphael
2009-10-01 12:32:56 -07:00
parent 657bdd7d28
commit 2ace885b29
2 changed files with 87 additions and 60 deletions

View File

@@ -90,7 +90,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** Container editor */ /** Container editor */
AndroidEditor mEditor; AndroidEditor mEditor;
/** The root {@link UiElementNode} which contains all the elements that are to be /** The root {@link UiElementNode} which contains all the elements that are to be
* manipulated by this tree view. In general this is the manifest UI node. */ * manipulated by this tree view. In general this is the manifest UI node. */
private UiElementNode mUiRootNode; private UiElementNode mUiRootNode;
/** The descriptor of the elements to be displayed as root in this tree view. All elements /** The descriptor of the elements to be displayed as root in this tree view. All elements
@@ -104,7 +104,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
private ManifestSectionPart mMasterPart; private ManifestSectionPart mMasterPart;
/** The tree viewer in the master-detail part */ /** The tree viewer in the master-detail part */
private TreeViewer mTreeViewer; private TreeViewer mTreeViewer;
/** The "add" button for the tree view */ /** The "add" button for the tree view */
private Button mAddButton; private Button mAddButton;
/** The "remove" button for the tree view */ /** The "remove" button for the tree view */
private Button mRemoveButton; private Button mRemoveButton;
@@ -137,7 +137,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** /**
* Creates a new {@link MasterDetailsBlock} that will display all UI nodes matching the * Creates a new {@link MasterDetailsBlock} that will display all UI nodes matching the
* given filter in the given root node. * given filter in the given root node.
* *
* @param editor The parent manifest editor. * @param editor The parent manifest editor.
* @param uiRootNode The root {@link UiElementNode} which contains all the elements that are * @param uiRootNode The root {@link UiElementNode} which contains all the elements that are
* to be manipulated by this tree view. In general this is the manifest UI node or the * to be manipulated by this tree view. In general this is the manifest UI node or the
@@ -165,17 +165,17 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
mTitle = title; mTitle = title;
mDescription = description; mDescription = description;
} }
/** @returns The container editor */ /** @returns The container editor */
AndroidEditor getEditor() { AndroidEditor getEditor() {
return mEditor; return mEditor;
} }
/** @returns The reference to the clipboard for copy-paste */ /** @returns The reference to the clipboard for copy-paste */
Clipboard getClipboard() { Clipboard getClipboard() {
return mClipboard; return mClipboard;
} }
/** @returns The master-detail part, composed of a main tree and an auxiliary detail part */ /** @returns The master-detail part, composed of a main tree and an auxiliary detail part */
ManifestSectionPart getMasterPart() { ManifestSectionPart getMasterPart() {
return mMasterPart; return mMasterPart;
@@ -186,7 +186,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
* <p/> * <p/>
* This is used by the content provider attached to {@link #mTreeViewer} since * This is used by the content provider attached to {@link #mTreeViewer} since
* the uiRootNode changes after each call to * the uiRootNode changes after each call to
* {@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}. * {@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}.
*/ */
public UiElementNode getRootNode() { public UiElementNode getRootNode() {
return mUiRootNode; return mUiRootNode;
@@ -215,21 +215,21 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
private void createSectionActions(Section section, FormToolkit toolkit) { private void createSectionActions(Section section, FormToolkit toolkit) {
ToolBarManager manager = new ToolBarManager(SWT.FLAT); ToolBarManager manager = new ToolBarManager(SWT.FLAT);
manager.removeAll(); manager.removeAll();
ToolBar toolbar = manager.createControl(section); ToolBar toolbar = manager.createControl(section);
section.setTextClient(toolbar); section.setTextClient(toolbar);
ElementDescriptor[] descs = mDescriptorFilters; ElementDescriptor[] descs = mDescriptorFilters;
if (descs == null && mUiRootNode != null) { if (descs == null && mUiRootNode != null) {
descs = mUiRootNode.getDescriptor().getChildren(); descs = mUiRootNode.getDescriptor().getChildren();
} }
if (descs != null && descs.length > 1) { if (descs != null && descs.length > 1) {
for (ElementDescriptor desc : descs) { for (ElementDescriptor desc : descs) {
manager.add(new DescriptorFilterAction(desc)); manager.add(new DescriptorFilterAction(desc));
} }
} }
manager.add(new TreeSortAction()); manager.add(new TreeSortAction());
manager.update(true /*force*/); manager.update(true /*force*/);
@@ -264,7 +264,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
adjustTreeButtons(event.getSelection()); adjustTreeButtons(event.getSelection());
} }
}); });
// Create three listeners: // Create three listeners:
// - One to refresh the tree viewer when the parent's node has been updated // - One to refresh the tree viewer when the parent's node has been updated
// - One to refresh the tree viewer when the framework resources have changed // - One to refresh the tree viewer when the framework resources have changed
@@ -274,7 +274,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
mTreeViewer.refresh(); mTreeViewer.refresh();
} }
}; };
mUiEnableListener = new IUiUpdateListener() { mUiEnableListener = new IUiUpdateListener() {
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
// The UiElementNode for the application XML node always exists, even // The UiElementNode for the application XML node always exists, even
@@ -345,7 +345,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
} }
} }
}); });
// Get a new clipboard reference. It is disposed when the tree is disposed. // Get a new clipboard reference. It is disposed when the tree is disposed.
mClipboard = new Clipboard(tree.getDisplay()); mClipboard = new Clipboard(tree.getDisplay());
@@ -357,7 +357,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
* <p/> * <p/>
* This removes the listeners attached to the old root node and reattaches them to the * This removes the listeners attached to the old root node and reattaches them to the
* new one. * new one.
* *
* @param uiRootNode The root {@link UiElementNode} which contains all the elements that are * @param uiRootNode The root {@link UiElementNode} which contains all the elements that are
* to be manipulated by this tree view. In general this is the manifest UI node or the * to be manipulated by this tree view. In general this is the manifest UI node or the
* application UI node. This cannot be null. * application UI node. This cannot be null.
@@ -375,7 +375,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
node.removeUpdateListener(mUiRefreshListener); node.removeUpdateListener(mUiRefreshListener);
mUiRootNode.removeUpdateListener(mUiEnableListener); mUiRootNode.removeUpdateListener(mUiEnableListener);
} }
mUiRootNode = uiRootNode; mUiRootNode = uiRootNode;
mDescriptorFilters = descriptorFilters; mDescriptorFilters = descriptorFilters;
@@ -385,13 +385,13 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
// If the node has a parent, listen on the parent instead. // If the node has a parent, listen on the parent instead.
node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode; node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode;
node.addUpdateListener(mUiRefreshListener); node.addUpdateListener(mUiRefreshListener);
// Use the root node to listen to its presence. // Use the root node to listen to its presence.
mUiRootNode.addUpdateListener(mUiEnableListener); mUiRootNode.addUpdateListener(mUiEnableListener);
// Initialize the enabled/disabled state // Initialize the enabled/disabled state
mUiEnableListener.uiElementNodeUpdated(mUiRootNode, null /* state, not used */); mUiEnableListener.uiElementNodeUpdated(mUiRootNode, null /* state, not used */);
if (forceRefresh) { if (forceRefresh) {
mTreeViewer.refresh(); mTreeViewer.refresh();
} }
@@ -403,9 +403,9 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
* Creates the buttons next to the tree. * Creates the buttons next to the tree.
*/ */
private void createButtons(FormToolkit toolkit, Composite grid) { private void createButtons(FormToolkit toolkit, Composite grid) {
mUiTreeActions = new UiTreeActions(); mUiTreeActions = new UiTreeActions();
Composite button_grid = SectionHelper.createGridLayout(grid, toolkit, 1); Composite button_grid = SectionHelper.createGridLayout(grid, toolkit, 1);
button_grid.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); button_grid.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
mAddButton = toolkit.createButton(button_grid, "Add...", SWT.PUSH); mAddButton = toolkit.createButton(button_grid, "Add...", SWT.PUSH);
@@ -420,11 +420,11 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
doTreeAdd(); doTreeAdd();
} }
}); });
mRemoveButton = toolkit.createButton(button_grid, "Remove...", SWT.PUSH); mRemoveButton = toolkit.createButton(button_grid, "Remove...", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Removes an existing selected element."); SectionHelper.addControlTooltip(mRemoveButton, "Removes an existing selected element.");
mRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mRemoveButton.addSelectionListener(new SelectionAdapter() { mRemoveButton.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
@@ -432,11 +432,11 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
doTreeRemove(); doTreeRemove();
} }
}); });
mUpButton = toolkit.createButton(button_grid, "Up", SWT.PUSH); mUpButton = toolkit.createButton(button_grid, "Up", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element up."); SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element up.");
mUpButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mUpButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mUpButton.addSelectionListener(new SelectionAdapter() { mUpButton.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
@@ -448,7 +448,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
mDownButton = toolkit.createButton(button_grid, "Down", SWT.PUSH); mDownButton = toolkit.createButton(button_grid, "Down", SWT.PUSH);
SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element down."); SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element down.");
mDownButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mDownButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mDownButton.addSelectionListener(new SelectionAdapter() { mDownButton.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent e) { public void widgetSelected(SelectionEvent e) {
@@ -478,7 +478,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
return; return;
} }
doCreateMenuAction(manager, null /* ui_node */); doCreateMenuAction(manager, null /* ui_node */);
} }
}); });
Menu contextMenu = menuManager.createContextMenu(tree); Menu contextMenu = menuManager.createContextMenu(tree);
tree.setMenu(contextMenu); tree.setMenu(contextMenu);
@@ -487,7 +487,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** /**
* Adds the menu actions to the context menu when the given UI node is selected in * Adds the menu actions to the context menu when the given UI node is selected in
* the tree view. * the tree view.
* *
* @param manager The context menu manager * @param manager The context menu manager
* @param selected The UI nodes selected in the tree. Can be null, in which case the root * @param selected The UI nodes selected in the tree. Can be null, in which case the root
* is to be modified. * is to be modified.
@@ -526,7 +526,6 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
// Append "add" and "remove" actions. They do the same thing as the add/remove // Append "add" and "remove" actions. They do the same thing as the add/remove
// buttons on the side. // buttons on the side.
Action action;
IconFactory factory = IconFactory.getInstance(); IconFactory factory = IconFactory.getInstance();
// "Add" makes sense only if there's 0 or 1 item selected since the // "Add" makes sense only if there's 0 or 1 item selected since the
@@ -552,7 +551,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
}); });
} }
manager.add(new Separator()); manager.add(new Separator());
manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-1$ manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-1$
@Override @Override
public void run() { public void run() {
@@ -570,7 +569,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
} }
} }
/** /**
* This is called by the tree when a selection is made. * This is called by the tree when a selection is made.
* It enables/disables the buttons associated with the tree depending on the current * It enables/disables the buttons associated with the tree depending on the current
@@ -619,16 +618,16 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** /**
* Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's * Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's
* something else in there). * something else in there).
* *
* @return A new list of {@link UiElementNode} with at least one item or null. * @return A new list of {@link UiElementNode} with at least one item or null.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private ArrayList<UiElementNode> filterSelection(ITreeSelection selection) { private ArrayList<UiElementNode> filterSelection(ITreeSelection selection) {
ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>();
for (Iterator it = selection.iterator(); it.hasNext(); ) { for (Iterator it = selection.iterator(); it.hasNext(); ) {
Object selectedObj = it.next(); Object selectedObj = it.next();
if (selectedObj instanceof UiElementNode) { if (selectedObj instanceof UiElementNode) {
selected.add((UiElementNode) selectedObj); selected.add((UiElementNode) selectedObj);
} }
@@ -639,7 +638,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** /**
* Called when the "Add..." button next to the tree view is selected. * Called when the "Add..." button next to the tree view is selected.
* *
* Displays a selection dialog that lets the user select which kind of node * Displays a selection dialog that lets the user select which kind of node
* to create, depending on the current selection. * to create, depending on the current selection.
*/ */
@@ -663,7 +662,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
/** /**
* Called when the "Remove" button is selected. * Called when the "Remove" button is selected.
* *
* If the tree has a selection, remove it. * If the tree has a selection, remove it.
* This simply deletes the XML node attached to the UI node: when the XML model fires the * This simply deletes the XML node attached to the UI node: when the XML model fires the
* update event, the tree will get refreshed. * update event, the tree will get refreshed.
@@ -689,10 +688,10 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
mUiTreeActions.doUp(selected); mUiTreeActions.doUp(selected);
} }
} }
/** /**
* Called when the "Down" button is selected. * Called when the "Down" button is selected.
* *
* If the tree has a selection, move it down, either in the same child list or as the * If the tree has a selection, move it down, either in the same child list or as the
* first child of the next parent. * first child of the next parent.
*/ */
@@ -729,16 +728,16 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
// Keep a reference on the details part (the super class doesn't provide a getter // Keep a reference on the details part (the super class doesn't provide a getter
// for it.) // for it.)
mDetailsPart = detailsPart; mDetailsPart = detailsPart;
// The page selection mechanism does not use pages registered by association with // The page selection mechanism does not use pages registered by association with
// a node class. Instead it uses a custom details page provider that provides a // a node class. Instead it uses a custom details page provider that provides a
// new UiElementDetail instance for each node instance. A limit of 5 pages is // new UiElementDetail instance for each node instance. A limit of 5 pages is
// then set (the value is arbitrary but should be reasonable) for the internal // then set (the value is arbitrary but should be reasonable) for the internal
// page book. // page book.
detailsPart.setPageLimit(5); detailsPart.setPageLimit(5);
final UiTreeBlock tree = this; final UiTreeBlock tree = this;
detailsPart.setPageProvider(new IDetailsPageProvider() { detailsPart.setPageProvider(new IDetailsPageProvider() {
public IDetailsPage getPage(Object key) { public IDetailsPage getPage(Object key) {
if (key instanceof UiElementNode) { if (key instanceof UiElementNode) {
@@ -757,13 +756,13 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
* An alphabetic sort action for the tree viewer. * An alphabetic sort action for the tree viewer.
*/ */
private class TreeSortAction extends Action { private class TreeSortAction extends Action {
private ViewerComparator mComparator; private ViewerComparator mComparator;
public TreeSortAction() { public TreeSortAction() {
super("Sorts elements alphabetically.", AS_CHECK_BOX); super("Sorts elements alphabetically.", AS_CHECK_BOX);
setImageDescriptor(IconFactory.getInstance().getImageDescriptor("az_sort")); //$NON-NLS-1$ setImageDescriptor(IconFactory.getInstance().getImageDescriptor("az_sort")); //$NON-NLS-1$
if (mTreeViewer != null) { if (mTreeViewer != null) {
boolean is_sorted = mTreeViewer.getComparator() != null; boolean is_sorted = mTreeViewer.getComparator() != null;
setChecked(is_sorted); setChecked(is_sorted);
@@ -794,7 +793,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
} }
mTreeViewer.setComparator(mComparator); mTreeViewer.setComparator(mComparator);
} }
notifyResult(true /*success*/); notifyResult(true /*success*/);
} }
} }
@@ -813,11 +812,11 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
private final ElementDescriptor mDescriptor; private final ElementDescriptor mDescriptor;
private ViewerFilter mFilter; private ViewerFilter mFilter;
public DescriptorFilterAction(ElementDescriptor descriptor) { public DescriptorFilterAction(ElementDescriptor descriptor) {
super(String.format("Displays only %1$s elements.", descriptor.getUiName()), super(String.format("Displays only %1$s elements.", descriptor.getUiName()),
AS_CHECK_BOX); AS_CHECK_BOX);
mDescriptor = descriptor; mDescriptor = descriptor;
setImageDescriptor(descriptor.getImageDescriptor()); setImageDescriptor(descriptor.getImageDescriptor());
} }
@@ -830,7 +829,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
@Override @Override
public void run() { public void run() {
super.run(); super.run();
if (isChecked()) { if (isChecked()) {
if (mFilter == null) { if (mFilter == null) {
// create filter when required // create filter when required
@@ -868,7 +867,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
public DescriptorFilter(DescriptorFilterAction action) { public DescriptorFilter(DescriptorFilterAction action) {
mAction = action; mAction = action;
} }
public DescriptorFilterAction getAction() { public DescriptorFilterAction getAction() {
return mAction; return mAction;
} }
@@ -890,5 +889,5 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml
} }
} }
} }
} }

View File

@@ -926,7 +926,8 @@ public class UiElementNode implements IPropertySource {
* Internal helper to remove an UI child node given by its index in the * Internal helper to remove an UI child node given by its index in the
* internal child list. * internal child list.
* *
* Also invokes the update listener on the node to be deleted. * Also invokes the update listener on the node to be deleted *after* the node has
* been removed.
* *
* @param ui_index The index of the UI child to remove, range 0 .. mUiChildren.size()-1 * @param ui_index The index of the UI child to remove, range 0 .. mUiChildren.size()-1
* @return True if the structure has changed * @return True if the structure has changed
@@ -935,19 +936,46 @@ public class UiElementNode implements IPropertySource {
*/ */
private boolean removeUiChildAtIndex(int ui_index) { private boolean removeUiChildAtIndex(int ui_index) {
UiElementNode ui_node = mUiChildren.get(ui_index); UiElementNode ui_node = mUiChildren.get(ui_index);
invokeUiUpdateListeners(UiUpdateState.DELETED); ElementDescriptor desc = ui_node.getDescriptor();
if (ui_node.getDescriptor().isMandatory()) {
// We can't remove a mandatory node, we just clear its content.
// A mandatory node with no XML means it doesn't really exist, so it can't be try {
// deleted. if (ui_node.getDescriptor().isMandatory()) {
boolean xml_exists = (ui_node.getXmlNode() != null); // This is a mandatory node. Such a node must exist in the UiNode hierarchy
// even if there's no XML counterpart. However we only need to keep one.
// Check if the parent (e.g. this node) has another similar ui child node.
boolean keepNode = true;
for (UiElementNode child : mUiChildren) {
if (child != ui_node && child.getDescriptor() == desc) {
// We found another child with the same descriptor that is not
// the node we want to remove. This means we have one mandatory
// node so we can safely remove ui_node.
keepNode = false;
break;
}
}
if (keepNode) {
// We can't remove a mandatory node as we need to keep at least one
// mandatory node in the parent. Instead we just clear its content
// (including its XML Node reference).
// A mandatory node with no XML means it doesn't really exist, so it can't be
// deleted. So the structure will change only if the ui node is actually
// associated to an XML node.
boolean xml_exists = (ui_node.getXmlNode() != null);
ui_node.clearContent();
return xml_exists;
}
}
ui_node.clearContent();
return xml_exists;
} else {
mUiChildren.remove(ui_index); mUiChildren.remove(ui_index);
return true; return true;
} finally {
// Tell listeners that a node has been removed.
// The model has already been modified.
invokeUiUpdateListeners(UiUpdateState.DELETED);
} }
} }
@@ -1077,7 +1105,7 @@ public class UiElementNode implements IPropertySource {
} }
/** /**
* Invoke all registered {@link IUiUpdateListener} listening on this UI updates for this node. * Invoke all registered {@link IUiUpdateListener} listening on this UI update for this node.
*/ */
protected void invokeUiUpdateListeners(UiUpdateState state) { protected void invokeUiUpdateListeners(UiUpdateState state) {
if (mUiUpdateListeners != null) { if (mUiUpdateListeners != null) {