diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree')
9 files changed, 0 insertions, 3057 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java deleted file mode 100644 index 3fe98bb23..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/CopyCutAction.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.apache.xml.serialize.Method; -import org.apache.xml.serialize.OutputFormat; -import org.apache.xml.serialize.XMLSerializer; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; -import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.xml.core.internal.document.NodeContainer; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.List; - - -/** - * Provides Cut and Copy actions for the tree nodes. - */ -@SuppressWarnings({"restriction", "deprecation"}) -public class CopyCutAction extends Action { - private List<UiElementNode> mUiNodes; - private boolean mPerformCut; - private final AndroidXmlEditor mEditor; - private final Clipboard mClipboard; - private final ICommitXml mXmlCommit; - - /** - * Creates a new Copy or Cut action. - * - * @param selected The UI node to cut or copy. It *must* have a non-null XML node. - * @param performCut True if the operation is cut, false if it is copy. - */ - public CopyCutAction(AndroidXmlEditor editor, Clipboard clipboard, ICommitXml xmlCommit, - UiElementNode selected, boolean performCut) { - this(editor, clipboard, xmlCommit, toList(selected), performCut); - } - - /** - * Creates a new Copy or Cut action. - * - * @param selected The UI nodes to cut or copy. They *must* have a non-null XML node. - * The list becomes owned by the {@link CopyCutAction}. - * @param performCut True if the operation is cut, false if it is copy. - */ - public CopyCutAction(AndroidXmlEditor editor, Clipboard clipboard, ICommitXml xmlCommit, - List<UiElementNode> selected, boolean performCut) { - super(performCut ? "Cut" : "Copy"); - mEditor = editor; - mClipboard = clipboard; - mXmlCommit = xmlCommit; - - ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); - if (performCut) { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_HOVER)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); - } else { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_HOVER)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); - } - - mUiNodes = selected; - mPerformCut = performCut; - } - - /** - * Performs the cut or copy action. - * First an XML serializer is used to turn the existing XML node into a valid - * XML fragment, which is added as text to the clipboard. - */ - @Override - public void run() { - super.run(); - if (mUiNodes == null || mUiNodes.size() < 1) { - return; - } - - // Commit the current pages first, to make sure the XML is in sync. - // Committing may change the XML structure. - if (mXmlCommit != null) { - mXmlCommit.commitPendingXmlChanges(); - } - - StringBuilder allText = new StringBuilder(); - ArrayList<UiElementNode> nodesToCut = mPerformCut ? new ArrayList<UiElementNode>() : null; - - for (UiElementNode uiNode : mUiNodes) { - try { - Node xml_node = uiNode.getXmlNode(); - if (xml_node == null) { - return; - } - - String data = getXmlTextFromEditor(xml_node); - - // In the unlikely event that IStructuredDocument failed to extract the text - // directly from the editor, try to fall back on a direct XML serialization - // of the XML node. This uses the generic Node interface with no SSE tricks. - if (data == null) { - data = getXmlTextFromSerialization(xml_node); - } - - if (data != null) { - allText.append(data); - if (mPerformCut) { - // only remove notes to cut if we actually got some XML text from them - nodesToCut.add(uiNode); - } - } - - } catch (Exception e) { - AdtPlugin.log(e, "CopyCutAction failed for UI node %1$s", //$NON-NLS-1$ - uiNode.getBreadcrumbTrailDescription(true)); - } - } // for uiNode - - if (allText != null && allText.length() > 0) { - mClipboard.setContents( - new Object[] { allText.toString() }, - new Transfer[] { TextTransfer.getInstance() }); - if (mPerformCut) { - for (UiElementNode uiNode : nodesToCut) { - uiNode.deleteXmlNode(); - } - } - } - } - - /** Get the data directly from the editor. */ - private String getXmlTextFromEditor(Node xml_node) { - String data = null; - IStructuredModel model = mEditor.getModelForRead(); - try { - IStructuredDocument sse_doc = mEditor.getStructuredDocument(); - if (xml_node instanceof NodeContainer) { - // The easy way to get the source of an SSE XML node. - data = ((NodeContainer) xml_node).getSource(); - } else if (xml_node instanceof IndexedRegion && sse_doc != null) { - // Try harder. - IndexedRegion region = (IndexedRegion) xml_node; - int start = region.getStartOffset(); - int end = region.getEndOffset(); - - if (end > start) { - data = sse_doc.get(start, end - start); - } - } - } catch (BadLocationException e) { - // the region offset was invalid. ignore. - } finally { - model.releaseFromRead(); - } - return data; - } - - /** - * Direct XML serialization of the XML node. - * <p/> - * This uses the generic Node interface with no SSE tricks. It's however slower - * and doesn't respect formatting (since serialization is involved instead of reading - * the actual text buffer.) - */ - private String getXmlTextFromSerialization(Node xml_node) throws IOException { - String data; - StringWriter sw = new StringWriter(); - XMLSerializer serializer = new XMLSerializer(sw, - new OutputFormat(Method.XML, - OutputFormat.Defaults.Encoding /* utf-8 */, - true /* indent */)); - // Serialize will throw an IOException if it fails. - serializer.serialize((Element) xml_node); - data = sw.toString(); - return data; - } - - /** - * Static helper class to wrap on node into a list for the constructors. - */ - private static ArrayList<UiElementNode> toList(UiElementNode selected) { - ArrayList<UiElementNode> list = null; - if (selected != null) { - list = new ArrayList<UiElementNode>(1); - list.add(selected); - } - return list; - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java deleted file mode 100644 index 067d1459e..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/ICommitXml.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -/** - * Interface for an object that can commit its changes to the underlying XML model - */ -public interface ICommitXml { - - /** Commits pending data to the underlying XML model. */ - public void commitPendingXmlChanges(); - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java deleted file mode 100644 index 385a53a5f..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/NewItemSelectionDialog.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog; -import org.eclipse.ui.dialogs.ISelectionStatusValidator; -import org.eclipse.ui.part.FileEditorInput; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; - -/** - * A selection dialog to select the type of the new element node to - * create, either in the application node or the selected sub node. - */ -public class NewItemSelectionDialog extends AbstractElementListSelectionDialog { - - /** The UI node selected in the tree view before creating the new item selection dialog. - * Can be null -- which means new items must be created in the root_node. */ - private UiElementNode mSelectedUiNode; - /** The root node chosen by the user, either root_node or the one passed - * to the constructor if not null */ - private UiElementNode mChosenRootNode; - private UiElementNode mLocalRootNode; - /** 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. Can be null. */ - 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. - * - * @param shell The parent shell for the list. - * @param labelProvider ILabelProvider for the list. - * @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 root_node The root of the Ui Tree, either the UiDocumentNode or a sub-node. - */ - public NewItemSelectionDialog(Shell shell, ILabelProvider labelProvider, - ElementDescriptor[] descriptorFilters, - UiElementNode ui_node, - UiElementNode root_node) { - super(shell, labelProvider); - mDescriptorFilters = descriptorFilters; - mLocalRootNode = root_node; - - // Only accept the UI node if it is not the UI root node and it can have children. - // If the node cannot have children, select its parent as a potential target. - if (ui_node != null && ui_node != mLocalRootNode) { - if (ui_node.getDescriptor().hasChildren()) { - mSelectedUiNode = ui_node; - } else { - UiElementNode parent = ui_node.getUiParent(); - if (parent != null && parent != mLocalRootNode) { - mSelectedUiNode = parent; - } - } - } - - setHelpAvailable(false); - setMultipleSelection(false); - - setValidator(new ISelectionStatusValidator() { - @Override - public IStatus validate(Object[] selection) { - if (selection.length == 1 && selection[0] instanceof ViewElementDescriptor) { - return new Status(IStatus.OK, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.OK, // code - ((ViewElementDescriptor) selection[0]).getFullClassName(), //msg - null); // exception - } else if (selection.length == 1 && selection[0] instanceof ElementDescriptor) { - return new Status(IStatus.OK, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.OK, // code - "", //$NON-NLS-1$ // msg - null); // exception - } else { - return new Status(IStatus.ERROR, // severity - AdtPlugin.PLUGIN_ID, //plugin id - IStatus.ERROR, // code - "Invalid selection", // msg, translatable - null); // exception - } - } - }); - - // 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) { - AndroidXmlEditor 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) { - AndroidXmlEditor 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; - } - - /** - * @return The root node selected by the user, either root node or the - * one passed to the constructor if not null. - */ - public UiElementNode getChosenRootNode() { - return mChosenRootNode; - } - - /** - * Internal helper to compute the result. Returns the selection from - * the list view, if any. - */ - @Override - protected void computeResult() { - setResult(Arrays.asList(getSelectedElements())); - setLastUsedXmlName(getSelectedElements()); - } - - /** - * Creates the dialog area. - * - * First add a radio area, which may be either 2 radio controls or - * just a message area if there's only one choice (the app root node). - * - * Then uses the default from the AbstractElementListSelectionDialog - * which is to add both a filter text and a filtered list. Adding both - * is necessary (since the base class accesses both internal directly - * fields without checking for null pointers.) - * - * Finally sets the initial selection list. - */ - @Override - protected Control createDialogArea(Composite parent) { - Composite contents = (Composite) super.createDialogArea(parent); - - createRadioControl(contents); - createFilterText(contents); - createFilteredList(contents); - - // We don't want the builtin message area label (we use a radio control - // instead), but if we don't create it, Bad Stuff happens on - // Eclipse 3.8 and later (see issue 32527). - Label label = createMessageArea(contents); - if (label != null) { - GridData data = (GridData) label.getLayoutData(); - data.exclude = true; - } - - // Initialize the list state. - // This must be done after the filtered list as been created. - chooseNode(mChosenRootNode); - - // Set the initial selection - setInitialSelection(mChosenRootNode); - 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(Locale.US); - } - - for (ElementDescriptor desc : getAllowedDescriptors(rootNode)) { - if (!partial && desc.getXmlName().equals(name)) { - initialElement = desc; - break; - } else if (partial) { - String name2 = desc.getXmlLocalName().toLowerCase(Locale.US); - 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. - * @param content the parent composite of the message area. - */ - private Composite createRadioControl(Composite content) { - - if (mSelectedUiNode != null) { - Button radio1 = new Button(content, SWT.RADIO); - radio1.setText(String.format("Create a new element at the top level, in %1$s.", - mLocalRootNode.getShortDescription())); - - Button radio2 = new Button(content, SWT.RADIO); - radio2.setText(String.format("Create a new element in the selected element, %1$s.", - mSelectedUiNode.getBreadcrumbTrailDescription(false /* include_root */))); - - // Set the initial selection before adding the listeners - // (they can't be run till the filtered list has been created) - radio1.setSelection(false); - radio2.setSelection(true); - mChosenRootNode = mSelectedUiNode; - - radio1.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - chooseNode(mLocalRootNode); - } - }); - - radio2.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - chooseNode(mSelectedUiNode); - } - }); - } else { - setMessage(String.format("Create a new element at the top level, in %1$s.", - mLocalRootNode.getShortDescription())); - createMessageArea(content); - - mChosenRootNode = mLocalRootNode; - } - - return content; - } - - /** - * Internal helper to remember the root node choosen by the user. - * It also sets the list view to the adequate list of children that can - * be added to the chosen root node. - * - * If the chosen root node is mLocalRootNode and a descriptor filter was specified - * when creating the master-detail part, we use this as the set of nodes that - * can be created on the root node. - * - * @param ui_node The chosen root node, either mLocalRootNode or - * mSelectedUiNode. - */ - private void chooseNode(UiElementNode 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 && - mDescriptorFilters != null && - mDescriptorFilters.length != 0) { - return mDescriptorFilters; - } else { - return ui_node.getDescriptor().getChildren(); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java deleted file mode 100644 index 6674ba9ca..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/PasteAction.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.action.Action; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; -import org.eclipse.wst.xml.core.internal.document.NodeContainer; -import org.w3c.dom.Node; - - -/** - * Provides Paste operation for the tree nodes - */ -@SuppressWarnings("restriction") -public class PasteAction extends Action { - private UiElementNode mUiNode; - private final AndroidXmlEditor mEditor; - private final Clipboard mClipboard; - - public PasteAction(AndroidXmlEditor editor, Clipboard clipboard, UiElementNode ui_node) { - super("Paste"); - mEditor = editor; - mClipboard = clipboard; - - ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED)); - - mUiNode = ui_node; - } - - /** - * Performs the paste operation. - */ - @Override - public void run() { - super.run(); - - final String data = (String) mClipboard.getContents(TextTransfer.getInstance()); - if (data != null) { - mEditor.wrapEditXmlModel(new Runnable() { - @Override - public void run() { - try { - IStructuredDocument sse_doc = mEditor.getStructuredDocument(); - if (sse_doc != null) { - if (mUiNode.getDescriptor().hasChildren()) { - // This UI Node can have children. The new XML is - // inserted as the first child. - - if (mUiNode.getUiChildren().size() > 0) { - // There's already at least one child, - // so insert right before it. - Node xml_node = mUiNode.getUiChildren().get(0).getXmlNode(); - - if (xml_node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) xml_node; - sse_doc.replace(region.getStartOffset(), 0, data); - return; // we're done, no need to try the other cases - } - } - - // If there's no first XML node child. Create one by - // inserting at the end of the *start* tag. - Node xml_node = mUiNode.getXmlNode(); - if (xml_node instanceof NodeContainer) { - NodeContainer container = (NodeContainer) xml_node; - IStructuredDocumentRegion start_tag = - container.getStartStructuredDocumentRegion(); - if (start_tag != null) { - sse_doc.replace(start_tag.getEndOffset(), 0, data); - return; // we're done, no need to try the other case - } - } - } - - // This UI Node doesn't accept children. The new XML is inserted as the - // next sibling. This also serves as a fallback if all the previous - // attempts failed. However, this is not possible if the current node - // has for parent a document -- an XML document can only have one root, - // with no siblings. - if (!(mUiNode.getUiParent() instanceof UiDocumentNode)) { - Node xml_node = mUiNode.getXmlNode(); - if (xml_node instanceof IndexedRegion) { - IndexedRegion region = (IndexedRegion) xml_node; - sse_doc.replace(region.getEndOffset(), 0, data); - } - } - } - - } catch (BadLocationException e) { - AdtPlugin.log(e, - "ParseAction failed for UI Node %2$s, content '%1$s'", //$NON-NLS-1$ - mUiNode.getBreadcrumbTrailDescription(true), data); - } - } - }); - } - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java deleted file mode 100644 index 92ccf2e7d..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiActions.java +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.widgets.Shell; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import java.util.List; - -/** - * Performs basic actions on an XML tree: add node, remove node, move up/down. - */ -public abstract class UiActions implements ICommitXml { - - public UiActions() { - } - - //--------------------- - // Actual implementations must override these to provide specific hooks - - /** Returns the UiDocumentNode for the current model. */ - abstract protected UiElementNode getRootNode(); - - /** Commits pending data before the XML model is modified. */ - @Override - abstract public void commitPendingXmlChanges(); - - /** - * Utility method to select an outline item based on its model node - * - * @param uiNode The node to select. Can be null (in which case nothing should happen) - */ - abstract protected void selectUiNode(UiElementNode uiNode); - - //--------------------- - - /** - * Called when the "Add..." button next to the tree view is selected. - * <p/> - * This simplified version of doAdd does not support descriptor filters and creates - * a new {@link UiModelTreeLabelProvider} for each call. - */ - public void doAdd(UiElementNode uiNode, Shell shell) { - doAdd(uiNode, null /* descriptorFilters */, shell, new UiModelTreeLabelProvider()); - } - - /** - * 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 - * to create, depending on the current selection. - */ - public void doAdd(UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Shell shell, ILabelProvider labelProvider) { - // If the root node is a document with already a root, use it as the root node - UiElementNode rootNode = getRootNode(); - if (rootNode instanceof UiDocumentNode && rootNode.getUiChildren().size() > 0) { - rootNode = rootNode.getUiChildren().get(0); - } - - NewItemSelectionDialog dlg = new NewItemSelectionDialog( - shell, - labelProvider, - descriptorFilters, - uiNode, rootNode); - dlg.open(); - Object[] results = dlg.getResult(); - if (results != null && results.length > 0) { - addElement(dlg.getChosenRootNode(), null, (ElementDescriptor) results[0], - true /*updateLayout*/); - } - } - - /** - * Adds a new XML element based on the {@link ElementDescriptor} to the given parent - * {@link UiElementNode}, and then select it. - * <p/> - * If the parent is a document root which already contains a root element, the inner - * root element is used as the actual parent. This ensure you can't create a broken - * XML file with more than one root element. - * <p/> - * If a sibling is given and that sibling has the same parent, the new node is added - * right after that sibling. Otherwise the new node is added at the end of the parent - * child list. - * - * @param uiParent An existing UI node or null to add to the tree root - * @param uiSibling An existing UI node before which to insert the new node. Can be null. - * @param descriptor The descriptor of the element to add - * @param updateLayout True if layout attributes should be set - * @return The new {@link UiElementNode} or null. - */ - public UiElementNode addElement(UiElementNode uiParent, - UiElementNode uiSibling, - ElementDescriptor descriptor, - boolean updateLayout) { - if (uiParent instanceof UiDocumentNode && uiParent.getUiChildren().size() > 0) { - uiParent = uiParent.getUiChildren().get(0); - } - if (uiSibling != null && uiSibling.getUiParent() != uiParent) { - uiSibling = null; - } - - UiElementNode uiNew = addNewTreeElement(uiParent, uiSibling, descriptor, updateLayout); - selectUiNode(uiNew); - - return uiNew; - } - - /** - * Called when the "Remove" button is selected. - * - * 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 - * update event, the tree will get refreshed. - */ - public void doRemove(final List<UiElementNode> nodes, Shell shell) { - - if (nodes == null || nodes.size() == 0) { - return; - } - - final int len = nodes.size(); - - StringBuilder sb = new StringBuilder(); - for (UiElementNode node : nodes) { - sb.append("\n- "); //$NON-NLS-1$ - sb.append(node.getBreadcrumbTrailDescription(false /* include_root */)); - } - - if (MessageDialog.openQuestion(shell, - len > 1 ? "Remove elements from Android XML" // title - : "Remove element from Android XML", - String.format("Do you really want to remove %1$s?", sb.toString()))) { - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - UiElementNode previous = null; - UiElementNode parent = null; - - for (int i = len - 1; i >= 0; i--) { - UiElementNode node = nodes.get(i); - previous = node.getUiPreviousSibling(); - parent = node.getUiParent(); - - // delete node - node.deleteXmlNode(); - } - - // try to select the last previous sibling or the last parent - if (previous != null) { - selectUiNode(previous); - } else if (parent != null) { - selectUiNode(parent); - } - } - }); - } - } - - /** - * Called when the "Up" button is selected. - * <p/> - * If the tree has a selection, move it up, either in the child list or as the last child - * of the previous parent. - */ - public void doUp( - final List<UiElementNode> uiNodes, - final ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiLastNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - for (int i = 0; i < uiNodes.size(); i++) { - UiElementNode uiNode = uiLastNode[0] = uiNodes.get(i); - doUpInternal( - uiNode, - descriptorFilters, - selectXmlNode, - uiSearchRoot, - false /*testOnly*/); - } - } - }); - - assert uiLastNode[0] != null; // tell Eclipse this can't be null below - - if (selectXmlNode[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(uiLastNode[0]); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (uiSearchRoot[0] == null) { - uiSearchRoot[0] = uiLastNode[0].getUiRoot(); - } - if (uiSearchRoot[0] != null) { - selectUiNode(uiSearchRoot[0].findXmlNode(selectXmlNode[0])); - } - } - } - - /** - * Checks whether the "up" action can be performed on all items. - * - * @return True if the up action can be carried on *all* items. - */ - public boolean canDoUp( - List<UiElementNode> uiNodes, - ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return false; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - - for (int i = 0; i < uiNodes.size(); i++) { - if (!doUpInternal( - uiNodes.get(i), - descriptorFilters, - selectXmlNode, - uiSearchRoot, - true /*testOnly*/)) { - return false; - } - } - - return true; - } - - private boolean doUpInternal( - UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Node[] outSelectXmlNode, - UiElementNode[] outUiSearchRoot, - boolean testOnly) { - // the node will move either up to its parent or grand-parent - outUiSearchRoot[0] = uiNode.getUiParent(); - if (outUiSearchRoot[0] != null && outUiSearchRoot[0].getUiParent() != null) { - outUiSearchRoot[0] = outUiSearchRoot[0].getUiParent(); - } - Node xmlNode = uiNode.getXmlNode(); - ElementDescriptor nodeDesc = uiNode.getDescriptor(); - if (xmlNode == null || nodeDesc == null) { - return false; - } - UiElementNode uiParentNode = uiNode.getUiParent(); - Node xmlParent = uiParentNode == null ? null : uiParentNode.getXmlNode(); - if (xmlParent == null) { - return false; - } - - UiElementNode uiPrev = uiNode.getUiPreviousSibling(); - - // Only accept a sibling that has an XML attached and - // is part of the allowed descriptor filters. - while (uiPrev != null && - (uiPrev.getXmlNode() == null || !matchDescFilter(descriptorFilters, uiPrev))) { - uiPrev = uiPrev.getUiPreviousSibling(); - } - - if (uiPrev != null && uiPrev.getXmlNode() != null) { - // This node is not the first one of the parent. - Node xmlPrev = uiPrev.getXmlNode(); - if (uiPrev.getDescriptor().acceptChild(nodeDesc)) { - // If the previous sibling can accept this child, then it - // is inserted at the end of the children list. - if (testOnly) { - return true; - } - xmlPrev.appendChild(xmlParent.removeChild(xmlNode)); - outSelectXmlNode[0] = xmlNode; - } else { - // This node is not the first one of the parent, so it can be - // removed and then inserted before its previous sibling. - if (testOnly) { - return true; - } - xmlParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlPrev); - outSelectXmlNode[0] = xmlNode; - } - } else if (uiParentNode != null && !(xmlParent instanceof Document)) { - UiElementNode uiGrandParent = uiParentNode.getUiParent(); - Node xmlGrandParent = uiGrandParent == null ? null : uiGrandParent.getXmlNode(); - ElementDescriptor grandDesc = - uiGrandParent == null ? null : uiGrandParent.getDescriptor(); - - if (xmlGrandParent != null && - !(xmlGrandParent instanceof Document) && - grandDesc != null && - grandDesc.acceptChild(nodeDesc)) { - // If the node is the first one of the child list of its - // parent, move it up in the hierarchy as previous sibling - // to the parent. This is only possible if the parent of the - // parent is not a document. - // The parent node must actually accept this kind of child. - - if (testOnly) { - return true; - } - xmlGrandParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlParent); - outSelectXmlNode[0] = xmlNode; - } - } - - return false; - } - - private boolean matchDescFilter( - ElementDescriptor[] descriptorFilters, - UiElementNode uiNode) { - if (descriptorFilters == null || descriptorFilters.length < 1) { - return true; - } - - ElementDescriptor desc = uiNode.getDescriptor(); - - for (ElementDescriptor filter : descriptorFilters) { - if (filter.equals(desc)) { - return true; - } - } - return false; - } - - /** - * 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 - * first child of the next parent. - */ - public void doDown( - final List<UiElementNode> nodes, - final ElementDescriptor[] descriptorFilters) { - if (nodes == null || nodes.size() < 1) { - return; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiLastNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - getRootNode().getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - for (int i = nodes.size() - 1; i >= 0; i--) { - final UiElementNode node = uiLastNode[0] = nodes.get(i); - doDownInternal( - node, - descriptorFilters, - selectXmlNode, - uiSearchRoot, - false /*testOnly*/); - } - } - }); - - assert uiLastNode[0] != null; // tell Eclipse this can't be null below - - if (selectXmlNode[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(uiLastNode[0]); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (uiSearchRoot[0] == null) { - uiSearchRoot[0] = uiLastNode[0].getUiRoot(); - } - if (uiSearchRoot[0] != null) { - selectUiNode(uiSearchRoot[0].findXmlNode(selectXmlNode[0])); - } - } - } - - /** - * Checks whether the "down" action can be performed on all items. - * - * @return True if the down action can be carried on *all* items. - */ - public boolean canDoDown( - List<UiElementNode> uiNodes, - ElementDescriptor[] descriptorFilters) { - if (uiNodes == null || uiNodes.size() < 1) { - return false; - } - - final Node[] selectXmlNode = { null }; - final UiElementNode[] uiSearchRoot = { null }; - - commitPendingXmlChanges(); - - for (int i = 0; i < uiNodes.size(); i++) { - if (!doDownInternal( - uiNodes.get(i), - descriptorFilters, - selectXmlNode, - uiSearchRoot, - true /*testOnly*/)) { - return false; - } - } - - return true; - } - - private boolean doDownInternal( - UiElementNode uiNode, - ElementDescriptor[] descriptorFilters, - Node[] outSelectXmlNode, - UiElementNode[] outUiSearchRoot, - boolean testOnly) { - // the node will move either down to its parent or grand-parent - outUiSearchRoot[0] = uiNode.getUiParent(); - if (outUiSearchRoot[0] != null && outUiSearchRoot[0].getUiParent() != null) { - outUiSearchRoot[0] = outUiSearchRoot[0].getUiParent(); - } - - Node xmlNode = uiNode.getXmlNode(); - ElementDescriptor nodeDesc = uiNode.getDescriptor(); - if (xmlNode == null || nodeDesc == null) { - return false; - } - UiElementNode uiParentNode = uiNode.getUiParent(); - Node xmlParent = uiParentNode == null ? null : uiParentNode.getXmlNode(); - if (xmlParent == null) { - return false; - } - - UiElementNode uiNext = uiNode.getUiNextSibling(); - - // Only accept a sibling that has an XML attached and - // is part of the allowed descriptor filters. - while (uiNext != null && - (uiNext.getXmlNode() == null || !matchDescFilter(descriptorFilters, uiNext))) { - uiNext = uiNext.getUiNextSibling(); - } - - if (uiNext != null && uiNext.getXmlNode() != null) { - // This node is not the last one of the parent. - Node xmlNext = uiNext.getXmlNode(); - // If the next sibling is a node that can have children, though, - // then the node is inserted as the first child. - if (uiNext.getDescriptor().acceptChild(nodeDesc)) { - if (testOnly) { - return true; - } - // Note: insertBefore works as append if the ref node is - // null, i.e. when the node doesn't have children yet. - xmlNext.insertBefore( - xmlParent.removeChild(xmlNode), - xmlNext.getFirstChild()); - outSelectXmlNode[0] = xmlNode; - } else { - // This node is not the last one of the parent, so it can be - // removed and then inserted after its next sibling. - - if (testOnly) { - return true; - } - // Insert "before after next" ;-) - xmlParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlNext.getNextSibling()); - outSelectXmlNode[0] = xmlNode; - } - } else if (uiParentNode != null && !(xmlParent instanceof Document)) { - UiElementNode uiGrandParent = uiParentNode.getUiParent(); - Node xmlGrandParent = uiGrandParent == null ? null : uiGrandParent.getXmlNode(); - ElementDescriptor grandDesc = - uiGrandParent == null ? null : uiGrandParent.getDescriptor(); - - if (xmlGrandParent != null && - !(xmlGrandParent instanceof Document) && - grandDesc != null && - grandDesc.acceptChild(nodeDesc)) { - // This node is the last node of its parent. - // If neither the parent nor the grandparent is a document, - // then the node can be inserted right after the parent. - // The parent node must actually accept this kind of child. - if (testOnly) { - return true; - } - xmlGrandParent.insertBefore( - xmlParent.removeChild(xmlNode), - xmlParent.getNextSibling()); - outSelectXmlNode[0] = xmlNode; - } - } - - return false; - } - - //--------------------- - - /** - * Adds a new element of the given descriptor's type to the given UI parent node. - * - * This actually creates the corresponding XML node in the XML model, which in turn - * will refresh the current tree view. - * - * @param uiParent An existing UI node or null to add to the tree root - * @param uiSibling An existing UI node to insert right before. Can be null. - * @param descriptor The descriptor of the element to add - * @param updateLayout True if layout attributes should be set - * @return The {@link UiElementNode} that has been added to the UI tree. - */ - private UiElementNode addNewTreeElement(UiElementNode uiParent, - UiElementNode uiSibling, - ElementDescriptor descriptor, - final boolean updateLayout) { - commitPendingXmlChanges(); - - List<UiElementNode> uiChildren = uiParent.getUiChildren(); - int n = uiChildren.size(); - - // The default is to append at the end of the list. - int index = n; - - if (uiSibling != null) { - // Try to find the requested sibling. - index = uiChildren.indexOf(uiSibling); - if (index < 0) { - // This sibling didn't exist. Should not happen but compensate - // by simply adding to the end of the list. - uiSibling = null; - index = n; - } - } - - if (uiSibling == null) { - // If we don't require any specific position, make sure to insert before the - // first mandatory_last descriptor's position, if any. - - for (int i = 0; i < n; i++) { - UiElementNode uiChild = uiChildren.get(i); - if (uiChild.getDescriptor().getMandatory() == Mandatory.MANDATORY_LAST) { - index = i; - break; - } - } - } - - final UiElementNode uiNew = uiParent.insertNewUiChild(index, descriptor); - UiElementNode rootNode = getRootNode(); - - rootNode.getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - DescriptorsUtils.setDefaultLayoutAttributes(uiNew, updateLayout); - uiNew.createXmlNode(); - } - }); - return uiNew; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java deleted file mode 100644 index 2aa56a826..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiElementDetail.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; -import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.ui.forms.IDetailsPage; -import org.eclipse.ui.forms.IFormPart; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.events.ExpansionEvent; -import org.eclipse.ui.forms.events.IExpansionListener; -import org.eclipse.ui.forms.widgets.FormText; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; -import org.eclipse.ui.forms.widgets.SharedScrolledComposite; -import org.eclipse.ui.forms.widgets.TableWrapData; -import org.eclipse.ui.forms.widgets.TableWrapLayout; - -import java.util.Collection; -import java.util.HashSet; - -/** - * Details page for the {@link UiElementNode} nodes in the tree view. - * <p/> - * See IDetailsBase for more details. - */ -class UiElementDetail implements IDetailsPage { - - /** The master-detail part, composed of a main tree and an auxiliary detail part */ - private ManifestSectionPart mMasterPart; - - private Section mMasterSection; - private UiElementNode mCurrentUiElementNode; - private Composite mCurrentTable; - private boolean mIsDirty; - - private IManagedForm mManagedForm; - - private final UiTreeBlock mTree; - - public UiElementDetail(UiTreeBlock tree) { - mTree = tree; - mMasterPart = mTree.getMasterPart(); - mManagedForm = mMasterPart.getManagedForm(); - } - - /* (non-java doc) - * Initializes the part. - */ - @Override - public void initialize(IManagedForm form) { - mManagedForm = form; - } - - /* (non-java doc) - * Creates the contents of the page in the provided parent. - */ - @Override - public void createContents(Composite parent) { - mMasterSection = createMasterSection(parent); - } - - /* (non-java doc) - * Called when the provided part has changed selection state. - * <p/> - * Only reply when our master part originates the selection. - */ - @Override - public void selectionChanged(IFormPart part, ISelection selection) { - if (part == mMasterPart && - !selection.isEmpty() && - selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - - Object first = tree_selection.getFirstElement(); - if (first instanceof UiElementNode) { - UiElementNode ui_node = (UiElementNode) first; - createUiAttributeControls(mManagedForm, ui_node); - } - } - } - - /* (non-java doc) - * Instructs it to commit the new (modified) data back into the model. - */ - @Override - public void commit(boolean onSave) { - - mTree.getEditor().wrapEditXmlModel(new Runnable() { - @Override - public void run() { - try { - if (mCurrentUiElementNode != null) { - mCurrentUiElementNode.commit(); - } - - // Finally reset the dirty flag if everything was saved properly - mIsDirty = false; - } catch (Exception e) { - AdtPlugin.log(e, "Detail node failed to commit XML attribute!"); //$NON-NLS-1$ - } - } - }); - } - - @Override - public void dispose() { - // pass - } - - - /* (non-java doc) - * Returns true if the part has been modified with respect to the data - * loaded from the model. - */ - @Override - public boolean isDirty() { - if (mCurrentUiElementNode != null && mCurrentUiElementNode.isDirty()) { - markDirty(); - } - return mIsDirty; - } - - @Override - public boolean isStale() { - // pass - return false; - } - - /** - * Called by the master part when the tree is refreshed after the framework resources - * have been reloaded. - */ - @Override - public void refresh() { - if (mCurrentTable != null) { - mCurrentTable.dispose(); - mCurrentTable = null; - } - mCurrentUiElementNode = null; - mMasterSection.getParent().pack(true /* changed */); - } - - @Override - public void setFocus() { - // pass - } - - @Override - public boolean setFormInput(Object input) { - // pass - return false; - } - - /** - * Creates a TableWrapLayout in the DetailsPage, which in turns contains a Section. - * - * All the UI should be created in a layout which parent is the mSection itself. - * The hierarchy is: - * <pre> - * DetailPage - * + TableWrapLayout - * + Section (with title/description && fill_grab horizontal) - * + TableWrapLayout [*] - * + Labels/Forms/etc... [*] - * </pre> - * Both items marked with [*] are created by the derived classes to fit their needs. - * - * @param parent Parent of the mSection (from createContents) - * @return The new Section - */ - private Section createMasterSection(Composite parent) { - TableWrapLayout layout = new TableWrapLayout(); - layout.topMargin = 0; - parent.setLayout(layout); - - FormToolkit toolkit = mManagedForm.getToolkit(); - Section section = toolkit.createSection(parent, Section.TITLE_BAR); - section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP)); - return section; - } - - /** - * Create the ui attribute controls to edit the attributes for the given - * ElementDescriptor. - * <p/> - * This is called by the constructor. - * Derived classes can override this if necessary. - * - * @param managedForm The managed form - */ - private void createUiAttributeControls( - final IManagedForm managedForm, - final UiElementNode ui_node) { - - final ElementDescriptor elem_desc = ui_node.getDescriptor(); - mMasterSection.setText(String.format("Attributes for %1$s", ui_node.getShortDescription())); - - if (mCurrentUiElementNode != ui_node) { - // Before changing the table, commit all dirty state. - if (mIsDirty) { - commit(false); - } - if (mCurrentTable != null) { - mCurrentTable.dispose(); - mCurrentTable = null; - } - - // To iterate over all attributes, we use the {@link ElementDescriptor} instead - // of the {@link UiElementNode} because the attributes order is guaranteed in the - // descriptor but not in the node itself. - AttributeDescriptor[] attr_desc_list = ui_node.getAttributeDescriptors(); - - // If the attribute list contains at least one SeparatorAttributeDescriptor, - // sub-sections will be used. This needs to be known early as it influences the - // creation of the master table. - boolean useSubsections = false; - for (AttributeDescriptor attr_desc : attr_desc_list) { - if (attr_desc instanceof SeparatorAttributeDescriptor) { - // Sub-sections will be used. The default sections should no longer be - useSubsections = true; - break; - } - } - - FormToolkit toolkit = managedForm.getToolkit(); - Composite masterTable = SectionHelper.createTableLayout(mMasterSection, - toolkit, useSubsections ? 1 : 2 /* numColumns */); - mCurrentTable = masterTable; - - mCurrentUiElementNode = ui_node; - - if (elem_desc.getTooltip() != null) { - String tooltip; - if (Sdk.getCurrent() != null && - Sdk.getCurrent().getDocumentationBaseUrl() != null) { - tooltip = DescriptorsUtils.formatFormText(elem_desc.getTooltip(), - elem_desc, - Sdk.getCurrent().getDocumentationBaseUrl()); - } else { - tooltip = elem_desc.getTooltip(); - } - - try { - FormText text = SectionHelper.createFormText(masterTable, toolkit, - true /* isHtml */, tooltip, true /* setupLayoutData */); - text.addHyperlinkListener(mTree.getEditor().createHyperlinkListener()); - Image icon = elem_desc.getCustomizedIcon(); - if (icon != null) { - text.setImage(DescriptorsUtils.IMAGE_KEY, icon); - } - } catch(Exception e) { - // The FormText parser is really really basic and will fail as soon as the - // HTML javadoc is ever so slightly malformatted. - AdtPlugin.log(e, - "Malformed javadoc, rejected by FormText for node %1$s: '%2$s'", //$NON-NLS-1$ - ui_node.getDescriptor().getXmlName(), - tooltip); - - // Fallback to a pure text tooltip, no fancy HTML - tooltip = DescriptorsUtils.formatTooltip(elem_desc.getTooltip()); - SectionHelper.createLabel(masterTable, toolkit, tooltip, tooltip); - } - } - - Composite table = useSubsections ? null : masterTable; - - for (AttributeDescriptor attr_desc : attr_desc_list) { - if (attr_desc instanceof XmlnsAttributeDescriptor) { - // Do not show hidden attributes - continue; - } else if (table == null || attr_desc instanceof SeparatorAttributeDescriptor) { - String title = null; - if (attr_desc instanceof SeparatorAttributeDescriptor) { - // xmlName is actually the label of the separator - title = attr_desc.getXmlLocalName(); - } else { - title = String.format("Attributes from %1$s", elem_desc.getUiName()); - } - - table = createSubSectionTable(toolkit, masterTable, title); - if (attr_desc instanceof SeparatorAttributeDescriptor) { - continue; - } - } - - UiAttributeNode ui_attr = ui_node.findUiAttribute(attr_desc); - - if (ui_attr != null) { - ui_attr.createUiControl(table, managedForm); - - if (ui_attr.getCurrentValue() != null && - ui_attr.getCurrentValue().length() > 0) { - ((Section) table.getParent()).setExpanded(true); - } - } else { - // The XML has an extra unknown attribute. - // This is not expected to happen so it is ignored. - AdtPlugin.log(IStatus.INFO, - "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ - attr_desc.getXmlLocalName(), - ui_node.getDescriptor().getXmlName()); - } - } - - // Create a sub-section for the unknown attributes. - // It is initially hidden till there are some attributes to show here. - final Composite unknownTable = createSubSectionTable(toolkit, masterTable, - "Unknown XML Attributes"); - unknownTable.getParent().setVisible(false); // set section to not visible - final HashSet<UiAttributeNode> reference = new HashSet<UiAttributeNode>(); - - final IUiUpdateListener updateListener = new IUiUpdateListener() { - @Override - public void uiElementNodeUpdated(UiElementNode uiNode, UiUpdateState state) { - if (state == UiUpdateState.ATTR_UPDATED) { - updateUnknownAttributesSection(uiNode, unknownTable, managedForm, - reference); - } - } - }; - ui_node.addUpdateListener(updateListener); - - // remove the listener when the UI is disposed - unknownTable.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - ui_node.removeUpdateListener(updateListener); - } - }); - - updateUnknownAttributesSection(ui_node, unknownTable, managedForm, reference); - - mMasterSection.getParent().pack(true /* changed */); - } - } - - /** - * Create a sub Section and its embedding wrapper table with 2 columns. - * @return The table, child of a new section. - */ - private Composite createSubSectionTable(FormToolkit toolkit, - Composite masterTable, String title) { - - // The Section composite seems to ignore colspan when assigned a TableWrapData so - // if the parent is a table with more than one column an extra table with one column - // is inserted to respect colspan. - int parentNumCol = ((TableWrapLayout) masterTable.getLayout()).numColumns; - if (parentNumCol > 1) { - masterTable = SectionHelper.createTableLayout(masterTable, toolkit, 1); - TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB); - twd.maxWidth = AndroidXmlEditor.TEXT_WIDTH_HINT; - twd.colspan = parentNumCol; - masterTable.setLayoutData(twd); - } - - Composite table; - Section section = toolkit.createSection(masterTable, - Section.TITLE_BAR | Section.TWISTIE); - - // Add an expansion listener that will trigger a reflow on the parent - // ScrolledPageBook (which is actually a SharedScrolledComposite). This will - // recompute the correct size and adjust the scrollbar as needed. - section.addExpansionListener(new IExpansionListener() { - @Override - public void expansionStateChanged(ExpansionEvent e) { - reflowMasterSection(); - } - - @Override - public void expansionStateChanging(ExpansionEvent e) { - // pass - } - }); - - section.setText(title); - section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, - TableWrapData.TOP)); - table = SectionHelper.createTableLayout(section, toolkit, 2 /* numColumns */); - return table; - } - - /** - * Reflow the parent ScrolledPageBook (which is actually a SharedScrolledComposite). - * This will recompute the correct size and adjust the scrollbar as needed. - */ - private void reflowMasterSection() { - for(Composite c = mMasterSection; c != null; c = c.getParent()) { - if (c instanceof SharedScrolledComposite) { - ((SharedScrolledComposite) c).reflow(true /* flushCache */); - break; - } - } - } - - /** - * Updates the unknown attributes section for the UI Node. - */ - private void updateUnknownAttributesSection(UiElementNode ui_node, - final Composite unknownTable, final IManagedForm managedForm, - HashSet<UiAttributeNode> reference) { - Collection<UiAttributeNode> ui_attrs = ui_node.getUnknownUiAttributes(); - Section section = ((Section) unknownTable.getParent()); - boolean needs_reflow = false; - - // The table was created hidden, show it if there are unknown attributes now - if (ui_attrs.size() > 0 && !section.isVisible()) { - section.setVisible(true); - needs_reflow = true; - } - - // Compare the new attribute set with the old "reference" one - boolean has_differences = ui_attrs.size() != reference.size(); - if (!has_differences) { - for (UiAttributeNode ui_attr : ui_attrs) { - if (!reference.contains(ui_attr)) { - has_differences = true; - break; - } - } - } - - if (has_differences) { - needs_reflow = true; - reference.clear(); - - // Remove all children of the table - for (Control c : unknownTable.getChildren()) { - c.dispose(); - } - - // Recreate all attributes UI - for (UiAttributeNode ui_attr : ui_attrs) { - reference.add(ui_attr); - ui_attr.createUiControl(unknownTable, managedForm); - - if (ui_attr.getCurrentValue() != null && ui_attr.getCurrentValue().length() > 0) { - section.setExpanded(true); - } - } - } - - if (needs_reflow) { - reflowMasterSection(); - } - } - - /** - * Marks the part dirty. Called as a result of user interaction with the widgets in the - * section. - */ - private void markDirty() { - if (!mIsDirty) { - mIsDirty = true; - mManagedForm.dirtyStateChanged(); - } - } -} - - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java deleted file mode 100644 index 14049cf86..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeContentProvider.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -import java.util.ArrayList; - -/** - * UiModelTreeContentProvider is a trivial implementation of {@link ITreeContentProvider} - * where elements are expected to be instances of {@link UiElementNode}. - */ -class UiModelTreeContentProvider implements ITreeContentProvider { - - /** 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. */ - private ElementDescriptor[] mDescriptorFilters; - /** The uiRootNode of the model. */ - private final UiElementNode mUiRootNode; - - public UiModelTreeContentProvider(UiElementNode uiRootNode, - ElementDescriptor[] descriptorFilters) { - mUiRootNode = uiRootNode; - mDescriptorFilters = descriptorFilters; - } - - /* (non-java doc) - * Returns all the UI node children of the given element or null if not the right kind - * of object. */ - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof UiElementNode) { - UiElementNode node = (UiElementNode) parentElement; - return node.getUiChildren().toArray(); - } - return null; - } - - /* (non-java doc) - * Returns the parent of a given UI node or null if it's a root node or it's not the - * right kind of node. */ - @Override - public Object getParent(Object element) { - if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getUiParent(); - } - return null; - } - - /* (non-java doc) - * Returns true if the UI node has any UI children nodes. */ - @Override - public boolean hasChildren(Object element) { - if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getUiChildren().size() > 0; - } - return false; - } - - /* (non-java doc) - * Get root elements for the tree. These are all the UI nodes that - * match the filter descriptor in the current root node. - * <p/> - * Although not documented, it seems this method should not return null. - * At worse, it should return new Object[0]. - * <p/> - * inputElement is not currently used. The root node and the filter are given - * by the enclosing class. - */ - @Override - public Object[] getElements(Object inputElement) { - ArrayList<UiElementNode> roots = new ArrayList<UiElementNode>(); - if (mUiRootNode != null) { - for (UiElementNode ui_node : mUiRootNode.getUiChildren()) { - if (mDescriptorFilters == null || mDescriptorFilters.length == 0) { - roots.add(ui_node); - } else { - for (ElementDescriptor filter : mDescriptorFilters) { - if (ui_node.getDescriptor() == filter) { - roots.add(ui_node); - } - } - } - } - } - - return roots.toArray(); - } - - @Override - public void dispose() { - // pass - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java deleted file mode 100644 index 337319761..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiModelTreeLabelProvider.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; - -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.swt.graphics.Image; - -/** - * UiModelTreeLabelProvider is a trivial implementation of {@link ILabelProvider} - * where elements are expected to derive from {@link UiElementNode} or - * from {@link ElementDescriptor}. - * - * It is used by both the master tree viewer and by the list in the Add... selection dialog. - */ -public class UiModelTreeLabelProvider implements ILabelProvider { - - public UiModelTreeLabelProvider() { - } - - /** - * Returns the element's logo with a fallback on the android logo. - */ - @Override - public Image getImage(Object element) { - ElementDescriptor desc = null; - UiElementNode node = null; - - if (element instanceof ElementDescriptor) { - desc = (ElementDescriptor) element; - } else if (element instanceof UiElementNode) { - node = (UiElementNode) element; - desc = node.getDescriptor(); - } - - if (desc != null) { - Image img = desc.getCustomizedIcon(); - if (img != null) { - if (node != null && node.hasError()) { - return IconFactory.getInstance().addErrorIcon(img); - } else { - return img; - } - } - } - - return AdtPlugin.getAndroidLogo(); - } - - /** - * Uses UiElementNode.shortDescription for the label for this tree item. - */ - @Override - public String getText(Object element) { - if (element instanceof ElementDescriptor) { - ElementDescriptor desc = (ElementDescriptor) element; - return desc.getUiName(); - } else if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - return node.getShortDescription(); - } - return element.toString(); - } - - @Override - public void addListener(ILabelProviderListener listener) { - // pass - } - - @Override - public void dispose() { - // pass - } - - @Override - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - @Override - public void removeListener(ILabelProviderListener listener) { - // pass - } -} - - diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java deleted file mode 100644 index d11b8a4c6..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.editors.ui.tree; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; -import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; -import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; - -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.IMenuListener; -import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.jface.action.ToolBarManager; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.TreeSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.ui.forms.DetailsPart; -import org.eclipse.ui.forms.IDetailsPage; -import org.eclipse.ui.forms.IDetailsPageProvider; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.MasterDetailsBlock; -import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.forms.widgets.Section; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; - -/** - * {@link UiTreeBlock} is a {@link MasterDetailsBlock} which displays a tree view for - * a specific set of {@link UiElementNode}. - * <p/> - * For a given UI element node, the tree view displays all first-level children that - * match a given type (given by an {@link ElementDescriptor}. All children from these - * nodes are also displayed. - * <p/> - * In the middle next to the tree are some controls to add or delete tree nodes. - * On the left is a details part that displays all the visible UI attributes for a given - * selected UI element node. - */ -public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml { - - /** Height hint for the tree view. Helps the grid layout resize properly on smaller screens. */ - private static final int TREE_HEIGHT_HINT = 50; - - /** Container editor */ - AndroidXmlEditor mEditor; - /** 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. */ - private UiElementNode mUiRootNode; - /** 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. Can be null or empty to mean everything - * can be displayed. */ - private ElementDescriptor[] mDescriptorFilters; - /** The title for the master-detail part (displayed on the top "tab" on top of the tree) */ - private String mTitle; - /** The description for the master-detail part (displayed on top of the tree view) */ - private String mDescription; - /** The master-detail part, composed of a main tree and an auxiliary detail part */ - private ManifestSectionPart mMasterPart; - /** The tree viewer in the master-detail part */ - private TreeViewer mTreeViewer; - /** The "add" button for the tree view */ - private Button mAddButton; - /** The "remove" button for the tree view */ - private Button mRemoveButton; - /** The "up" button for the tree view */ - private Button mUpButton; - /** The "down" button for the tree view */ - private Button mDownButton; - /** The Managed Form used to create the master part */ - private IManagedForm mManagedForm; - /** Reference to the details part of the tree master block. */ - private DetailsPart mDetailsPart; - /** Reference to the clipboard for copy-paste */ - private Clipboard mClipboard; - /** Listener to refresh the tree viewer when the parent's node has been updated */ - private IUiUpdateListener mUiRefreshListener; - /** Listener to enable/disable the UI based on the application node's presence */ - private IUiUpdateListener mUiEnableListener; - /** An adapter/wrapper to use the add/remove/up/down tree edit actions. */ - private UiTreeActions mUiTreeActions; - /** - * True if the root node can be created on-demand (i.e. as needed as - * soon as children exist). False if an external entity controls the existence of the - * root node. In practise, this is false for the manifest application page (the actual - * "application" node is managed by the ApplicationToggle part) whereas it is true - * for all other tree pages. - */ - private final boolean mAutoCreateRoot; - - - /** - * Creates a new {@link MasterDetailsBlock} that will display all UI nodes matching the - * given filter in the given root node. - * - * @param editor The parent manifest editor. - * @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 - * application UI node. This cannot be null. - * @param autoCreateRoot True if the root node can be created on-demand (i.e. as needed as - * soon as children exist). False if an external entity controls the existence of the - * root node. In practise, this is false for the manifest application page (the actual - * "application" node is managed by the ApplicationToggle part) whereas it is true - * for all other tree pages. - * @param descriptorFilters A list of descriptors of the elements to be displayed as root in - * this tree view. Use null or an empty list to accept any kind of node. - * @param title Title for the section - * @param description Description for the section - */ - public UiTreeBlock(AndroidXmlEditor editor, - UiElementNode uiRootNode, - boolean autoCreateRoot, - ElementDescriptor[] descriptorFilters, - String title, - String description) { - mEditor = editor; - mUiRootNode = uiRootNode; - mAutoCreateRoot = autoCreateRoot; - mDescriptorFilters = descriptorFilters; - mTitle = title; - mDescription = description; - } - - /** @returns The container editor */ - AndroidXmlEditor getEditor() { - return mEditor; - } - - /** @returns The reference to the clipboard for copy-paste */ - Clipboard getClipboard() { - return mClipboard; - } - - /** @returns The master-detail part, composed of a main tree and an auxiliary detail part */ - ManifestSectionPart getMasterPart() { - return mMasterPart; - } - - /** - * Returns the {@link UiElementNode} for the current model. - * <p/> - * This is used by the content provider attached to {@link #mTreeViewer} since - * the uiRootNode changes after each call to - * {@link #changeRootAndDescriptors(UiElementNode, ElementDescriptor[], boolean)}. - */ - public UiElementNode getRootNode() { - return mUiRootNode; - } - - @Override - protected void createMasterPart(final IManagedForm managedForm, Composite parent) { - FormToolkit toolkit = managedForm.getToolkit(); - - mManagedForm = managedForm; - mMasterPart = new ManifestSectionPart(parent, toolkit); - Section section = mMasterPart.getSection(); - section.setText(mTitle); - section.setDescription(mDescription); - section.setLayout(new GridLayout()); - section.setLayoutData(new GridData(GridData.FILL_BOTH)); - - Composite grid = SectionHelper.createGridLayout(section, toolkit, 2); - - Tree tree = createTreeViewer(toolkit, grid, managedForm); - createButtons(toolkit, grid); - createTreeContextMenu(tree); - createSectionActions(section, toolkit); - } - - private void createSectionActions(Section section, FormToolkit toolkit) { - ToolBarManager manager = new ToolBarManager(SWT.FLAT); - manager.removeAll(); - - ToolBar toolbar = manager.createControl(section); - section.setTextClient(toolbar); - - ElementDescriptor[] descs = mDescriptorFilters; - if (descs == null && mUiRootNode != null) { - descs = mUiRootNode.getDescriptor().getChildren(); - } - - if (descs != null && descs.length > 1) { - for (ElementDescriptor desc : descs) { - manager.add(new DescriptorFilterAction(desc)); - } - } - - manager.add(new TreeSortAction()); - - manager.update(true /*force*/); - } - - /** - * Creates the tree and its viewer - * @return The tree control - */ - private Tree createTreeViewer(FormToolkit toolkit, Composite grid, - final IManagedForm managedForm) { - // Note: we *could* use a FilteredTree instead of the Tree+TreeViewer here. - // However the class must be adapted to create an adapted toolkit tree. - final Tree tree = toolkit.createTree(grid, SWT.MULTI); - GridData gd = new GridData(GridData.FILL_BOTH); - gd.widthHint = AndroidXmlEditor.TEXT_WIDTH_HINT; - gd.heightHint = TREE_HEIGHT_HINT; - tree.setLayoutData(gd); - - mTreeViewer = new TreeViewer(tree); - mTreeViewer.setContentProvider(new UiModelTreeContentProvider(mUiRootNode, mDescriptorFilters)); - mTreeViewer.setLabelProvider(new UiModelTreeLabelProvider()); - mTreeViewer.setInput("unused"); //$NON-NLS-1$ - - // Create a listener that reacts to selections on the tree viewer. - // When a selection is made, ask the managed form to propagate an event to - // all parts in the managed form. - // This is picked up by UiElementDetail.selectionChanged(). - mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { - @Override - public void selectionChanged(SelectionChangedEvent event) { - managedForm.fireSelectionChanged(mMasterPart, event.getSelection()); - adjustTreeButtons(event.getSelection()); - } - }); - - // 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 framework resources have changed - // - One to enable/disable the UI based on the application node's presence. - mUiRefreshListener = new IUiUpdateListener() { - @Override - public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { - mTreeViewer.refresh(); - } - }; - - mUiEnableListener = new IUiUpdateListener() { - @Override - public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { - // The UiElementNode for the application XML node always exists, even - // if there is no corresponding XML node in the XML file. - // - // Normally, we enable the UI here if the XML node is not null. - // - // However if mAutoCreateRoot is true, the root node will be created on-demand - // so the tree/block is always enabled. - boolean exists = mAutoCreateRoot || (ui_node.getXmlNode() != null); - if (mMasterPart != null) { - Section section = mMasterPart.getSection(); - if (section.getEnabled() != exists) { - section.setEnabled(exists); - for (Control c : section.getChildren()) { - c.setEnabled(exists); - } - } - } - } - }; - - /** Listener to update the root node if the target of the file is changed because of a - * SDK location change or a project target change */ - final ITargetChangeListener targetListener = new TargetChangeListener() { - @Override - public IProject getProject() { - if (mEditor != null) { - return mEditor.getProject(); - } - - return null; - } - - @Override - public void reload() { - // If a details part has been created, we need to "refresh" it too. - if (mDetailsPart != null) { - // The details part does not directly expose access to its internal - // page book. Instead it is possible to resize the page book to 0 and then - // back to its original value, which has the side effect of removing all - // existing cached pages. - int limit = mDetailsPart.getPageLimit(); - mDetailsPart.setPageLimit(0); - mDetailsPart.setPageLimit(limit); - } - // Refresh the tree, preserving the selection if possible. - mTreeViewer.refresh(); - } - }; - - // Setup the listeners - changeRootAndDescriptors(mUiRootNode, mDescriptorFilters, false /* refresh */); - - // Listen on resource framework changes to refresh the tree - AdtPlugin.getDefault().addTargetListener(targetListener); - - // Remove listeners when the tree widget gets disposed. - tree.addDisposeListener(new DisposeListener() { - @Override - public void widgetDisposed(DisposeEvent e) { - if (mUiRootNode != null) { - UiElementNode node = mUiRootNode.getUiParent() != null ? - mUiRootNode.getUiParent() : - mUiRootNode; - - if (node != null) { - node.removeUpdateListener(mUiRefreshListener); - } - mUiRootNode.removeUpdateListener(mUiEnableListener); - } - - AdtPlugin.getDefault().removeTargetListener(targetListener); - if (mClipboard != null) { - mClipboard.dispose(); - mClipboard = null; - } - } - }); - - // Get a new clipboard reference. It is disposed when the tree is disposed. - mClipboard = new Clipboard(tree.getDisplay()); - - return tree; - } - - /** - * Changes the UI root node and the descriptor filters of the tree. - * <p/> - * This removes the listeners attached to the old root node and reattaches them to the - * new one. - * - * @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 - * application UI node. This cannot be null. - * @param descriptorFilters A list of descriptors of the elements to be displayed as root in - * this tree view. Use null or an empty list to accept any kind of node. - * @param forceRefresh If tree, forces the tree to refresh - */ - public void changeRootAndDescriptors(UiElementNode uiRootNode, - ElementDescriptor[] descriptorFilters, boolean forceRefresh) { - UiElementNode node; - - // Remove previous listeners if any - if (mUiRootNode != null) { - node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode; - node.removeUpdateListener(mUiRefreshListener); - mUiRootNode.removeUpdateListener(mUiEnableListener); - } - - mUiRootNode = uiRootNode; - mDescriptorFilters = descriptorFilters; - - mTreeViewer.setContentProvider( - new UiModelTreeContentProvider(mUiRootNode, mDescriptorFilters)); - - // Listen on structural changes on the root node of the tree - // If the node has a parent, listen on the parent instead. - if (mUiRootNode != null) { - node = mUiRootNode.getUiParent() != null ? mUiRootNode.getUiParent() : mUiRootNode; - - if (node != null) { - node.addUpdateListener(mUiRefreshListener); - } - - // Use the root node to listen to its presence. - mUiRootNode.addUpdateListener(mUiEnableListener); - - // Initialize the enabled/disabled state - mUiEnableListener.uiElementNodeUpdated(mUiRootNode, null /* state, not used */); - } - - if (forceRefresh) { - mTreeViewer.refresh(); - } - - createSectionActions(mMasterPart.getSection(), mManagedForm.getToolkit()); - } - - /** - * Creates the buttons next to the tree. - */ - private void createButtons(FormToolkit toolkit, Composite grid) { - - mUiTreeActions = new UiTreeActions(); - - Composite button_grid = SectionHelper.createGridLayout(grid, toolkit, 1); - button_grid.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); - mAddButton = toolkit.createButton(button_grid, "Add...", SWT.PUSH); - SectionHelper.addControlTooltip(mAddButton, "Adds a new element."); - mAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | - GridData.VERTICAL_ALIGN_BEGINNING)); - - mAddButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - doTreeAdd(); - } - }); - - mRemoveButton = toolkit.createButton(button_grid, "Remove...", SWT.PUSH); - SectionHelper.addControlTooltip(mRemoveButton, "Removes an existing selected element."); - mRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - mRemoveButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - doTreeRemove(); - } - }); - - mUpButton = toolkit.createButton(button_grid, "Up", SWT.PUSH); - SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element up."); - mUpButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - mUpButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - doTreeUp(); - } - }); - - mDownButton = toolkit.createButton(button_grid, "Down", SWT.PUSH); - SectionHelper.addControlTooltip(mRemoveButton, "Moves the selected element down."); - mDownButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - mDownButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - doTreeDown(); - } - }); - - adjustTreeButtons(TreeSelection.EMPTY); - } - - private void createTreeContextMenu(Tree tree) { - MenuManager menuManager = new MenuManager(); - menuManager.setRemoveAllWhenShown(true); - menuManager.addMenuListener(new IMenuListener() { - /** - * The menu is about to be shown. The menu manager has already been - * requested to remove any existing menu item. This method gets the - * tree selection and if it is of the appropriate type it re-creates - * the necessary actions. - */ - @Override - public void menuAboutToShow(IMenuManager manager) { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - doCreateMenuAction(manager, selected); - return; - } - doCreateMenuAction(manager, null /* ui_node */); - } - }); - Menu contextMenu = menuManager.createContextMenu(tree); - tree.setMenu(contextMenu); - } - - /** - * Adds the menu actions to the context menu when the given UI node is selected in - * the tree view. - * - * @param manager The context menu manager - * @param selected The UI nodes selected in the tree. Can be null, in which case the root - * is to be modified. - */ - private void doCreateMenuAction(IMenuManager manager, ArrayList<UiElementNode> selected) { - if (selected != null) { - boolean hasXml = false; - for (UiElementNode uiNode : selected) { - if (uiNode.getXmlNode() != null) { - hasXml = true; - break; - } - } - - if (hasXml) { - manager.add(new CopyCutAction(getEditor(), getClipboard(), - null, selected, true /* cut */)); - manager.add(new CopyCutAction(getEditor(), getClipboard(), - null, selected, false /* cut */)); - - // Can't paste with more than one element selected (the selection is the target) - if (selected.size() <= 1) { - // Paste is not valid if it would add a second element on a terminal element - // which parent is a document -- an XML document can only have one child. This - // means paste is valid if the current UI node can have children or if the - // parent is not a document. - UiElementNode ui_root = selected.get(0).getUiRoot(); - if (ui_root.getDescriptor().hasChildren() || - !(ui_root.getUiParent() instanceof UiDocumentNode)) { - manager.add(new PasteAction(getEditor(), getClipboard(), selected.get(0))); - } - } - manager.add(new Separator()); - } - } - - // Append "add" and "remove" actions. They do the same thing as the add/remove - // buttons on the side. - IconFactory factory = IconFactory.getInstance(); - - // "Add" makes sense only if there's 0 or 1 item selected since the - // one selected item becomes the target. - if (selected == null || selected.size() <= 1) { - manager.add(new Action("Add...", factory.getImageDescriptor("add")) { //$NON-NLS-1$ - @Override - public void run() { - super.run(); - doTreeAdd(); - } - }); - } - - if (selected != null) { - if (selected != null) { - manager.add(new Action("Remove", factory.getImageDescriptor("delete")) { //$NON-NLS-1$ - @Override - public void run() { - super.run(); - doTreeRemove(); - } - }); - } - manager.add(new Separator()); - - manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-1$ - @Override - public void run() { - super.run(); - doTreeUp(); - } - }); - manager.add(new Action("Down", factory.getImageDescriptor("down")) { //$NON-NLS-1$ - @Override - public void run() { - super.run(); - doTreeDown(); - } - }); - } - } - - - /** - * This is called by the tree when a selection is made. - * It enables/disables the buttons associated with the tree depending on the current - * selection. - * - * @param selection The current tree selection (same as mTreeViewer.getSelection()) - */ - private void adjustTreeButtons(ISelection selection) { - mRemoveButton.setEnabled(!selection.isEmpty() && selection instanceof ITreeSelection); - mUpButton.setEnabled(canDoTreeUp(selection)); - mDownButton.setEnabled(canDoTreeDown(selection)); - } - - /** - * An adapter/wrapper to use the add/remove/up/down tree edit actions. - */ - private class UiTreeActions extends UiActions { - @Override - protected UiElementNode getRootNode() { - return mUiRootNode; - } - - @Override - protected void selectUiNode(UiElementNode uiNodeToSelect) { - // Select the new item - if (uiNodeToSelect != null) { - LinkedList<UiElementNode> segments = new LinkedList<UiElementNode>(); - for (UiElementNode ui_node = uiNodeToSelect; ui_node != mUiRootNode; - ui_node = ui_node.getUiParent()) { - segments.add(0, ui_node); - } - if (segments.size() > 0) { - mTreeViewer.setSelection(new TreeSelection(new TreePath(segments.toArray()))); - } else { - mTreeViewer.setSelection(null); - } - } - } - - @Override - public void commitPendingXmlChanges() { - commitManagedForm(); - } - } - - /** - * Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's - * something else in there). - * - * @return A new list of {@link UiElementNode} with at least one item or null. - */ - private ArrayList<UiElementNode> filterSelection(ITreeSelection selection) { - ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); - - for (Iterator<Object> it = selection.iterator(); it.hasNext(); ) { - Object selectedObj = it.next(); - - if (selectedObj instanceof UiElementNode) { - selected.add((UiElementNode) selectedObj); - } - } - - return selected.size() > 0 ? selected : null; - } - - /** - * 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 - * to create, depending on the current selection. - */ - private void doTreeAdd() { - UiElementNode ui_node = mUiRootNode; - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - Object first = tree_selection.getFirstElement(); - if (first != null && first instanceof UiElementNode) { - ui_node = (UiElementNode) first; - } - } - - mUiTreeActions.doAdd( - ui_node, - mDescriptorFilters, - mTreeViewer.getControl().getShell(), - (ILabelProvider) mTreeViewer.getLabelProvider()); - } - - /** - * Called when the "Remove" button is selected. - * - * 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 - * update event, the tree will get refreshed. - */ - protected void doTreeRemove() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doRemove(selected, mTreeViewer.getControl().getShell()); - } - } - - /** - * Called when the "Up" button is selected. - * <p/> - * If the tree has a selection, move it up, either in the child list or as the last child - * of the previous parent. - */ - protected void doTreeUp() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doUp(selected, mDescriptorFilters); - } - } - - /** - * Checks whether the "up" action can be done on the current selection. - * - * @param selection The current tree selection. - * @return True if all the selected nodes can be moved up. - */ - protected boolean canDoTreeUp(ISelection selection) { - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - return mUiTreeActions.canDoUp(selected, mDescriptorFilters); - } - - return false; - } - - /** - * 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 - * first child of the next parent. - */ - protected void doTreeDown() { - ISelection selection = mTreeViewer.getSelection(); - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - mUiTreeActions.doDown(selected, mDescriptorFilters); - } - } - - /** - * Checks whether the "down" action can be done on the current selection. - * - * @param selection The current tree selection. - * @return True if all the selected nodes can be moved down. - */ - protected boolean canDoTreeDown(ISelection selection) { - if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); - return mUiTreeActions.canDoDown(selected, mDescriptorFilters); - } - - return false; - } - - /** - * Commits the current managed form (the one associated with our master part). - * As a side effect, this will commit the current UiElementDetails page. - */ - void commitManagedForm() { - if (mManagedForm != null) { - mManagedForm.commit(false /* onSave */); - } - } - - /* Implements ICommitXml for CopyCutAction */ - @Override - public void commitPendingXmlChanges() { - commitManagedForm(); - } - - @Override - protected void createToolBarActions(IManagedForm managedForm) { - // Pass. Not used, toolbar actions are defined by createSectionActions(). - } - - @Override - protected void registerPages(DetailsPart inDetailsPart) { - // Keep a reference on the details part (the super class doesn't provide a getter - // for it.) - mDetailsPart = inDetailsPart; - - // 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 - // 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 - // page book. - inDetailsPart.setPageLimit(5); - - final UiTreeBlock tree = this; - - inDetailsPart.setPageProvider(new IDetailsPageProvider() { - @Override - public IDetailsPage getPage(Object key) { - if (key instanceof UiElementNode) { - return new UiElementDetail(tree); - } - return null; - } - - @Override - public Object getPageKey(Object object) { - return object; // use node object as key - } - }); - } - - /** - * An alphabetic sort action for the tree viewer. - */ - private class TreeSortAction extends Action { - - private ViewerComparator mComparator; - - public TreeSortAction() { - super("Sorts elements alphabetically.", AS_CHECK_BOX); - setImageDescriptor(IconFactory.getInstance().getImageDescriptor("az_sort")); //$NON-NLS-1$ - - if (mTreeViewer != null) { - boolean is_sorted = mTreeViewer.getComparator() != null; - setChecked(is_sorted); - } - } - - /** - * Called when the button is selected. Toggles the tree viewer comparator. - */ - @Override - public void run() { - if (mTreeViewer == null) { - notifyResult(false /*success*/); - return; - } - - ViewerComparator comp = mTreeViewer.getComparator(); - if (comp != null) { - // Tree is currently sorted. - // Save currently comparator and remove it - mComparator = comp; - mTreeViewer.setComparator(null); - } else { - // Tree is not currently sorted. - // Reuse or add a new comparator. - if (mComparator == null) { - mComparator = new ViewerComparator(); - } - mTreeViewer.setComparator(mComparator); - } - - notifyResult(true /*success*/); - } - } - - /** - * A filter on descriptor for the tree viewer. - * <p/> - * The tree viewer will contain many of these actions and only one can be enabled at a - * given time. When no action is selected, everything is displayed. - * <p/> - * Since "radio"-like actions do not allow for unselecting all of them, we manually - * handle the exclusive radio button-like property: when an action is selected, it manually - * removes all other actions as needed. - */ - private class DescriptorFilterAction extends Action { - - private final ElementDescriptor mDescriptor; - private ViewerFilter mFilter; - - public DescriptorFilterAction(ElementDescriptor descriptor) { - super(String.format("Displays only %1$s elements.", descriptor.getUiName()), - AS_CHECK_BOX); - - mDescriptor = descriptor; - setImageDescriptor(descriptor.getImageDescriptor()); - } - - /** - * Called when the button is selected. - * <p/> - * Find any existing {@link DescriptorFilter}s and remove them. Install ours. - */ - @Override - public void run() { - super.run(); - - if (isChecked()) { - if (mFilter == null) { - // create filter when required - mFilter = new DescriptorFilter(this); - } - - // we add our filter first, otherwise the UI might show the full list - mTreeViewer.addFilter(mFilter); - - // Then remove the any other filters except ours. There should be at most - // one other filter, since that's how the actions are made to look like - // exclusive radio buttons. - for (ViewerFilter filter : mTreeViewer.getFilters()) { - if (filter instanceof DescriptorFilter && filter != mFilter) { - DescriptorFilterAction action = ((DescriptorFilter) filter).getAction(); - action.setChecked(false); - mTreeViewer.removeFilter(filter); - } - } - } else if (mFilter != null){ - mTreeViewer.removeFilter(mFilter); - } - } - - /** - * Filters the tree viewer for the given descriptor. - * <p/> - * The filter is linked to the action so that an action can iterate through the list - * of filters and un-select the actions. - */ - private class DescriptorFilter extends ViewerFilter { - - private final DescriptorFilterAction mAction; - - public DescriptorFilter(DescriptorFilterAction action) { - mAction = action; - } - - public DescriptorFilterAction getAction() { - return mAction; - } - - /** - * Returns true if an element should be displayed, that if the element or - * any of its parent matches the requested descriptor. - */ - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - while (element instanceof UiElementNode) { - UiElementNode uiNode = (UiElementNode)element; - if (uiNode.getDescriptor() == mDescriptor) { - return true; - } - element = uiNode.getUiParent(); - } - return false; - } - } - } - -} |