diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java | 1439 |
1 files changed, 0 insertions, 1439 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java deleted file mode 100644 index 8178c6871..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java +++ /dev/null @@ -1,1439 +0,0 @@ -/* - * Copyright (C) 2010 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.layout.gle2; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_COLUMN_COUNT; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN; -import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN; -import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW; -import static com.android.SdkConstants.ATTR_LAYOUT_ROW_SPAN; -import static com.android.SdkConstants.ATTR_ROW_COUNT; -import static com.android.SdkConstants.ATTR_SRC; -import static com.android.SdkConstants.ATTR_TEXT; -import static com.android.SdkConstants.AUTO_URI; -import static com.android.SdkConstants.DRAWABLE_PREFIX; -import static com.android.SdkConstants.GRID_LAYOUT; -import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; -import static com.android.SdkConstants.URI_PREFIX; -import static org.eclipse.jface.viewers.StyledString.COUNTER_STYLER; -import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER; - -import com.android.SdkConstants; -import com.android.annotations.VisibleForTesting; -import com.android.ide.common.api.INode; -import com.android.ide.common.api.InsertType; -import com.android.ide.common.layout.BaseLayoutRule; -import com.android.ide.common.layout.GridLayoutRule; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; -import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; -import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertySheetPage; -import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ActionContributionItem; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.action.IContributionItem; -import org.eclipse.jface.action.IMenuListener; -import org.eclipse.jface.action.IMenuManager; -import org.eclipse.jface.action.IToolBarManager; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.jface.preference.JFacePreferences; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IElementComparer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.ITreeSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StyledCellLabelProvider; -import org.eclipse.jface.viewers.StyledString; -import org.eclipse.jface.viewers.StyledString.Styler; -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.ViewerCell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.MenuDetectEvent; -import org.eclipse.swt.events.MenuDetectListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeItem; -import org.eclipse.ui.IActionBars; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.INullSelectionListener; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.actions.ActionFactory; -import org.eclipse.ui.views.contentoutline.ContentOutlinePage; -import org.eclipse.wb.core.controls.SelfOrientingSashForm; -import org.eclipse.wb.internal.core.editor.structure.IPage; -import org.eclipse.wb.internal.core.editor.structure.PageSiteComposite; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * An outline page for the layout canvas view. - * <p/> - * The page is created by {@link LayoutEditorDelegate#delegateGetAdapter(Class)}. This means - * we have *one* instance of the outline page per open canvas editor. - * <p/> - * It sets itself as a listener on the site's selection service in order to be - * notified of the canvas' selection changes. - * The underlying page is also a selection provider (via IContentOutlinePage) - * and as such it will broadcast selection changes to the site's selection service - * (on which both the layout editor part and the property sheet page listen.) - */ -public class OutlinePage extends ContentOutlinePage - implements INullSelectionListener, IPage { - - /** Label which separates outline text from additional attributes like text prefix or url */ - private static final String LABEL_SEPARATOR = " - "; - - /** Max character count in labels, used for truncation */ - private static final int LABEL_MAX_WIDTH = 50; - - /** - * The graphical editor that created this outline. - */ - private final GraphicalEditorPart mGraphicalEditorPart; - - /** - * RootWrapper is a workaround: we can't set the input of the TreeView to its root - * element, so we introduce a fake parent. - */ - private final RootWrapper mRootWrapper = new RootWrapper(); - - /** - * Menu manager for the context menu actions. - * The actions delegate to the current GraphicalEditorPart. - */ - private MenuManager mMenuManager; - - private Composite mControl; - private PropertySheetPage mPropertySheet; - private PageSiteComposite mPropertySheetComposite; - private boolean mShowPropertySheet; - private boolean mShowHeader; - private boolean mIgnoreSelection; - private boolean mActive = true; - - /** Action to Select All in the tree */ - private final Action mTreeSelectAllAction = new Action() { - @Override - public void run() { - getTreeViewer().getTree().selectAll(); - OutlinePage.this.fireSelectionChanged(getSelection()); - } - - @Override - public String getId() { - return ActionFactory.SELECT_ALL.getId(); - } - }; - - /** Action for moving items up in the tree */ - private Action mMoveUpAction = new Action("Move Up\t-", - IconFactory.getInstance().getImageDescriptor("up")) { //$NON-NLS-1$ - - @Override - public String getId() { - return "adt.outline.moveup"; //$NON-NLS-1$ - } - - @Override - public boolean isEnabled() { - return canMove(false); - } - - @Override - public void run() { - move(false); - } - }; - - /** Action for moving items down in the tree */ - private Action mMoveDownAction = new Action("Move Down\t+", - IconFactory.getInstance().getImageDescriptor("down")) { //$NON-NLS-1$ - - @Override - public String getId() { - return "adt.outline.movedown"; //$NON-NLS-1$ - } - - @Override - public boolean isEnabled() { - return canMove(true); - } - - @Override - public void run() { - move(true); - } - }; - - /** - * Creates a new {@link OutlinePage} associated with the given editor - * - * @param graphicalEditorPart the editor associated with this outline - */ - public OutlinePage(GraphicalEditorPart graphicalEditorPart) { - super(); - mGraphicalEditorPart = graphicalEditorPart; - } - - @Override - public Control getControl() { - // We've injected some controls between the root of the outline page - // and the tree control, so return the actual root (a sash form) rather - // than the superclass' implementation which returns the tree. If we don't - // do this, various checks in the outline page which checks that getControl().getParent() - // is the outline window itself will ignore this page. - return mControl; - } - - void setActive(boolean active) { - if (active != mActive) { - mActive = active; - - // Outlines are by default active when they are created; this is intended - // for deactivating a hidden outline and later reactivating it - assert mControl != null; - if (active) { - getSite().getPage().addSelectionListener(this); - setModel(mGraphicalEditorPart.getCanvasControl().getViewHierarchy().getRoot()); - } else { - getSite().getPage().removeSelectionListener(this); - mRootWrapper.setRoot(null); - if (mPropertySheet != null) { - mPropertySheet.selectionChanged(null, TreeSelection.EMPTY); - } - } - } - } - - /** Refresh all the icon state */ - public void refreshIcons() { - TreeViewer treeViewer = getTreeViewer(); - if (treeViewer != null) { - Tree tree = treeViewer.getTree(); - if (tree != null && !tree.isDisposed()) { - treeViewer.refresh(); - } - } - } - - /** - * Set whether the outline should be shown in the header - * - * @param show whether a header should be shown - */ - public void setShowHeader(boolean show) { - mShowHeader = show; - } - - /** - * Set whether the property sheet should be shown within this outline - * - * @param show whether the property sheet should show - */ - public void setShowPropertySheet(boolean show) { - if (show != mShowPropertySheet) { - mShowPropertySheet = show; - if (mControl == null) { - return; - } - - if (show && mPropertySheet == null) { - createPropertySheet(); - } else if (!show) { - mPropertySheetComposite.dispose(); - mPropertySheetComposite = null; - mPropertySheet.dispose(); - mPropertySheet = null; - } - - mControl.layout(); - } - } - - @Override - public void createControl(Composite parent) { - mControl = new SelfOrientingSashForm(parent, SWT.VERTICAL); - - if (mShowHeader) { - PageSiteComposite mOutlineComposite = new PageSiteComposite(mControl, SWT.BORDER); - mOutlineComposite.setTitleText("Outline"); - mOutlineComposite.setTitleImage(IconFactory.getInstance().getIcon("components_view")); - mOutlineComposite.setPage(new IPage() { - @Override - public void createControl(Composite outlineParent) { - createOutline(outlineParent); - } - - @Override - public void dispose() { - } - - @Override - public Control getControl() { - return getTreeViewer().getTree(); - } - - @Override - public void setToolBar(IToolBarManager toolBarManager) { - makeContributions(null, toolBarManager, null); - toolBarManager.update(false); - } - - @Override - public void setFocus() { - getControl().setFocus(); - } - }); - } else { - createOutline(mControl); - } - - if (mShowPropertySheet) { - createPropertySheet(); - } - } - - private void createOutline(Composite parent) { - if (AdtUtils.isEclipse4()) { - // This is a workaround for the focus behavior in Eclipse 4 where - // the framework ends up calling setFocus() on the first widget in the outline - // AFTER a mouse click has been received. Specifically, if the user clicks in - // the embedded property sheet to for example give a Text property editor focus, - // then after the mouse click, the Outline window activation event is processed, - // and this event causes setFocus() to be called first on the PageBookView (which - // ends up calling setFocus on the first control, normally the TreeViewer), and - // then on the Page itself. We're dealing with the page setFocus() in the override - // of that method in the class, such that it does nothing. - // However, we have to also disable the setFocus on the first control in the - // outline page. To deal with that, we create our *own* first control in the - // outline, and make its setFocus() a no-op. We also make it invisible, since we - // don't actually want anything but the tree viewer showing in the outline. - Text text = new Text(parent, SWT.NONE) { - @Override - public boolean setFocus() { - // Focus no-op - return true; - } - - @Override - protected void checkSubclass() { - // Disable the check that prevents subclassing of SWT components - } - }; - text.setVisible(false); - } - - super.createControl(parent); - - TreeViewer tv = getTreeViewer(); - tv.setAutoExpandLevel(2); - tv.setContentProvider(new ContentProvider()); - tv.setLabelProvider(new LabelProvider()); - tv.setInput(mRootWrapper); - tv.expandToLevel(mRootWrapper.getRoot(), 2); - - int supportedOperations = DND.DROP_COPY | DND.DROP_MOVE; - Transfer[] transfers = new Transfer[] { - SimpleXmlTransfer.getInstance() - }; - - tv.addDropSupport(supportedOperations, transfers, new OutlineDropListener(this, tv)); - tv.addDragSupport(supportedOperations, transfers, new OutlineDragListener(this, tv)); - - // The tree viewer will hold CanvasViewInfo instances, however these - // change each time the canvas is reloaded. OTOH layoutlib gives us - // constant UiView keys which we can use to perform tree item comparisons. - tv.setComparer(new IElementComparer() { - @Override - public int hashCode(Object element) { - if (element instanceof CanvasViewInfo) { - UiViewElementNode key = ((CanvasViewInfo) element).getUiViewNode(); - if (key != null) { - return key.hashCode(); - } - } - if (element != null) { - return element.hashCode(); - } - return 0; - } - - @Override - public boolean equals(Object a, Object b) { - if (a instanceof CanvasViewInfo && b instanceof CanvasViewInfo) { - UiViewElementNode keyA = ((CanvasViewInfo) a).getUiViewNode(); - UiViewElementNode keyB = ((CanvasViewInfo) b).getUiViewNode(); - if (keyA != null) { - return keyA.equals(keyB); - } - } - if (a != null) { - return a.equals(b); - } - return false; - } - }); - tv.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - // This used to open the property view, but now that properties are docked - // let's use it for something else -- such as showing the editor source - /* - // Front properties panel; its selection is already linked - IWorkbenchPage page = getSite().getPage(); - try { - page.showView(IPageLayout.ID_PROP_SHEET, null, IWorkbenchPage.VIEW_ACTIVATE); - } catch (PartInitException e) { - AdtPlugin.log(e, "Could not activate property sheet"); - } - */ - - TreeItem[] selection = getTreeViewer().getTree().getSelection(); - if (selection.length > 0) { - CanvasViewInfo vi = getViewInfo(selection[0].getData()); - if (vi != null) { - LayoutCanvas canvas = mGraphicalEditorPart.getCanvasControl(); - canvas.show(vi); - } - } - } - }); - - setupContextMenu(); - - // Listen to selection changes from the layout editor - getSite().getPage().addSelectionListener(this); - getControl().addDisposeListener(new DisposeListener() { - - @Override - public void widgetDisposed(DisposeEvent e) { - dispose(); - } - }); - - Tree tree = tv.getTree(); - tree.addKeyListener(new KeyListener() { - - @Override - public void keyPressed(KeyEvent e) { - if (e.character == '-') { - if (mMoveUpAction.isEnabled()) { - mMoveUpAction.run(); - } - } else if (e.character == '+') { - if (mMoveDownAction.isEnabled()) { - mMoveDownAction.run(); - } - } - } - - @Override - public void keyReleased(KeyEvent e) { - } - }); - - setupTooltip(); - } - - /** - * This flag is true when the mouse button is being pressed somewhere inside - * the property sheet - */ - private boolean mPressInPropSheet; - - private void createPropertySheet() { - mPropertySheetComposite = new PageSiteComposite(mControl, SWT.BORDER); - mPropertySheetComposite.setTitleText("Properties"); - mPropertySheetComposite.setTitleImage(IconFactory.getInstance().getIcon("properties_view")); - mPropertySheet = new PropertySheetPage(mGraphicalEditorPart); - mPropertySheetComposite.setPage(mPropertySheet); - if (AdtUtils.isEclipse4()) { - mPropertySheet.getControl().addMouseListener(new MouseListener() { - @Override - public void mouseDown(MouseEvent e) { - mPressInPropSheet = true; - } - - @Override - public void mouseUp(MouseEvent e) { - mPressInPropSheet = false; - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - }); - } - } - - @Override - public void setFocus() { - // Only call setFocus on the tree viewer if the mouse click isn't in the property - // sheet area - if (!mPressInPropSheet) { - super.setFocus(); - } - } - - @Override - public void dispose() { - mRootWrapper.setRoot(null); - - getSite().getPage().removeSelectionListener(this); - super.dispose(); - if (mPropertySheet != null) { - mPropertySheet.dispose(); - mPropertySheet = null; - } - } - - /** - * Invoked by {@link LayoutCanvas} to set the model (a.k.a. the root view info). - * - * @param rootViewInfo The root of the view info hierarchy. Can be null. - */ - public void setModel(CanvasViewInfo rootViewInfo) { - if (!mActive) { - return; - } - - mRootWrapper.setRoot(rootViewInfo); - - TreeViewer tv = getTreeViewer(); - if (tv != null && !tv.getTree().isDisposed()) { - Object[] expanded = tv.getExpandedElements(); - tv.refresh(); - tv.setExpandedElements(expanded); - // Ensure that the root is expanded - tv.expandToLevel(rootViewInfo, 2); - } - } - - /** - * Returns the current tree viewer selection. Shouldn't be null, - * although it can be {@link TreeSelection#EMPTY}. - */ - @Override - public ISelection getSelection() { - return super.getSelection(); - } - - /** - * Sets the outline selection. - * - * @param selection Only {@link ITreeSelection} will be used, otherwise the - * selection will be cleared (including a null selection). - */ - @Override - public void setSelection(ISelection selection) { - // TreeViewer should be able to deal with a null selection, but let's make it safe - if (selection == null) { - selection = TreeSelection.EMPTY; - } - if (selection.equals(TreeSelection.EMPTY)) { - return; - } - - super.setSelection(selection); - - TreeViewer tv = getTreeViewer(); - if (tv == null || !(selection instanceof ITreeSelection) || selection.isEmpty()) { - return; - } - - // auto-reveal the selection - ITreeSelection treeSel = (ITreeSelection) selection; - for (TreePath p : treeSel.getPaths()) { - tv.expandToLevel(p, 1); - } - } - - @Override - protected void fireSelectionChanged(ISelection selection) { - super.fireSelectionChanged(selection); - if (mPropertySheet != null && !mIgnoreSelection) { - mPropertySheet.selectionChanged(null, selection); - } - } - - /** - * Listens to a workbench selection. - * Only listen on selection coming from {@link LayoutEditorDelegate}, which avoid - * picking up our own selections. - */ - @Override - public void selectionChanged(IWorkbenchPart part, ISelection selection) { - if (mIgnoreSelection) { - return; - } - - if (part instanceof IEditorPart) { - LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor((IEditorPart) part); - if (delegate != null) { - try { - mIgnoreSelection = true; - setSelection(selection); - - if (mPropertySheet != null) { - mPropertySheet.selectionChanged(part, selection); - } - } finally { - mIgnoreSelection = false; - } - } - } - } - - @Override - public void selectionChanged(SelectionChangedEvent event) { - if (!mIgnoreSelection) { - super.selectionChanged(event); - } - } - - // ---- - - /** - * In theory, the root of the model should be the input of the {@link TreeViewer}, - * which would be the root {@link CanvasViewInfo}. - * That means in theory {@link ContentProvider#getElements(Object)} should return - * its own input as the single root node. - * <p/> - * However as described in JFace Bug 9262, this case is not properly handled by - * a {@link TreeViewer} and leads to an infinite recursion in the tree viewer. - * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=9262 - * <p/> - * The solution is to wrap the tree viewer input in a dummy root node that acts - * as a parent. This class does just that. - */ - private static class RootWrapper { - private CanvasViewInfo mRoot; - - public void setRoot(CanvasViewInfo root) { - mRoot = root; - } - - public CanvasViewInfo getRoot() { - return mRoot; - } - } - - /** Return the {@link CanvasViewInfo} associated with the given TreeItem's data field */ - /* package */ static CanvasViewInfo getViewInfo(Object viewData) { - if (viewData instanceof RootWrapper) { - return ((RootWrapper) viewData).getRoot(); - } - if (viewData instanceof CanvasViewInfo) { - return (CanvasViewInfo) viewData; - } - return null; - } - - // --- Content and Label Providers --- - - /** - * Content provider for the Outline model. - * Objects are going to be {@link CanvasViewInfo}. - */ - private static class ContentProvider implements ITreeContentProvider { - - @Override - public Object[] getChildren(Object element) { - if (element instanceof RootWrapper) { - CanvasViewInfo root = ((RootWrapper)element).getRoot(); - if (root != null) { - return new Object[] { root }; - } - } - if (element instanceof CanvasViewInfo) { - List<CanvasViewInfo> children = ((CanvasViewInfo) element).getUniqueChildren(); - if (children != null) { - return children.toArray(); - } - } - return new Object[0]; - } - - @Override - public Object getParent(Object element) { - if (element instanceof CanvasViewInfo) { - return ((CanvasViewInfo) element).getParent(); - } - return null; - } - - @Override - public boolean hasChildren(Object element) { - if (element instanceof CanvasViewInfo) { - List<CanvasViewInfo> children = ((CanvasViewInfo) element).getChildren(); - if (children != null) { - return children.size() > 0; - } - } - return false; - } - - /** - * Returns the root element. - * Semantically, the root element is the single top-level XML element of the XML layout. - */ - @Override - public Object[] getElements(Object inputElement) { - return getChildren(inputElement); - } - - @Override - public void dispose() { - // pass - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - } - - /** - * Label provider for the Outline model. - * Objects are going to be {@link CanvasViewInfo}. - */ - private class LabelProvider extends StyledCellLabelProvider { - /** - * Returns the element's logo with a fallback on the android logo. - * - * @param element the tree element - * @return the image to be used as a logo - */ - public Image getImage(Object element) { - if (element instanceof CanvasViewInfo) { - element = ((CanvasViewInfo) element).getUiViewNode(); - } - - if (element instanceof UiViewElementNode) { - UiViewElementNode v = (UiViewElementNode) element; - return v.getIcon(); - } - - return AdtPlugin.getAndroidLogo(); - } - - /** - * Uses {@link UiElementNode#getStyledDescription} for the label for this tree item. - */ - @Override - public void update(ViewerCell cell) { - Object element = cell.getElement(); - StyledString styledString = null; - - CanvasViewInfo vi = null; - if (element instanceof CanvasViewInfo) { - vi = (CanvasViewInfo) element; - element = vi.getUiViewNode(); - } - - Image image = getImage(element); - - if (element instanceof UiElementNode) { - UiElementNode node = (UiElementNode) element; - styledString = node.getStyledDescription(); - Node xmlNode = node.getXmlNode(); - if (xmlNode instanceof Element) { - Element e = (Element) xmlNode; - - // Temporary diagnostics code when developing GridLayout - if (GridLayoutRule.sDebugGridLayout) { - - String namespace; - if (e.getNodeName().equals(GRID_LAYOUT) || - e.getParentNode() != null - && e.getParentNode().getNodeName().equals(GRID_LAYOUT)) { - namespace = ANDROID_URI; - } else { - // Else: probably a v7 gridlayout - IProject project = mGraphicalEditorPart.getProject(); - ProjectState projectState = Sdk.getProjectState(project); - if (projectState != null && projectState.isLibrary()) { - namespace = AUTO_URI; - } else { - ManifestInfo info = ManifestInfo.get(project); - namespace = URI_PREFIX + info.getPackage(); - } - } - - if (e.getNodeName() != null && e.getNodeName().endsWith(GRID_LAYOUT)) { - // Attach rowCount/columnCount info - String rowCount = e.getAttributeNS(namespace, ATTR_ROW_COUNT); - if (rowCount.length() == 0) { - rowCount = "?"; - } - String columnCount = e.getAttributeNS(namespace, ATTR_COLUMN_COUNT); - if (columnCount.length() == 0) { - columnCount = "?"; - } - - styledString.append(" - columnCount=", QUALIFIER_STYLER); - styledString.append(columnCount, QUALIFIER_STYLER); - styledString.append(", rowCount=", QUALIFIER_STYLER); - styledString.append(rowCount, QUALIFIER_STYLER); - } else if (e.getParentNode() != null - && e.getParentNode().getNodeName() != null - && e.getParentNode().getNodeName().endsWith(GRID_LAYOUT)) { - // Attach row/column info - String row = e.getAttributeNS(namespace, ATTR_LAYOUT_ROW); - if (row.length() == 0) { - row = "?"; - } - Styler colStyle = QUALIFIER_STYLER; - String column = e.getAttributeNS(namespace, ATTR_LAYOUT_COLUMN); - if (column.length() == 0) { - column = "?"; - } else { - String colCount = ((Element) e.getParentNode()).getAttributeNS( - namespace, ATTR_COLUMN_COUNT); - if (colCount.length() > 0 && Integer.parseInt(colCount) <= - Integer.parseInt(column)) { - colStyle = StyledString.createColorRegistryStyler( - JFacePreferences.ERROR_COLOR, null); - } - } - String rowSpan = e.getAttributeNS(namespace, ATTR_LAYOUT_ROW_SPAN); - String columnSpan = e.getAttributeNS(namespace, - ATTR_LAYOUT_COLUMN_SPAN); - if (rowSpan.length() == 0) { - rowSpan = "1"; - } - if (columnSpan.length() == 0) { - columnSpan = "1"; - } - - styledString.append(" - cell (row=", QUALIFIER_STYLER); - styledString.append(row, QUALIFIER_STYLER); - styledString.append(',', QUALIFIER_STYLER); - styledString.append("col=", colStyle); - styledString.append(column, colStyle); - styledString.append(')', colStyle); - styledString.append(", span=(", QUALIFIER_STYLER); - styledString.append(columnSpan, QUALIFIER_STYLER); - styledString.append(',', QUALIFIER_STYLER); - styledString.append(rowSpan, QUALIFIER_STYLER); - styledString.append(')', QUALIFIER_STYLER); - - String gravity = e.getAttributeNS(namespace, ATTR_LAYOUT_GRAVITY); - if (gravity != null && gravity.length() > 0) { - styledString.append(" : ", COUNTER_STYLER); - styledString.append(gravity, COUNTER_STYLER); - } - - } - } - - if (e.hasAttributeNS(ANDROID_URI, ATTR_TEXT)) { - // Show the text attribute - String text = e.getAttributeNS(ANDROID_URI, ATTR_TEXT); - if (text != null && text.length() > 0 - && !text.contains(node.getDescriptor().getUiName())) { - if (text.charAt(0) == '@') { - String resolved = mGraphicalEditorPart.findString(text); - if (resolved != null) { - text = resolved; - } - } - if (styledString.length() < LABEL_MAX_WIDTH - LABEL_SEPARATOR.length() - - 2) { - styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER); - - styledString.append('"', QUALIFIER_STYLER); - styledString.append(truncate(text, styledString), QUALIFIER_STYLER); - styledString.append('"', QUALIFIER_STYLER); - } - } - } else if (e.hasAttributeNS(ANDROID_URI, ATTR_SRC)) { - // Show ImageView source attributes etc - String src = e.getAttributeNS(ANDROID_URI, ATTR_SRC); - if (src != null && src.length() > 0) { - if (src.startsWith(DRAWABLE_PREFIX)) { - src = src.substring(DRAWABLE_PREFIX.length()); - } - styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER); - styledString.append(truncate(src, styledString), QUALIFIER_STYLER); - } - } else if (e.getTagName().equals(SdkConstants.VIEW_INCLUDE)) { - // Show the include reference. - - // Note: the layout attribute is NOT in the Android namespace - String src = e.getAttribute(SdkConstants.ATTR_LAYOUT); - if (src != null && src.length() > 0) { - if (src.startsWith(LAYOUT_RESOURCE_PREFIX)) { - src = src.substring(LAYOUT_RESOURCE_PREFIX.length()); - } - styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER); - styledString.append(truncate(src, styledString), QUALIFIER_STYLER); - } - } - } - } else if (element == null && vi != null) { - // It's an inclusion-context: display it - Reference includedWithin = mGraphicalEditorPart.getIncludedWithin(); - if (includedWithin != null) { - styledString = new StyledString(); - styledString.append(includedWithin.getDisplayName(), QUALIFIER_STYLER); - image = IconFactory.getInstance().getIcon(SdkConstants.VIEW_INCLUDE); - } - } - - if (styledString == null) { - styledString = new StyledString(); - styledString.append(element == null ? "(null)" : element.toString()); - } - - cell.setText(styledString.toString()); - cell.setStyleRanges(styledString.getStyleRanges()); - cell.setImage(image); - super.update(cell); - } - - @Override - public boolean isLabelProperty(Object element, String property) { - return super.isLabelProperty(element, property); - } - } - - // --- Context Menu --- - - /** - * This viewer uses its own actions that delegate to the ones given - * by the {@link LayoutCanvas}. All the processing is actually handled - * directly by the canvas and this viewer only gets refreshed as a - * consequence of the canvas changing the XML model. - */ - private void setupContextMenu() { - - mMenuManager = new MenuManager(); - mMenuManager.removeAll(); - - mMenuManager.add(mMoveUpAction); - mMenuManager.add(mMoveDownAction); - mMenuManager.add(new Separator()); - - mMenuManager.add(new SelectionManager.SelectionMenu(mGraphicalEditorPart)); - mMenuManager.add(new Separator()); - final String prefix = LayoutCanvas.PREFIX_CANVAS_ACTION; - mMenuManager.add(new DelegateAction(prefix + ActionFactory.CUT.getId())); - mMenuManager.add(new DelegateAction(prefix + ActionFactory.COPY.getId())); - mMenuManager.add(new DelegateAction(prefix + ActionFactory.PASTE.getId())); - - mMenuManager.add(new Separator()); - - mMenuManager.add(new DelegateAction(prefix + ActionFactory.DELETE.getId())); - - mMenuManager.addMenuListener(new IMenuListener() { - @Override - public void menuAboutToShow(IMenuManager manager) { - // Update all actions to match their LayoutCanvas counterparts - for (IContributionItem contrib : manager.getItems()) { - if (contrib instanceof ActionContributionItem) { - IAction action = ((ActionContributionItem) contrib).getAction(); - if (action instanceof DelegateAction) { - ((DelegateAction) action).updateFromEditorPart(mGraphicalEditorPart); - } - } - } - } - }); - - new DynamicContextMenu( - mGraphicalEditorPart.getEditorDelegate(), - mGraphicalEditorPart.getCanvasControl(), - mMenuManager); - - getTreeViewer().getTree().setMenu(mMenuManager.createContextMenu(getControl())); - - // Update Move Up/Move Down state only when the menu is opened - getTreeViewer().getTree().addMenuDetectListener(new MenuDetectListener() { - @Override - public void menuDetected(MenuDetectEvent e) { - mMenuManager.update(IAction.ENABLED); - } - }); - } - - /** - * An action that delegates its properties and behavior to a target action. - * The target action can be null or it can change overtime, typically as the - * layout canvas' editor part is activated or closed. - */ - private static class DelegateAction extends Action { - private IAction mTargetAction; - private final String mCanvasActionId; - - public DelegateAction(String canvasActionId) { - super(canvasActionId); - setId(canvasActionId); - mCanvasActionId = canvasActionId; - } - - // --- Methods form IAction --- - - /** Returns the target action's {@link #isEnabled()} if defined, or false. */ - @Override - public boolean isEnabled() { - return mTargetAction == null ? false : mTargetAction.isEnabled(); - } - - /** Returns the target action's {@link #isChecked()} if defined, or false. */ - @Override - public boolean isChecked() { - return mTargetAction == null ? false : mTargetAction.isChecked(); - } - - /** Returns the target action's {@link #isHandled()} if defined, or false. */ - @Override - public boolean isHandled() { - return mTargetAction == null ? false : mTargetAction.isHandled(); - } - - /** Runs the target action if defined. */ - @Override - public void run() { - if (mTargetAction != null) { - mTargetAction.run(); - } - super.run(); - } - - /** - * Updates this action to delegate to its counterpart in the given editor part - * - * @param editorPart The editor being updated - */ - public void updateFromEditorPart(GraphicalEditorPart editorPart) { - LayoutCanvas canvas = editorPart == null ? null : editorPart.getCanvasControl(); - if (canvas == null) { - mTargetAction = null; - } else { - mTargetAction = canvas.getAction(mCanvasActionId); - } - - if (mTargetAction != null) { - setText(mTargetAction.getText()); - setId(mTargetAction.getId()); - setDescription(mTargetAction.getDescription()); - setImageDescriptor(mTargetAction.getImageDescriptor()); - setHoverImageDescriptor(mTargetAction.getHoverImageDescriptor()); - setDisabledImageDescriptor(mTargetAction.getDisabledImageDescriptor()); - setToolTipText(mTargetAction.getToolTipText()); - setActionDefinitionId(mTargetAction.getActionDefinitionId()); - setHelpListener(mTargetAction.getHelpListener()); - setAccelerator(mTargetAction.getAccelerator()); - setChecked(mTargetAction.isChecked()); - setEnabled(mTargetAction.isEnabled()); - } else { - setEnabled(false); - } - } - } - - /** Returns the associated editor with this outline */ - /* package */GraphicalEditorPart getEditor() { - return mGraphicalEditorPart; - } - - @Override - public void setActionBars(IActionBars actionBars) { - super.setActionBars(actionBars); - - // Map Outline actions to canvas actions such that they share Undo context etc - LayoutCanvas canvas = mGraphicalEditorPart.getCanvasControl(); - canvas.updateGlobalActions(actionBars); - - // Special handling for Select All since it's different than the canvas (will - // include selecting the root etc) - actionBars.setGlobalActionHandler(mTreeSelectAllAction.getId(), mTreeSelectAllAction); - actionBars.updateActionBars(); - } - - // ---- Move Up/Down Support ---- - - /** Returns true if the current selected item can be moved */ - private boolean canMove(boolean forward) { - CanvasViewInfo viewInfo = getSingleSelectedItem(); - if (viewInfo != null) { - UiViewElementNode node = viewInfo.getUiViewNode(); - if (forward) { - return findNext(node) != null; - } else { - return findPrevious(node) != null; - } - } - - return false; - } - - /** Moves the current selected item down (forward) or up (not forward) */ - private void move(boolean forward) { - CanvasViewInfo viewInfo = getSingleSelectedItem(); - if (viewInfo != null) { - final Pair<UiViewElementNode, Integer> target; - UiViewElementNode selected = viewInfo.getUiViewNode(); - if (forward) { - target = findNext(selected); - } else { - target = findPrevious(selected); - } - if (target != null) { - final LayoutCanvas canvas = mGraphicalEditorPart.getCanvasControl(); - final SelectionManager selectionManager = canvas.getSelectionManager(); - final ArrayList<SelectionItem> dragSelection = new ArrayList<SelectionItem>(); - dragSelection.add(selectionManager.createSelection(viewInfo)); - SelectionManager.sanitize(dragSelection); - - if (!dragSelection.isEmpty()) { - final SimpleElement[] elements = SelectionItem.getAsElements(dragSelection); - UiViewElementNode parentNode = target.getFirst(); - final NodeProxy targetNode = canvas.getNodeFactory().create(parentNode); - - // Record children of the target right before the drop (such that we - // can find out after the drop which exact children were inserted) - Set<INode> children = new HashSet<INode>(); - for (INode node : targetNode.getChildren()) { - children.add(node); - } - - String label = MoveGesture.computeUndoLabel(targetNode, - elements, DND.DROP_MOVE); - canvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(label, new Runnable() { - @Override - public void run() { - InsertType insertType = InsertType.MOVE_INTO; - if (dragSelection.get(0).getNode().getParent() == targetNode) { - insertType = InsertType.MOVE_WITHIN; - } - canvas.getRulesEngine().setInsertType(insertType); - int index = target.getSecond(); - BaseLayoutRule.insertAt(targetNode, elements, false, index); - targetNode.applyPendingChanges(); - canvas.getClipboardSupport().deleteSelection("Remove", dragSelection); - } - }); - - // Now find out which nodes were added, and look up their - // corresponding CanvasViewInfos - final List<INode> added = new ArrayList<INode>(); - for (INode node : targetNode.getChildren()) { - if (!children.contains(node)) { - added.add(node); - } - } - - selectionManager.setOutlineSelection(added); - } - } - } - } - - /** - * Returns the {@link CanvasViewInfo} for the currently selected item, or null if - * there are no or multiple selected items - * - * @return the current selected item if there is exactly one item selected - */ - private CanvasViewInfo getSingleSelectedItem() { - TreeItem[] selection = getTreeViewer().getTree().getSelection(); - if (selection.length == 1) { - return getViewInfo(selection[0].getData()); - } - - return null; - } - - - /** Returns the pair [parent,index] of the next node (when iterating forward) */ - @VisibleForTesting - /* package */ static Pair<UiViewElementNode, Integer> findNext(UiViewElementNode node) { - UiElementNode parent = node.getUiParent(); - if (parent == null) { - return null; - } - - UiElementNode next = node.getUiNextSibling(); - if (next != null) { - if (DescriptorsUtils.canInsertChildren(next.getDescriptor(), null)) { - return getFirstPosition(next); - } else { - return getPositionAfter(next); - } - } - - next = parent.getUiNextSibling(); - if (next != null) { - return getPositionBefore(next); - } else { - UiElementNode grandParent = parent.getUiParent(); - if (grandParent != null) { - return getLastPosition(grandParent); - } - } - - return null; - } - - /** Returns the pair [parent,index] of the previous node (when iterating backward) */ - @VisibleForTesting - /* package */ static Pair<UiViewElementNode, Integer> findPrevious(UiViewElementNode node) { - UiElementNode prev = node.getUiPreviousSibling(); - if (prev != null) { - UiElementNode curr = prev; - while (true) { - List<UiElementNode> children = curr.getUiChildren(); - if (children.size() > 0) { - curr = children.get(children.size() - 1); - continue; - } - if (DescriptorsUtils.canInsertChildren(curr.getDescriptor(), null)) { - return getFirstPosition(curr); - } else { - if (curr == prev) { - return getPositionBefore(curr); - } else { - return getPositionAfter(curr); - } - } - } - } - - return getPositionBefore(node.getUiParent()); - } - - /** Returns the pair [parent,index] of the position immediately before the given node */ - private static Pair<UiViewElementNode, Integer> getPositionBefore(UiElementNode node) { - if (node != null) { - UiElementNode parent = node.getUiParent(); - if (parent != null && parent instanceof UiViewElementNode) { - return Pair.of((UiViewElementNode) parent, node.getUiSiblingIndex()); - } - } - - return null; - } - - /** Returns the pair [parent,index] of the position immediately following the given node */ - private static Pair<UiViewElementNode, Integer> getPositionAfter(UiElementNode node) { - if (node != null) { - UiElementNode parent = node.getUiParent(); - if (parent != null && parent instanceof UiViewElementNode) { - return Pair.of((UiViewElementNode) parent, node.getUiSiblingIndex() + 1); - } - } - - return null; - } - - /** Returns the pair [parent,index] of the first position inside the given parent */ - private static Pair<UiViewElementNode, Integer> getFirstPosition(UiElementNode parent) { - if (parent != null && parent instanceof UiViewElementNode) { - return Pair.of((UiViewElementNode) parent, 0); - } - - return null; - } - - /** - * Returns the pair [parent,index] of the last position after the given node's - * children - */ - private static Pair<UiViewElementNode, Integer> getLastPosition(UiElementNode parent) { - if (parent != null && parent instanceof UiViewElementNode) { - return Pair.of((UiViewElementNode) parent, parent.getUiChildren().size()); - } - - return null; - } - - /** - * Truncates the given text such that it will fit into the given {@link StyledString} - * up to a maximum length of {@link #LABEL_MAX_WIDTH}. - * - * @param text the text to truncate - * @param string the existing string to be appended to - * @return the truncated string - */ - private static String truncate(String text, StyledString string) { - int existingLength = string.length(); - - if (text.length() + existingLength > LABEL_MAX_WIDTH) { - int truncatedLength = LABEL_MAX_WIDTH - existingLength - 3; - if (truncatedLength > 0) { - return String.format("%1$s...", text.substring(0, truncatedLength)); - } else { - return ""; //$NON-NLS-1$ - } - } - - return text; - } - - @Override - public void setToolBar(IToolBarManager toolBarManager) { - makeContributions(null, toolBarManager, null); - toolBarManager.update(false); - } - - /** - * Sets up a custom tooltip when hovering over tree items. It currently displays the error - * message for the lint warning associated with each node, if any (and only if the hover - * is over the icon portion). - */ - private void setupTooltip() { - final Tree tree = getTreeViewer().getTree(); - - // This is based on SWT Snippet 125 - final Listener listener = new Listener() { - Shell mTip = null; - Label mLabel = null; - - @Override - public void handleEvent(Event event) { - switch(event.type) { - case SWT.Dispose: - case SWT.KeyDown: - case SWT.MouseExit: - case SWT.MouseDown: - case SWT.MouseMove: - if (mTip != null) { - mTip.dispose(); - mTip = null; - mLabel = null; - } - break; - case SWT.MouseHover: - if (mTip != null) { - mTip.dispose(); - mTip = null; - mLabel = null; - } - - String tooltip = null; - - TreeItem item = tree.getItem(new Point(event.x, event.y)); - if (item != null) { - Rectangle rect = item.getBounds(0); - if (event.x - rect.x > 16) { // 16: Standard width of our outline icons - return; - } - - Object data = item.getData(); - if (data != null && data instanceof CanvasViewInfo) { - LayoutEditorDelegate editor = mGraphicalEditorPart.getEditorDelegate(); - CanvasViewInfo vi = (CanvasViewInfo) data; - IMarker marker = editor.getIssueForNode(vi.getUiViewNode()); - if (marker != null) { - tooltip = marker.getAttribute(IMarker.MESSAGE, null); - } - } - - if (tooltip != null) { - Shell shell = tree.getShell(); - Display display = tree.getDisplay(); - - Color fg = display.getSystemColor(SWT.COLOR_INFO_FOREGROUND); - Color bg = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND); - mTip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL); - mTip.setBackground(bg); - FillLayout layout = new FillLayout(); - layout.marginWidth = 1; - layout.marginHeight = 1; - mTip.setLayout(layout); - mLabel = new Label(mTip, SWT.WRAP); - mLabel.setForeground(fg); - mLabel.setBackground(bg); - mLabel.setText(tooltip); - mLabel.addListener(SWT.MouseExit, this); - mLabel.addListener(SWT.MouseDown, this); - - Point pt = tree.toDisplay(rect.x, rect.y + rect.height); - Rectangle displayBounds = display.getBounds(); - // -10: Don't extend -all- the way to the edge of the screen - // which would make it look like it has been cropped - int availableWidth = displayBounds.x + displayBounds.width - pt.x - 10; - if (availableWidth < 80) { - availableWidth = 80; - } - Point size = mTip.computeSize(SWT.DEFAULT, SWT.DEFAULT); - if (size.x > availableWidth) { - size = mTip.computeSize(availableWidth, SWT.DEFAULT); - } - mTip.setBounds(pt.x, pt.y, size.x, size.y); - - mTip.setVisible(true); - } - } - } - } - }; - - tree.addListener(SWT.Dispose, listener); - tree.addListener(SWT.KeyDown, listener); - tree.addListener(SWT.MouseMove, listener); - tree.addListener(SWT.MouseHover, listener); - } -} |