AI 147264: am: CL 147262 ADT #1761055: Pre-select node types when adding node in manifest & resource based on context
Original author: raphael Merged from: //branches/cupcake/... Automated import of CL 147264
This commit is contained in:
committed by
The Android Open Source Project
parent
136d537b79
commit
faffd247ee
@@ -17,10 +17,12 @@
|
|||||||
package com.android.ide.eclipse.editors.ui.tree;
|
package com.android.ide.eclipse.editors.ui.tree;
|
||||||
|
|
||||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||||
|
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||||
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
|
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
|
||||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.runtime.IStatus;
|
import org.eclipse.core.runtime.IStatus;
|
||||||
import org.eclipse.core.runtime.Status;
|
import org.eclipse.core.runtime.Status;
|
||||||
import org.eclipse.jface.viewers.ILabelProvider;
|
import org.eclipse.jface.viewers.ILabelProvider;
|
||||||
@@ -31,10 +33,16 @@ import org.eclipse.swt.widgets.Button;
|
|||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.eclipse.swt.widgets.Control;
|
import org.eclipse.swt.widgets.Control;
|
||||||
import org.eclipse.swt.widgets.Shell;
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.ui.IEditorInput;
|
||||||
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
|
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
|
||||||
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
|
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
|
||||||
|
import org.eclipse.ui.part.FileEditorInput;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A selection dialog to select the type of the new element node to
|
* A selection dialog to select the type of the new element node to
|
||||||
@@ -50,15 +58,25 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
|
|||||||
private UiElementNode mChosenRootNode;
|
private UiElementNode mChosenRootNode;
|
||||||
private UiElementNode mLocalRootNode;
|
private UiElementNode mLocalRootNode;
|
||||||
/** 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
|
||||||
* of the same type in the root will be displayed. */
|
* of the same type in the root will be displayed. Can be null. */
|
||||||
private ElementDescriptor[] mDescriptorFilters;
|
private ElementDescriptor[] mDescriptorFilters;
|
||||||
|
/** The key for the {@link #setLastUsedXmlName(Object[])}. It corresponds to the full
|
||||||
|
* workspace path of the currently edited file, if this can be computed. This is computed
|
||||||
|
* by {@link #getLastUsedXmlName(UiElementNode)}, called from the constructor. */
|
||||||
|
private String mLastUsedKey;
|
||||||
|
/** A static map of known XML Names used for a given file. The map has full workspace
|
||||||
|
* paths as key and XML names as values. */
|
||||||
|
private static final Map<String, String> sLastUsedXmlName = new HashMap<String, String>();
|
||||||
|
/** The potential XML Name to initially select in the selection dialog. This is computed
|
||||||
|
* in the constructor and set by {@link #setInitialSelection(UiElementNode)}. */
|
||||||
|
private String mInitialXmlName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the new item selection dialog.
|
* Creates the new item selection dialog.
|
||||||
*
|
*
|
||||||
* @param shell The parent shell for the list.
|
* @param shell The parent shell for the list.
|
||||||
* @param labelProvider ILabelProvider for the list.
|
* @param labelProvider ILabelProvider for the list.
|
||||||
* @param descriptorFilters The element allows at the root of the tree
|
* @param descriptorFilters The element allows at the root of the tree. Can be null.
|
||||||
* @param ui_node The selected node, or null if none is selected.
|
* @param ui_node The selected node, or null if none is selected.
|
||||||
* @param root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node.
|
* @param root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node.
|
||||||
*/
|
*/
|
||||||
@@ -109,6 +127,114 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Determine the initial selection using a couple heuristics.
|
||||||
|
|
||||||
|
// First check if we can get the last used node type for this file.
|
||||||
|
// The heuristic is that generally one keeps adding the same kind of items to the
|
||||||
|
// same file, so reusing the last used item type makes most sense.
|
||||||
|
String xmlName = getLastUsedXmlName(root_node);
|
||||||
|
if (xmlName == null) {
|
||||||
|
// Another heuristic is to find the most used item and default to that.
|
||||||
|
xmlName = getMostUsedXmlName(root_node);
|
||||||
|
}
|
||||||
|
if (xmlName == null) {
|
||||||
|
// Finally the last heuristic is to see if there's an item with a name
|
||||||
|
// similar to the edited file name.
|
||||||
|
xmlName = getLeafFileName(root_node);
|
||||||
|
}
|
||||||
|
// Set the potential name. Selecting the right item is done later by setInitialSelection().
|
||||||
|
mInitialXmlName = xmlName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a potential XML name based on the file name.
|
||||||
|
* The item name is marked with an asterisk to identify it as a partial match.
|
||||||
|
*/
|
||||||
|
private String getLeafFileName(UiElementNode ui_node) {
|
||||||
|
if (ui_node != null) {
|
||||||
|
AndroidEditor editor = ui_node.getEditor();
|
||||||
|
if (editor != null) {
|
||||||
|
IEditorInput editorInput = editor.getEditorInput();
|
||||||
|
if (editorInput instanceof FileEditorInput) {
|
||||||
|
IFile f = ((FileEditorInput) editorInput).getFile();
|
||||||
|
if (f != null) {
|
||||||
|
String leafName = f.getFullPath().removeFileExtension().lastSegment();
|
||||||
|
return "*" + leafName; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a potential non-null root node, this method looks for the currently edited
|
||||||
|
* file path and uses it as a key to retrieve the last used item for this file by this
|
||||||
|
* selection dialog. Returns null if nothing can be found, otherwise returns the string
|
||||||
|
* name of the item.
|
||||||
|
*/
|
||||||
|
private String getLastUsedXmlName(UiElementNode ui_node) {
|
||||||
|
if (ui_node != null) {
|
||||||
|
AndroidEditor editor = ui_node.getEditor();
|
||||||
|
if (editor != null) {
|
||||||
|
IEditorInput editorInput = editor.getEditorInput();
|
||||||
|
if (editorInput instanceof FileEditorInput) {
|
||||||
|
IFile f = ((FileEditorInput) editorInput).getFile();
|
||||||
|
if (f != null) {
|
||||||
|
mLastUsedKey = f.getFullPath().toPortableString();
|
||||||
|
|
||||||
|
return sLastUsedXmlName.get(mLastUsedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the last used item for this selection dialog for this file.
|
||||||
|
* @param objects The currently selected items. Only the first one is used if it is an
|
||||||
|
* {@link ElementDescriptor}.
|
||||||
|
*/
|
||||||
|
private void setLastUsedXmlName(Object[] objects) {
|
||||||
|
if (mLastUsedKey != null &&
|
||||||
|
objects != null &&
|
||||||
|
objects.length > 0 &&
|
||||||
|
objects[0] instanceof ElementDescriptor) {
|
||||||
|
ElementDescriptor desc = (ElementDescriptor) objects[0];
|
||||||
|
sLastUsedXmlName.put(mLastUsedKey, desc.getXmlName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most used sub-element name, if any, or null.
|
||||||
|
*/
|
||||||
|
private String getMostUsedXmlName(UiElementNode ui_node) {
|
||||||
|
if (ui_node != null) {
|
||||||
|
TreeMap<String, Integer> counts = new TreeMap<String, Integer>();
|
||||||
|
int max = -1;
|
||||||
|
|
||||||
|
for (UiElementNode child : ui_node.getUiChildren()) {
|
||||||
|
String name = child.getDescriptor().getXmlName();
|
||||||
|
Integer i = counts.get(name);
|
||||||
|
int count = i == null ? 1 : i.intValue() + 1;
|
||||||
|
counts.put(name, count);
|
||||||
|
max = Math.max(max, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max > 0) {
|
||||||
|
// Find first key with this max and return it
|
||||||
|
for (Entry<String, Integer> entry : counts.entrySet()) {
|
||||||
|
if (entry.getValue().intValue() == max) {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,6 +252,7 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
|
|||||||
@Override
|
@Override
|
||||||
protected void computeResult() {
|
protected void computeResult() {
|
||||||
setResult(Arrays.asList(getSelectedElements()));
|
setResult(Arrays.asList(getSelectedElements()));
|
||||||
|
setLastUsedXmlName(getSelectedElements());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,10 +279,46 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
|
|||||||
// Initialize the list state.
|
// Initialize the list state.
|
||||||
// This must be done after the filtered list as been created.
|
// This must be done after the filtered list as been created.
|
||||||
chooseNode(mChosenRootNode);
|
chooseNode(mChosenRootNode);
|
||||||
setSelection(getInitialElementSelections().toArray());
|
|
||||||
|
// Set the initial selection
|
||||||
|
setInitialSelection(mChosenRootNode);
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to set the initial selection based on the {@link #mInitialXmlName} computed
|
||||||
|
* in the constructor. The selection is only set if there's an element descriptor
|
||||||
|
* that matches the same exact XML name. When {@link #mInitialXmlName} starts with an
|
||||||
|
* asterisk, it means to do a partial case-insensitive match on the start of the
|
||||||
|
* strings.
|
||||||
|
*/
|
||||||
|
private void setInitialSelection(UiElementNode rootNode) {
|
||||||
|
ElementDescriptor initialElement = null;
|
||||||
|
|
||||||
|
if (mInitialXmlName != null && mInitialXmlName.length() > 0) {
|
||||||
|
String name = mInitialXmlName;
|
||||||
|
boolean partial = name.startsWith("*"); //$NON-NLS-1$
|
||||||
|
if (partial) {
|
||||||
|
name = name.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ElementDescriptor desc : getAllowedDescriptors(rootNode)) {
|
||||||
|
if (!partial && desc.getXmlName().equals(name)) {
|
||||||
|
initialElement = desc;
|
||||||
|
break;
|
||||||
|
} else if (partial) {
|
||||||
|
String name2 = desc.getXmlLocalName().toLowerCase();
|
||||||
|
if (name.startsWith(name2) || name2.startsWith(name)) {
|
||||||
|
initialElement = desc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelection(initialElement == null ? null : new ElementDescriptor[] { initialElement });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the message text widget and sets layout data.
|
* Creates the message text widget and sets layout data.
|
||||||
* @param content the parent composite of the message area.
|
* @param content the parent composite of the message area.
|
||||||
@@ -217,13 +380,23 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog {
|
|||||||
*/
|
*/
|
||||||
private void chooseNode(UiElementNode ui_node) {
|
private void chooseNode(UiElementNode ui_node) {
|
||||||
mChosenRootNode = ui_node;
|
mChosenRootNode = ui_node;
|
||||||
|
setListElements(getAllowedDescriptors(ui_node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of {@link ElementDescriptor}s that can be added to the given
|
||||||
|
* UI node.
|
||||||
|
*
|
||||||
|
* @param ui_node The UI node to which element should be added. Cannot be null.
|
||||||
|
* @return A non-null array of {@link ElementDescriptor}. The array might be empty.
|
||||||
|
*/
|
||||||
|
private ElementDescriptor[] getAllowedDescriptors(UiElementNode ui_node) {
|
||||||
if (ui_node == mLocalRootNode &&
|
if (ui_node == mLocalRootNode &&
|
||||||
mDescriptorFilters != null &&
|
mDescriptorFilters != null &&
|
||||||
mDescriptorFilters.length != 0) {
|
mDescriptorFilters.length != 0) {
|
||||||
setListElements(mDescriptorFilters);
|
return mDescriptorFilters;
|
||||||
} else {
|
} else {
|
||||||
setListElements(ui_node.getDescriptor().getChildren());
|
return ui_node.getDescriptor().getChildren();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user