aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java396
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/BinPacker.java352
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasAlternateSelection.java73
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java215
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java1178
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java429
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java195
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CreateNewConfigJob.java132
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java395
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java203
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java915
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DropGesture.java87
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java654
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/EmptyViewsOverlay.java96
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExportScreenshotAction.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java304
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java645
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Gesture.java156
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java930
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java217
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java182
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java2937
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java187
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java241
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java447
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java979
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java1111
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlay.java150
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java732
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java1720
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvasViewer.java165
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java413
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPoint.java156
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java394
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintOverlay.java140
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltip.java94
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltipManager.java181
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java220
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MarqueeGesture.java160
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java852
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDragListener.java129
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java217
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineOverlay.java107
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java1439
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Overlay.java91
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java1265
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java247
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java642
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLogger.java327
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java1333
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewList.java222
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java1696
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewMode.java43
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderService.java668
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java279
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandle.java141
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandles.java140
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java252
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java1262
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java247
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ShowWithinMenu.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java124
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java370
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleXmlTransfer.java154
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SubmenuAction.java75
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java319
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java457
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java771
68 files changed, 0 insertions, 32214 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java
deleted file mode 100644
index b3dce0756..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/AccordionControl.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.jface.resource.JFaceResources;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.layout.RowLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.ScrollBar;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The accordion control allows a series of labels with associated content that can be
- * shown. For more details on accordions, see http://en.wikipedia.org/wiki/Accordion_(GUI)
- * <p>
- * This control allows the children to be created lazily. You can also customize the
- * composite which is created to hold the children items, to for example allow multiple
- * columns of items rather than just the default vertical stack.
- * <p>
- * The visual appearance of the headers is built in; it uses a mild gradient, with a
- * heavier gradient during mouse-overs. It also uses a bold label along with the eclipse
- * folder icons.
- * <p>
- * The control can be configured to enforce a single category open at any time (the
- * default), or allowing multiple categories open (where they share the available space).
- * The control can also be configured to fill the available vertical space for the open
- * category/categories.
- */
-public abstract class AccordionControl extends Composite {
- /** Pixel spacing between header items */
- private static final int HEADER_SPACING = 0;
-
- /** Pixel spacing between items in the content area */
- private static final int ITEM_SPACING = 0;
-
- private static final String KEY_CONTENT = "content"; //$NON-NLS-1$
- private static final String KEY_HEADER = "header"; //$NON-NLS-1$
-
- private Image mClosed;
- private Image mOpen;
- private boolean mSingle = true;
- private boolean mWrap;
-
- /**
- * Creates the container which will hold the items in a category; this can be
- * overridden to lay out the children with a different layout than the default
- * vertical RowLayout
- */
- protected Composite createChildContainer(Composite parent, Object header, int style) {
- Composite composite = new Composite(parent, style);
- if (mWrap) {
- RowLayout layout = new RowLayout(SWT.HORIZONTAL);
- layout.center = true;
- composite.setLayout(layout);
- } else {
- RowLayout layout = new RowLayout(SWT.VERTICAL);
- layout.spacing = ITEM_SPACING;
- layout.marginHeight = 0;
- layout.marginWidth = 0;
- layout.marginLeft = 0;
- layout.marginTop = 0;
- layout.marginRight = 0;
- layout.marginBottom = 0;
- composite.setLayout(layout);
- }
-
- // TODO - maybe do multi-column arrangement for simple nodes
- return composite;
- }
-
- /**
- * Creates the children under a particular header
- *
- * @param parent the parent composite to add the SWT items to
- * @param header the header object that is being opened for the first time
- */
- protected abstract void createChildren(Composite parent, Object header);
-
- /**
- * Set whether a single category should be enforced or not (default=true)
- *
- * @param single if true, enforce a single category open at a time
- */
- public void setAutoClose(boolean single) {
- mSingle = single;
- }
-
- /**
- * Returns whether a single category should be enforced or not (default=true)
- *
- * @return true if only a single category can be open at a time
- */
- public boolean isAutoClose() {
- return mSingle;
- }
-
- /**
- * Returns the labels used as header categories
- *
- * @return list of header labels
- */
- public List<CLabel> getHeaderLabels() {
- List<CLabel> headers = new ArrayList<CLabel>();
- for (Control c : getChildren()) {
- if (c instanceof CLabel) {
- headers.add((CLabel) c);
- }
- }
-
- return headers;
- }
-
- /**
- * Show all categories
- *
- * @param performLayout if true, call {@link #layout} and {@link #pack} when done
- */
- public void expandAll(boolean performLayout) {
- for (Control c : getChildren()) {
- if (c instanceof CLabel) {
- if (!isOpen(c)) {
- toggle((CLabel) c, false, false);
- }
- }
- }
- if (performLayout) {
- pack();
- layout();
- }
- }
-
- /**
- * Hide all categories
- *
- * @param performLayout if true, call {@link #layout} and {@link #pack} when done
- */
- public void collapseAll(boolean performLayout) {
- for (Control c : getChildren()) {
- if (c instanceof CLabel) {
- if (isOpen(c)) {
- toggle((CLabel) c, false, false);
- }
- }
- }
- if (performLayout) {
- layout();
- }
- }
-
- /**
- * Create the composite.
- *
- * @param parent the parent widget to add the accordion to
- * @param style the SWT style mask to use
- * @param headers a list of headers, whose {@link Object#toString} method should
- * produce the heading label
- * @param greedy if true, grow vertically as much as possible
- * @param wrapChildren if true, configure the child area to be horizontally laid out
- * with wrapping
- * @param expand Set of headers to expand initially
- */
- public AccordionControl(Composite parent, int style, List<?> headers,
- boolean greedy, boolean wrapChildren, Set<String> expand) {
- super(parent, style);
- mWrap = wrapChildren;
-
- GridLayout gridLayout = new GridLayout(1, false);
- gridLayout.verticalSpacing = HEADER_SPACING;
- gridLayout.horizontalSpacing = 0;
- gridLayout.marginWidth = 0;
- gridLayout.marginHeight = 0;
- setLayout(gridLayout);
-
- Font labelFont = null;
-
- mOpen = IconFactory.getInstance().getIcon("open-folder"); //$NON-NLS-1$
- mClosed = IconFactory.getInstance().getIcon("closed-folder"); //$NON-NLS-1$
- List<CLabel> expandLabels = new ArrayList<CLabel>();
-
- for (Object header : headers) {
- final CLabel label = new CLabel(this, SWT.SHADOW_OUT);
- label.setText(header.toString().replace("&", "&&")); //$NON-NLS-1$ //$NON-NLS-2$
- updateBackground(label, false);
- if (labelFont == null) {
- labelFont = JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT);
- }
- label.setFont(labelFont);
- label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
- setHeader(header, label);
- label.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseUp(MouseEvent e) {
- if (e.button == 1 && (e.stateMask & SWT.MODIFIER_MASK) == 0) {
- toggle(label, true, mSingle);
- }
- }
- });
- label.addMouseTrackListener(new MouseTrackListener() {
- @Override
- public void mouseEnter(MouseEvent e) {
- updateBackground(label, true);
- }
-
- @Override
- public void mouseExit(MouseEvent e) {
- updateBackground(label, false);
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- }
- });
-
- // Turn off border?
- final ScrolledComposite scrolledComposite = new ScrolledComposite(this, SWT.V_SCROLL);
- ScrollBar verticalBar = scrolledComposite.getVerticalBar();
- verticalBar.setIncrement(20);
- verticalBar.setPageIncrement(100);
-
- // Do we need the scrolled composite or can we just look at the next
- // wizard in the hierarchy?
-
- setContentArea(label, scrolledComposite);
- scrolledComposite.setExpandHorizontal(true);
- scrolledComposite.setExpandVertical(true);
- GridData scrollGridData = new GridData(SWT.FILL,
- greedy ? SWT.FILL : SWT.TOP, false, greedy, 1, 1);
- scrollGridData.exclude = true;
- scrollGridData.grabExcessHorizontalSpace = wrapChildren;
- scrolledComposite.setLayoutData(scrollGridData);
-
- if (wrapChildren) {
- scrolledComposite.addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = scrolledComposite.getClientArea();
- Control content = scrolledComposite.getContent();
- if (content != null && r != null) {
- Point minSize = content.computeSize(r.width, SWT.DEFAULT);
- scrolledComposite.setMinSize(minSize);
- ScrollBar vBar = scrolledComposite.getVerticalBar();
- vBar.setPageIncrement(r.height);
- }
- }
- });
- }
-
- updateIcon(label);
- if (expand != null && expand.contains(label.getText())) {
- // Comparing "label.getText()" rather than "header" because we make some
- // tweaks to the label (replacing & with && etc) and in the getExpandedCategories
- // method we return the label texts
- expandLabels.add(label);
- }
- }
-
- // Expand the requested categories
- for (CLabel label : expandLabels) {
- toggle(label, false, false);
- }
- }
-
- /** Updates the background gradient of the given header label */
- private void updateBackground(CLabel label, boolean mouseOver) {
- Display display = label.getDisplay();
- label.setBackground(new Color[] {
- display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW),
- display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND),
- display.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW)
- }, new int[] {
- mouseOver ? 60 : 40, 100
- }, true);
- }
-
- /**
- * Updates the icon for a header label to be open/close based on the {@link #isOpen}
- * state
- */
- private void updateIcon(CLabel label) {
- label.setImage(isOpen(label) ? mOpen : mClosed);
- }
-
- /** Returns true if the content area for the given label is open/showing */
- private boolean isOpen(Control label) {
- return !((GridData) getContentArea(label).getLayoutData()).exclude;
- }
-
- /** Toggles the visibility of the children of the given label */
- private void toggle(CLabel label, boolean performLayout, boolean autoClose) {
- if (autoClose) {
- collapseAll(true);
- }
- ScrolledComposite scrolledComposite = getContentArea(label);
-
- GridData scrollGridData = (GridData) scrolledComposite.getLayoutData();
- boolean close = !scrollGridData.exclude;
- scrollGridData.exclude = close;
- scrolledComposite.setVisible(!close);
- updateIcon(label);
-
- if (!scrollGridData.exclude && scrolledComposite.getContent() == null) {
- Object header = getHeader(label);
- Composite composite = createChildContainer(scrolledComposite, header, SWT.NONE);
- createChildren(composite, header);
- while (composite.getParent() != scrolledComposite) {
- composite = composite.getParent();
- }
- scrolledComposite.setContent(composite);
- scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
- }
-
- if (performLayout) {
- layout(true);
- }
- }
-
- /** Returns the header object for the given header label */
- private Object getHeader(Control label) {
- return label.getData(KEY_HEADER);
- }
-
- /** Sets the header object for the given header label */
- private void setHeader(Object header, final CLabel label) {
- label.setData(KEY_HEADER, header);
- }
-
- /** Returns the content area for the given header label */
- private ScrolledComposite getContentArea(Control label) {
- return (ScrolledComposite) label.getData(KEY_CONTENT);
- }
-
- /** Sets the content area for the given header label */
- private void setContentArea(final CLabel label, ScrolledComposite scrolledComposite) {
- label.setData(KEY_CONTENT, scrolledComposite);
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- /**
- * Returns the set of expanded categories in the palette. Note: Header labels will have
- * escaped ampersand characters with double ampersands.
- *
- * @return the set of expanded categories in the palette - never null
- */
- public Set<String> getExpandedCategories() {
- Set<String> expanded = new HashSet<String>();
- for (Control c : getChildren()) {
- if (c instanceof CLabel) {
- if (isOpen(c)) {
- expanded.add(((CLabel) c).getText());
- }
- }
- }
-
- return expanded;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/BinPacker.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/BinPacker.java
deleted file mode 100644
index 9fc2e0937..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/BinPacker.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2012 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 com.android.annotations.Nullable;
-import com.android.ide.common.api.Rect;
-
-import java.awt.Color;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-/**
- * This class implements 2D bin packing: packing rectangles into a given area as
- * tightly as "possible" (bin packing in general is NP hard, so this class uses
- * heuristics).
- * <p>
- * The algorithm implemented is to keep a set of (possibly overlapping)
- * available areas for placement. For each newly inserted rectangle, we first
- * pick which available space to occupy, and we then subdivide the
- * current rectangle into all the possible remaining unoccupied sub-rectangles.
- * We also remove any other space rectangles which are no longer eligible if
- * they are intersecting the newly placed rectangle.
- * <p>
- * This algorithm is not very fast, so should not be used for a large number of
- * rectangles.
- */
-class BinPacker {
- /**
- * When enabled, the successive passes are dumped as PNG images showing the
- * various available and occupied rectangles)
- */
- private static final boolean DEBUG = false;
-
- private final List<Rect> mSpace = new ArrayList<Rect>();
- private final int mMinHeight;
- private final int mMinWidth;
-
- /**
- * Creates a new {@linkplain BinPacker}. To use it, first add one or more
- * initial available space rectangles with {@link #addSpace(Rect)}, and then
- * place the rectangles with {@link #occupy(int, int)}. The returned
- * {@link Rect} from {@link #occupy(int, int)} gives the coordinates of the
- * positioned rectangle.
- *
- * @param minWidth the smallest width of any rectangle placed into this bin
- * @param minHeight the smallest height of any rectangle placed into this bin
- */
- BinPacker(int minWidth, int minHeight) {
- mMinWidth = minWidth;
- mMinHeight = minHeight;
-
- if (DEBUG) {
- mAllocated = new ArrayList<Rect>();
- sLayoutId++;
- sRectId = 1;
- }
- }
-
- /** Adds more available space */
- void addSpace(Rect rect) {
- if (rect.w >= mMinWidth && rect.h >= mMinHeight) {
- mSpace.add(rect);
- }
- }
-
- /** Attempts to place a rectangle of the given dimensions, if possible */
- @Nullable
- Rect occupy(int width, int height) {
- int index = findBest(width, height);
- if (index == -1) {
- return null;
- }
-
- return split(index, width, height);
- }
-
- /**
- * Finds the best available space rectangle to position a new rectangle of
- * the given size in.
- */
- private int findBest(int width, int height) {
- if (mSpace.isEmpty()) {
- return -1;
- }
-
- // Try to pack as far up as possible first
- int bestIndex = -1;
- boolean multipleAtSameY = false;
- int minY = Integer.MAX_VALUE;
- for (int i = 0, n = mSpace.size(); i < n; i++) {
- Rect rect = mSpace.get(i);
- if (rect.y <= minY) {
- if (rect.w >= width && rect.h >= height) {
- if (rect.y < minY) {
- minY = rect.y;
- multipleAtSameY = false;
- bestIndex = i;
- } else if (minY == rect.y) {
- multipleAtSameY = true;
- }
- }
- }
- }
-
- if (!multipleAtSameY) {
- return bestIndex;
- }
-
- bestIndex = -1;
-
- // Pick a rectangle. This currently tries to find the rectangle whose shortest
- // side most closely matches the placed rectangle's size.
- // Attempt to find the best short side fit
- int bestShortDistance = Integer.MAX_VALUE;
- int bestLongDistance = Integer.MAX_VALUE;
-
- for (int i = 0, n = mSpace.size(); i < n; i++) {
- Rect rect = mSpace.get(i);
- if (rect.y != minY) { // Only comparing elements at same y
- continue;
- }
- if (rect.w >= width && rect.h >= height) {
- if (width < height) {
- int distance = rect.w - width;
- if (distance < bestShortDistance ||
- distance == bestShortDistance &&
- (rect.h - height) < bestLongDistance) {
- bestShortDistance = distance;
- bestLongDistance = rect.h - height;
- bestIndex = i;
- }
- } else {
- int distance = rect.w - width;
- if (distance < bestShortDistance ||
- distance == bestShortDistance &&
- (rect.h - height) < bestLongDistance) {
- bestShortDistance = distance;
- bestLongDistance = rect.h - height;
- bestIndex = i;
- }
- }
- }
- }
-
- return bestIndex;
- }
-
- /**
- * Removes the rectangle at the given index. Since the rectangles are in an
- * {@link ArrayList}, removing a rectangle in the normal way is slow (it
- * would involve shifting all elements), but since we don't care about
- * order, this always swaps the to-be-deleted element to the last position
- * in the array first, <b>then</b> it deletes it (which should be
- * immediate).
- *
- * @param index the index in the {@link #mSpace} list to remove a rectangle
- * from
- */
- private void removeRect(int index) {
- assert !mSpace.isEmpty();
- int lastIndex = mSpace.size() - 1;
- if (index != lastIndex) {
- // Swap before remove to make deletion faster since we don't
- // care about order
- Rect temp = mSpace.get(index);
- mSpace.set(index, mSpace.get(lastIndex));
- mSpace.set(lastIndex, temp);
- }
-
- mSpace.remove(lastIndex);
- }
-
- /**
- * Splits the rectangle at the given rectangle index such that it can contain
- * a rectangle of the given width and height. */
- private Rect split(int index, int width, int height) {
- Rect rect = mSpace.get(index);
- assert rect.w >= width && rect.h >= height : rect;
-
- Rect r = new Rect(rect);
- r.w = width;
- r.h = height;
-
- // Remove all rectangles that intersect my rectangle
- for (int i = 0; i < mSpace.size(); i++) {
- Rect other = mSpace.get(i);
- if (other.intersects(r)) {
- removeRect(i);
- i--;
- }
- }
-
-
- // Split along vertical line x = rect.x + width:
- // (rect.x,rect.y)
- // +-------------+-------------------------+
- // | | |
- // | | |
- // | | height |
- // | | |
- // | | |
- // +-------------+ B | rect.h
- // | width |
- // | | |
- // | A |
- // | | |
- // | |
- // +---------------------------------------+
- // rect.w
- int remainingHeight = rect.h - height;
- int remainingWidth = rect.w - width;
- if (remainingHeight >= mMinHeight) {
- mSpace.add(new Rect(rect.x, rect.y + height, width, remainingHeight));
- }
- if (remainingWidth >= mMinWidth) {
- mSpace.add(new Rect(rect.x + width, rect.y, remainingWidth, rect.h));
- }
-
- // Split along horizontal line y = rect.y + height:
- // +-------------+-------------------------+
- // | | |
- // | | height |
- // | | A |
- // | | |
- // | | | rect.h
- // +-------------+ - - - - - - - - - - - - |
- // | width |
- // | |
- // | B |
- // | |
- // | |
- // +---------------------------------------+
- // rect.w
- if (remainingHeight >= mMinHeight) {
- mSpace.add(new Rect(rect.x, rect.y + height, rect.w, remainingHeight));
- }
- if (remainingWidth >= mMinWidth) {
- mSpace.add(new Rect(rect.x + width, rect.y, remainingWidth, height));
- }
-
- // Remove redundant rectangles. This is not very efficient.
- for (int i = 0; i < mSpace.size() - 1; i++) {
- for (int j = i + 1; j < mSpace.size(); j++) {
- Rect iRect = mSpace.get(i);
- Rect jRect = mSpace.get(j);
- if (jRect.contains(iRect)) {
- removeRect(i);
- i--;
- break;
- }
- if (iRect.contains(jRect)) {
- removeRect(j);
- j--;
- }
- }
- }
-
- if (DEBUG) {
- mAllocated.add(r);
- dumpImage();
- }
-
- return r;
- }
-
- // DEBUGGING CODE: Enable with DEBUG
-
- private List<Rect> mAllocated;
- private static int sLayoutId;
- private static int sRectId;
- private void dumpImage() {
- if (DEBUG) {
- int width = 100;
- int height = 100;
- for (Rect rect : mSpace) {
- width = Math.max(width, rect.w);
- height = Math.max(height, rect.h);
- }
- width += 10;
- height += 10;
-
- BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = image.createGraphics();
- g.setColor(Color.BLACK);
- g.fillRect(0, 0, image.getWidth(), image.getHeight());
-
- Color[] colors = new Color[] {
- Color.blue, Color.cyan,
- Color.green, Color.magenta, Color.orange,
- Color.pink, Color.red, Color.white, Color.yellow, Color.darkGray,
- Color.lightGray, Color.gray,
- };
-
- char allocated = 'A';
- for (Rect rect : mAllocated) {
- Color color = new Color(0x9FFFFFFF, true);
- g.setColor(color);
- g.setBackground(color);
- g.fillRect(rect.x, rect.y, rect.w, rect.h);
- g.setColor(Color.WHITE);
- g.drawRect(rect.x, rect.y, rect.w, rect.h);
- g.drawString("" + (allocated++),
- rect.x + rect.w / 2, rect.y + rect.h / 2);
- }
-
- int colorIndex = 0;
- for (Rect rect : mSpace) {
- Color color = colors[colorIndex];
- colorIndex = (colorIndex + 1) % colors.length;
-
- color = new Color(color.getRed(), color.getGreen(), color.getBlue(), 128);
- g.setColor(color);
-
- g.fillRect(rect.x, rect.y, rect.w, rect.h);
- g.setColor(Color.WHITE);
- g.drawString(Integer.toString(colorIndex),
- rect.x + rect.w / 2, rect.y + rect.h / 2);
- }
-
-
- g.dispose();
-
- File file = new File("/tmp/layout" + sLayoutId + "_pass" + sRectId + ".png");
- try {
- ImageIO.write(image, "PNG", file);
- System.out.println("Wrote diagnostics image " + file);
- } catch (IOException e) {
- e.printStackTrace();
- }
- sRectId++;
- }
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasAlternateSelection.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasAlternateSelection.java
deleted file mode 100644
index c04061cbd..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasAlternateSelection.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2009 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 java.util.List;
-
-/**
- * Information for the current alternate selection, i.e. the possible selected items
- * that are located at the same x/y as the original view, either sibling or parents.
- */
-/* package */ class CanvasAlternateSelection {
- private final CanvasViewInfo mOriginatingView;
- private final List<CanvasViewInfo> mAltViews;
- private int mIndex;
-
- /**
- * Creates a new alternate selection based on the given originating view and the
- * given list of alternate views. Both cannot be null.
- */
- public CanvasAlternateSelection(CanvasViewInfo originatingView, List<CanvasViewInfo> altViews) {
- assert originatingView != null;
- assert altViews != null;
- mOriginatingView = originatingView;
- mAltViews = altViews;
- mIndex = altViews.size() - 1;
- }
-
- /** Returns the list of alternate views. Cannot be null. */
- public List<CanvasViewInfo> getAltViews() {
- return mAltViews;
- }
-
- /** Returns the originating view. Cannot be null. */
- public CanvasViewInfo getOriginatingView() {
- return mOriginatingView;
- }
-
- /**
- * Returns the current alternate view to select.
- * Initially this is the top-most view.
- */
- public CanvasViewInfo getCurrent() {
- return mIndex >= 0 ? mAltViews.get(mIndex) : null;
- }
-
- /**
- * Changes the current view to be the next one and then returns it.
- * This loops through the alternate views.
- */
- public CanvasViewInfo getNext() {
- if (mIndex == 0) {
- mIndex = mAltViews.size() - 1;
- } else if (mIndex > 0) {
- mIndex--;
- }
-
- return getCurrent();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java
deleted file mode 100644
index ad5bd52e5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasTransform.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2009 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.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE;
-
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.widgets.ScrollBar;
-
-/**
- * Helper class to convert between control pixel coordinates and canvas coordinates.
- * Takes care of the zooming and offset of the canvas.
- */
-public class CanvasTransform {
- /**
- * Default margin around the rendered image, reduced
- * when the contents do not fit.
- */
- public static final int DEFAULT_MARGIN = 25;
-
- /**
- * The canvas which controls the zooming.
- */
- private final LayoutCanvas mCanvas;
-
- /** Canvas image size (original, before zoom), in pixels. */
- private int mImgSize;
-
- /** Full size being scrolled (after zoom), in pixels */
- private int mFullSize;;
-
- /** Client size, in pixels. */
- private int mClientSize;
-
- /** Left-top offset in client pixel coordinates. */
- private int mTranslate;
-
- /** Current margin */
- private int mMargin = DEFAULT_MARGIN;
-
- /** Scaling factor, > 0. */
- private double mScale;
-
- /** Scrollbar widget. */
- private ScrollBar mScrollbar;
-
- public CanvasTransform(LayoutCanvas layoutCanvas, ScrollBar scrollbar) {
- mCanvas = layoutCanvas;
- mScrollbar = scrollbar;
- mScale = 1.0;
- mTranslate = 0;
-
- mScrollbar.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- // User requested scrolling. Changes translation and redraw canvas.
- mTranslate = mScrollbar.getSelection();
- CanvasTransform.this.mCanvas.redraw();
- }
- });
- mScrollbar.setIncrement(20);
- }
-
- /**
- * Sets the new scaling factor. Recomputes scrollbars.
- * @param scale Scaling factor, > 0.
- */
- public void setScale(double scale) {
- if (mScale != scale) {
- mScale = scale;
- resizeScrollbar();
- }
- }
-
- /** Recomputes the scrollbar and view port settings */
- public void refresh() {
- resizeScrollbar();
- }
-
- /**
- * Returns current scaling factor.
- *
- * @return The current scaling factor
- */
- public double getScale() {
- return mScale;
- }
-
- /**
- * Returns Canvas image size (original, before zoom), in pixels.
- *
- * @return Canvas image size (original, before zoom), in pixels
- */
- public int getImgSize() {
- return mImgSize;
- }
-
- /**
- * Returns the scaled image size in pixels.
- *
- * @return The scaled image size in pixels.
- */
- public int getScaledImgSize() {
- return (int) (mImgSize * mScale);
- }
-
- /**
- * Changes the size of the canvas image and the client size. Recomputes
- * scrollbars.
- *
- * @param imgSize the size of the image being scaled
- * @param fullSize the size of the full view area being scrolled
- * @param clientSize the size of the view port
- */
- public void setSize(int imgSize, int fullSize, int clientSize) {
- mImgSize = imgSize;
- mFullSize = fullSize;
- mClientSize = clientSize;
- mScrollbar.setPageIncrement(clientSize);
- resizeScrollbar();
- }
-
- private void resizeScrollbar() {
- // scaled image size
- int sx = (int) (mScale * mFullSize);
-
- // Adjust margin such that for zoomed out views
- // we don't waste space (unless the viewport is
- // large enough to accommodate it)
- int delta = mClientSize - sx;
- if (delta < 0) {
- mMargin = 0;
- } else if (delta < 2 * DEFAULT_MARGIN) {
- mMargin = delta / 2;
-
- ImageOverlay imageOverlay = mCanvas.getImageOverlay();
- if (imageOverlay != null && imageOverlay.getShowDropShadow()
- && delta >= SHADOW_SIZE / 2) {
- mMargin -= SHADOW_SIZE / 2;
- // Add a little padding on the top too, if there's room. The shadow assets
- // include enough padding on the bottom to not make this look clipped.
- if (mMargin < 4) {
- mMargin += 4;
- }
- }
- } else {
- mMargin = DEFAULT_MARGIN;
- }
-
- if (mCanvas.getPreviewManager().hasPreviews()) {
- // Make more room for the previews
- mMargin = 2;
- }
-
- // actual client area is always reduced by the margins
- int cx = mClientSize - 2 * mMargin;
-
- if (sx < cx) {
- mTranslate = 0;
- mScrollbar.setEnabled(false);
- } else {
- mScrollbar.setEnabled(true);
-
- int selection = mScrollbar.getSelection();
- int thumb = cx;
- int maximum = sx;
-
- if (selection + thumb > maximum) {
- selection = maximum - thumb;
- if (selection < 0) {
- selection = 0;
- }
- }
-
- mScrollbar.setValues(selection, mScrollbar.getMinimum(), maximum, thumb, mScrollbar
- .getIncrement(), mScrollbar.getPageIncrement());
-
- mTranslate = selection;
- }
- }
-
- public int getMargin() {
- return mMargin;
- }
-
- public int translate(int canvasX) {
- return mMargin - mTranslate + (int) (mScale * canvasX);
- }
-
- public int scale(int canwasW) {
- return (int) (mScale * canwasW);
- }
-
- public int inverseTranslate(int screenX) {
- return (int) ((screenX - mMargin + mTranslate) / mScale);
- }
-
- public int inverseScale(int canwasW) {
- return (int) (canwasW / mScale);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
deleted file mode 100644
index 03c6c3926..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * Copyright (C) 2009 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.FQCN_SPACE;
-import static com.android.SdkConstants.FQCN_SPACE_V7;
-import static com.android.SdkConstants.GESTURE_OVERLAY_VIEW;
-import static com.android.SdkConstants.VIEW_MERGE;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.layout.GridLayoutRule;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.MergeCookie;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.utils.Pair;
-
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.ui.views.properties.IPropertyDescriptor;
-import org.eclipse.ui.views.properties.IPropertySheetPage;
-import org.eclipse.ui.views.properties.IPropertySource;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Maps a {@link ViewInfo} in a structure more adapted to our needs.
- * The only large difference is that we keep both the original bounds of the view info
- * and we pre-compute the selection bounds which are absolute to the rendered image
- * (whereas the original bounds are relative to the parent view.)
- * <p/>
- * Each view also knows its parent and children.
- * <p/>
- * We can't alter {@link ViewInfo} as it is part of the LayoutBridge and needs to
- * have a fixed API.
- * <p/>
- * The view info also implements {@link IPropertySource}, which enables a linked
- * {@link IPropertySheetPage} to display the attributes of the selected element.
- * This class actually delegates handling of {@link IPropertySource} to the underlying
- * {@link UiViewElementNode}, if any.
- */
-public class CanvasViewInfo implements IPropertySource {
-
- /**
- * Minimal size of the selection, in case an empty view or layout is selected.
- */
- public static final int SELECTION_MIN_SIZE = 6;
-
- private final Rectangle mAbsRect;
- private final Rectangle mSelectionRect;
- private final String mName;
- private final Object mViewObject;
- private final UiViewElementNode mUiViewNode;
- private CanvasViewInfo mParent;
- private ViewInfo mViewInfo;
- private final List<CanvasViewInfo> mChildren = new ArrayList<CanvasViewInfo>();
-
- /**
- * Is this view info an individually exploded view? This is the case for views
- * that were specially inflated by the {@link UiElementPullParser} and assigned
- * fixed padding because they were invisible and somebody requested visibility.
- */
- private boolean mExploded;
-
- /**
- * Node sibling. This is usually null, but it's possible for a single node in the
- * model to have <b>multiple</b> separate views in the canvas, for example
- * when you {@code <include>} a view that has multiple widgets inside a
- * {@code <merge>} tag. In this case, all the views have the same node model,
- * the include tag, and selecting the include should highlight all the separate
- * views that are linked to this node. That's what this field is all about: it is
- * a <b>circular</b> list of all the siblings that share the same node.
- */
- private List<CanvasViewInfo> mNodeSiblings;
-
- /**
- * Constructs a {@link CanvasViewInfo} initialized with the given initial values.
- */
- private CanvasViewInfo(CanvasViewInfo parent, String name,
- Object viewObject, UiViewElementNode node, Rectangle absRect,
- Rectangle selectionRect, ViewInfo viewInfo) {
- mParent = parent;
- mName = name;
- mViewObject = viewObject;
- mViewInfo = viewInfo;
- mUiViewNode = node;
- mAbsRect = absRect;
- mSelectionRect = selectionRect;
- }
-
- /**
- * Returns the original {@link ViewInfo} bounds in absolute coordinates
- * over the whole graphic.
- *
- * @return the bounding box in absolute coordinates
- */
- @NonNull
- public Rectangle getAbsRect() {
- return mAbsRect;
- }
-
- /**
- * Returns the absolute selection bounds of the view info as a rectangle.
- * The selection bounds will always have a size greater or equal to
- * {@link #SELECTION_MIN_SIZE}.
- * The width/height is inclusive (i.e. width = right-left-1).
- * This is in absolute "screen" coordinates (relative to the rendered bitmap).
- *
- * @return the absolute selection bounds
- */
- @NonNull
- public Rectangle getSelectionRect() {
- return mSelectionRect;
- }
-
- /**
- * Returns the view node. Could be null, although unlikely.
- * @return An {@link UiViewElementNode} that uniquely identifies the object in the XML model.
- * @see ViewInfo#getCookie()
- */
- @Nullable
- public UiViewElementNode getUiViewNode() {
- return mUiViewNode;
- }
-
- /**
- * Returns the parent {@link CanvasViewInfo}.
- * It is null for the root and non-null for children.
- *
- * @return the parent {@link CanvasViewInfo}, which can be null
- */
- @Nullable
- public CanvasViewInfo getParent() {
- return mParent;
- }
-
- /**
- * Returns the list of children of this {@link CanvasViewInfo}.
- * The list is never null. It can be empty.
- * By contract, this.getChildren().get(0..n-1).getParent() == this.
- *
- * @return the children, never null
- */
- @NonNull
- public List<CanvasViewInfo> getChildren() {
- return mChildren;
- }
-
- /**
- * For nodes that have multiple views rendered from a single node, such as the
- * children of a {@code <merge>} tag included into a separate layout, return the
- * "primary" view, the first view that is rendered
- */
- @Nullable
- private CanvasViewInfo getPrimaryNodeSibling() {
- if (mNodeSiblings == null || mNodeSiblings.size() == 0) {
- return null;
- }
-
- return mNodeSiblings.get(0);
- }
-
- /**
- * Returns true if this view represents one view of many linked to a single node, and
- * where this is the primary view. The primary view is the one that will be shown
- * in the outline for example (since we only show nodes, not views, in the outline,
- * and therefore don't want repetitions when a view has more than one view info.)
- *
- * @return true if this is the primary view among more than one linked to a single
- * node
- */
- private boolean isPrimaryNodeSibling() {
- return getPrimaryNodeSibling() == this;
- }
-
- /**
- * Returns the list of node sibling of this view (which <b>will include this
- * view</b>). For most views this is going to be null, but for views that share a
- * single node (such as widgets inside a {@code <merge>} tag included into another
- * layout), this will provide all the views that correspond to the node.
- *
- * @return a non-empty list of siblings (including this), or null
- */
- @Nullable
- public List<CanvasViewInfo> getNodeSiblings() {
- return mNodeSiblings;
- }
-
- /**
- * Returns all the children of the canvas view info where each child corresponds to a
- * unique node that the user can see and select. This is intended for use by the
- * outline for example, where only the actual nodes are displayed, not the views
- * themselves.
- * <p>
- * Most views have their own nodes, so this is generally the same as
- * {@link #getChildren}, except in the case where you for example include a view that
- * has multiple widgets inside a {@code <merge>} tag, where all these widgets have the
- * same node (the {@code <merge>} tag).
- *
- * @return list of {@link CanvasViewInfo} objects that are children of this view,
- * never null
- */
- @NonNull
- public List<CanvasViewInfo> getUniqueChildren() {
- boolean haveHidden = false;
-
- for (CanvasViewInfo info : mChildren) {
- if (info.mNodeSiblings != null) {
- // We have secondary children; must create a new collection containing
- // only non-secondary children
- List<CanvasViewInfo> children = new ArrayList<CanvasViewInfo>();
- for (CanvasViewInfo vi : mChildren) {
- if (vi.mNodeSiblings == null) {
- children.add(vi);
- } else if (vi.isPrimaryNodeSibling()) {
- children.add(vi);
- }
- }
- return children;
- }
-
- haveHidden |= info.isHidden();
- }
-
- if (haveHidden) {
- List<CanvasViewInfo> children = new ArrayList<CanvasViewInfo>(mChildren.size());
- for (CanvasViewInfo vi : mChildren) {
- if (!vi.isHidden()) {
- children.add(vi);
- }
- }
-
- return children;
- }
-
- return mChildren;
- }
-
- /**
- * Returns true if the specific {@link CanvasViewInfo} is a parent
- * of this {@link CanvasViewInfo}. It can be a direct parent or any
- * grand-parent higher in the hierarchy.
- *
- * @param potentialParent the view info to check
- * @return true if the given info is a parent of this view
- */
- public boolean isParent(@NonNull CanvasViewInfo potentialParent) {
- CanvasViewInfo p = mParent;
- while (p != null) {
- if (p == potentialParent) {
- return true;
- }
- p = p.getParent();
- }
- return false;
- }
-
- /**
- * Returns the name of the {@link CanvasViewInfo}.
- * Could be null, although unlikely.
- * Experience shows this is the full qualified Java name of the View.
- * TODO: Rename this method to getFqcn.
- *
- * @return the name of the view info
- *
- * @see ViewInfo#getClassName()
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Returns the View object associated with the {@link CanvasViewInfo}.
- * @return the view object or null.
- */
- @Nullable
- public Object getViewObject() {
- return mViewObject;
- }
-
- /**
- * Returns the baseline of this object, or -1 if it does not support a baseline
- *
- * @return the baseline or -1
- */
- public int getBaseline() {
- if (mViewInfo != null) {
- int baseline = mViewInfo.getBaseLine();
- if (baseline != Integer.MIN_VALUE) {
- return baseline;
- }
- }
-
- return -1;
- }
-
- /**
- * Returns the {@link Margins} for this {@link CanvasViewInfo}
- *
- * @return the {@link Margins} for this {@link CanvasViewInfo}
- */
- @Nullable
- public Margins getMargins() {
- if (mViewInfo != null) {
- int leftMargin = mViewInfo.getLeftMargin();
- int topMargin = mViewInfo.getTopMargin();
- int rightMargin = mViewInfo.getRightMargin();
- int bottomMargin = mViewInfo.getBottomMargin();
- return new Margins(
- leftMargin != Integer.MIN_VALUE ? leftMargin : 0,
- rightMargin != Integer.MIN_VALUE ? rightMargin : 0,
- topMargin != Integer.MIN_VALUE ? topMargin : 0,
- bottomMargin != Integer.MIN_VALUE ? bottomMargin : 0
- );
- }
-
- return null;
- }
-
- // ---- Implementation of IPropertySource
- // TODO: Get rid of this once the old propertysheet implementation is fully gone
-
- @Override
- public Object getEditableValue() {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- return ((IPropertySource) uiView).getEditableValue();
- }
- return null;
- }
-
- @Override
- public IPropertyDescriptor[] getPropertyDescriptors() {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- return ((IPropertySource) uiView).getPropertyDescriptors();
- }
- return null;
- }
-
- @Override
- public Object getPropertyValue(Object id) {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- return ((IPropertySource) uiView).getPropertyValue(id);
- }
- return null;
- }
-
- @Override
- public boolean isPropertySet(Object id) {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- return ((IPropertySource) uiView).isPropertySet(id);
- }
- return false;
- }
-
- @Override
- public void resetPropertyValue(Object id) {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- ((IPropertySource) uiView).resetPropertyValue(id);
- }
- }
-
- @Override
- public void setPropertyValue(Object id, Object value) {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- ((IPropertySource) uiView).setPropertyValue(id, value);
- }
- }
-
- /**
- * Returns the XML node corresponding to this info, or null if there is no
- * such XML node.
- *
- * @return The XML node corresponding to this info object, or null
- */
- @Nullable
- public Node getXmlNode() {
- UiViewElementNode uiView = getUiViewNode();
- if (uiView != null) {
- return uiView.getXmlNode();
- }
-
- return null;
- }
-
- /**
- * Returns true iff this view info corresponds to a root element.
- *
- * @return True iff this is a root view info.
- */
- public boolean isRoot() {
- // Select the visual element -- unless it's the root.
- // The root element is the one whose GRAND parent
- // is null (because the parent will be a -document-
- // node).
-
- // Special case: a gesture overlay is sometimes added as the root, but for all intents
- // and purposes it is its layout child that is the real root so treat that one as the
- // root as well (such that the whole layout canvas does not highlight as part of hovers
- // etc)
- if (mParent != null
- && mParent.mName.endsWith(GESTURE_OVERLAY_VIEW)
- && mParent.isRoot()
- && mParent.mChildren.size() == 1) {
- return true;
- }
-
- return mUiViewNode == null || mUiViewNode.getUiParent() == null ||
- mUiViewNode.getUiParent().getUiParent() == null;
- }
-
- /**
- * Returns true if this {@link CanvasViewInfo} represents an invisible widget that
- * should be highlighted when selected. This is the case for any layout that is less than the minimum
- * threshold ({@link #SELECTION_MIN_SIZE}), or any other view that has -0- bounds.
- *
- * @return True if this is a tiny layout or invisible view
- */
- public boolean isInvisible() {
- if (isHidden()) {
- // Don't expand and highlight hidden widgets
- return false;
- }
-
- if (mAbsRect.width < SELECTION_MIN_SIZE || mAbsRect.height < SELECTION_MIN_SIZE) {
- return mUiViewNode != null && (mUiViewNode.getDescriptor().hasChildren() ||
- mAbsRect.width <= 0 || mAbsRect.height <= 0);
- }
-
- return false;
- }
-
- /**
- * Returns true if this {@link CanvasViewInfo} represents a widget that should be
- * hidden, such as a {@code <Space>} which are typically not manipulated by the user
- * through dragging etc.
- *
- * @return true if this is a hidden view
- */
- public boolean isHidden() {
- if (GridLayoutRule.sDebugGridLayout) {
- return false;
- }
-
- return FQCN_SPACE.equals(mName) || FQCN_SPACE_V7.equals(mName);
- }
-
- /**
- * Is this {@link CanvasViewInfo} a view that has had its padding inflated in order to
- * make it visible during selection or dragging? Note that this is NOT considered to
- * be the case in the explode-all-views mode where all nodes have their padding
- * increased; it's only used for views that individually exploded because they were
- * requested visible and they returned true for {@link #isInvisible()}.
- *
- * @return True if this is an exploded node.
- */
- public boolean isExploded() {
- return mExploded;
- }
-
- /**
- * Mark this {@link CanvasViewInfo} as having been exploded or not. See the
- * {@link #isExploded()} method for details on what this property means.
- *
- * @param exploded New value of the exploded property to mark this info with.
- */
- void setExploded(boolean exploded) {
- mExploded = exploded;
- }
-
- /**
- * Returns the info represented as a {@link SimpleElement}.
- *
- * @return A {@link SimpleElement} wrapping this info.
- */
- @NonNull
- SimpleElement toSimpleElement() {
-
- UiViewElementNode uiNode = getUiViewNode();
-
- String fqcn = SimpleXmlTransfer.getFqcn(uiNode.getDescriptor());
- String parentFqcn = null;
- Rect bounds = SwtUtils.toRect(getAbsRect());
- Rect parentBounds = null;
-
- UiElementNode uiParent = uiNode.getUiParent();
- if (uiParent != null) {
- parentFqcn = SimpleXmlTransfer.getFqcn(uiParent.getDescriptor());
- }
- if (getParent() != null) {
- parentBounds = SwtUtils.toRect(getParent().getAbsRect());
- }
-
- SimpleElement e = new SimpleElement(fqcn, parentFqcn, bounds, parentBounds);
-
- for (UiAttributeNode attr : uiNode.getAllUiAttributes()) {
- String value = attr.getCurrentValue();
- if (value != null && value.length() > 0) {
- AttributeDescriptor attrDesc = attr.getDescriptor();
- SimpleAttribute a = new SimpleAttribute(
- attrDesc.getNamespaceUri(),
- attrDesc.getXmlLocalName(),
- value);
- e.addAttribute(a);
- }
- }
-
- for (CanvasViewInfo childVi : getChildren()) {
- SimpleElement e2 = childVi.toSimpleElement();
- if (e2 != null) {
- e.addInnerElement(e2);
- }
- }
-
- return e;
- }
-
- /**
- * Returns the layout url attribute value for the closest surrounding include or
- * fragment element parent, or null if this {@link CanvasViewInfo} is not rendered as
- * part of an include or fragment tag.
- *
- * @return the layout url attribute value for the surrounding include tag, or null if
- * not applicable
- */
- @Nullable
- public String getIncludeUrl() {
- CanvasViewInfo curr = this;
- while (curr != null) {
- if (curr.mUiViewNode != null) {
- Node node = curr.mUiViewNode.getXmlNode();
- if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
- String nodeName = node.getNodeName();
- if (node.getNamespaceURI() == null
- && SdkConstants.VIEW_INCLUDE.equals(nodeName)) {
- // Note: the layout attribute is NOT in the Android namespace
- Element element = (Element) node;
- String url = element.getAttribute(SdkConstants.ATTR_LAYOUT);
- if (url.length() > 0) {
- return url;
- }
- } else if (SdkConstants.VIEW_FRAGMENT.equals(nodeName)) {
- String url = FragmentMenu.getFragmentLayout(node);
- if (url != null) {
- return url;
- }
- }
- }
- }
- curr = curr.mParent;
- }
-
- return null;
- }
-
- /** Adds the given {@link CanvasViewInfo} as a new last child of this view */
- private void addChild(@NonNull CanvasViewInfo child) {
- mChildren.add(child);
- }
-
- /** Adds the given {@link CanvasViewInfo} as a child at the given index */
- private void addChildAt(int index, @NonNull CanvasViewInfo child) {
- mChildren.add(index, child);
- }
-
- /**
- * Removes the given {@link CanvasViewInfo} from the child list of this view, and
- * returns true if it was successfully removed
- *
- * @param child the child to be removed
- * @return true if it was a child and was removed
- */
- public boolean removeChild(@NonNull CanvasViewInfo child) {
- return mChildren.remove(child);
- }
-
- @Override
- public String toString() {
- return "CanvasViewInfo [name=" + mName + ", node=" + mUiViewNode + "]";
- }
-
- // ---- Factory functionality ----
-
- /**
- * Creates a new {@link CanvasViewInfo} hierarchy based on the given {@link ViewInfo}
- * hierarchy. Note that this will not necessarily create one {@link CanvasViewInfo}
- * for each {@link ViewInfo}. It will generally only create {@link CanvasViewInfo}
- * objects for {@link ViewInfo} objects that contain a reference to an
- * {@link UiViewElementNode}, meaning that it corresponds to an element in the XML
- * file for this layout file. This is not always the case, such as in the following
- * scenarios:
- * <ul>
- * <li>we link to other layouts with {@code <include>}
- * <li>the current view is rendered within another view ("Show Included In") such that
- * the outer file does not correspond to elements in the current included XML layout
- * <li>on older platforms that don't support {@link Capability#EMBEDDED_LAYOUT} there
- * is no reference to the {@code <include>} tag
- * <li>with the {@code <merge>} tag we don't get a reference to the corresponding
- * element
- * <ul>
- * <p>
- * This method will build up a set of {@link CanvasViewInfo} that corresponds to the
- * actual <b>selectable</b> views (which are also shown in the Outline).
- *
- * @param layoutlib5 if true, the {@link ViewInfo} hierarchy was created by layoutlib
- * version 5 or higher, which means this algorithm can make certain assumptions
- * (for example that {@code <merge>} siblings will provide {@link MergeCookie}
- * references, so we don't have to search for them.)
- * @param root the root {@link ViewInfo} to build from
- * @return a {@link CanvasViewInfo} hierarchy
- */
- @NonNull
- public static Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root, boolean layoutlib5) {
- return new Builder(layoutlib5).create(root);
- }
-
- /** Builder object which walks over a tree of {@link ViewInfo} objects and builds
- * up a corresponding {@link CanvasViewInfo} hierarchy. */
- private static class Builder {
- public Builder(boolean layoutlib5) {
- mLayoutLib5 = layoutlib5;
- }
-
- /**
- * The mapping from nodes that have a {@code <merge>} as a parent in the node
- * model to their corresponding views
- */
- private Map<UiViewElementNode, List<CanvasViewInfo>> mMergeNodeMap;
-
- /**
- * Whether the ViewInfos are provided by a layout library that is version 5 or
- * later, since that will allow us to take several shortcuts
- */
- private boolean mLayoutLib5;
-
- /**
- * Creates a hierarchy of {@link CanvasViewInfo} objects and merge bounding
- * rectangles from the given {@link ViewInfo} hierarchy
- */
- private Pair<CanvasViewInfo,List<Rectangle>> create(ViewInfo root) {
- Object cookie = root.getCookie();
- if (cookie == null) {
- // Special case: If the root-most view does not have a view cookie,
- // then we are rendering some outer layout surrounding this layout, and in
- // that case we must search down the hierarchy for the (possibly multiple)
- // sub-roots that correspond to elements in this layout, and place them inside
- // an outer view that has no node. In the outline this item will be used to
- // show the inclusion-context.
- CanvasViewInfo rootView = createView(null, root, 0, 0);
- addKeyedSubtrees(rootView, root, 0, 0);
-
- List<Rectangle> includedBounds = new ArrayList<Rectangle>();
- for (CanvasViewInfo vi : rootView.getChildren()) {
- if (vi.getNodeSiblings() == null || vi.isPrimaryNodeSibling()) {
- includedBounds.add(vi.getAbsRect());
- }
- }
-
- // There are <merge> nodes here; see if we can insert it into the hierarchy
- if (mMergeNodeMap != null) {
- // Locate all the nodes that have a <merge> as a parent in the node model,
- // and where the view sits at the top level inside the include-context node.
- UiViewElementNode merge = null;
- List<CanvasViewInfo> merged = new ArrayList<CanvasViewInfo>();
- for (Map.Entry<UiViewElementNode, List<CanvasViewInfo>> entry : mMergeNodeMap
- .entrySet()) {
- UiViewElementNode node = entry.getKey();
- if (!hasMergeParent(node)) {
- continue;
- }
- List<CanvasViewInfo> views = entry.getValue();
- assert views.size() > 0;
- CanvasViewInfo view = views.get(0); // primary
- if (view.getParent() != rootView) {
- continue;
- }
- UiElementNode parent = node.getUiParent();
- if (merge != null && parent != merge) {
- continue;
- }
- merge = (UiViewElementNode) parent;
- merged.add(view);
- }
- if (merged.size() > 0) {
- // Compute a bounding box for the merged views
- Rectangle absRect = null;
- for (CanvasViewInfo child : merged) {
- Rectangle rect = child.getAbsRect();
- if (absRect == null) {
- absRect = rect;
- } else {
- absRect = absRect.union(rect);
- }
- }
-
- CanvasViewInfo mergeView = new CanvasViewInfo(rootView, VIEW_MERGE, null,
- merge, absRect, absRect, null /* viewInfo */);
- for (CanvasViewInfo view : merged) {
- if (rootView.removeChild(view)) {
- mergeView.addChild(view);
- }
- }
- rootView.addChild(mergeView);
- }
- }
-
- return Pair.of(rootView, includedBounds);
- } else {
- // We have a view key at the top, so just go and create {@link CanvasViewInfo}
- // objects for each {@link ViewInfo} until we run into a null key.
- CanvasViewInfo rootView = addKeyedSubtrees(null, root, 0, 0);
-
- // Special case: look to see if the root element is really a <merge>, and if so,
- // manufacture a view for it such that we can target this root element
- // in drag & drop operations, such that we can show it in the outline, etc
- if (rootView != null && hasMergeParent(rootView.getUiViewNode())) {
- CanvasViewInfo merge = new CanvasViewInfo(null, VIEW_MERGE, null,
- (UiViewElementNode) rootView.getUiViewNode().getUiParent(),
- rootView.getAbsRect(), rootView.getSelectionRect(),
- null /* viewInfo */);
- // Insert the <merge> as the new real root
- rootView.mParent = merge;
- merge.addChild(rootView);
- rootView = merge;
- }
-
- return Pair.of(rootView, null);
- }
- }
-
- private boolean hasMergeParent(UiViewElementNode rootNode) {
- UiElementNode rootParent = rootNode.getUiParent();
- return (rootParent instanceof UiViewElementNode
- && VIEW_MERGE.equals(rootParent.getDescriptor().getXmlName()));
- }
-
- /** Creates a {@link CanvasViewInfo} for a given {@link ViewInfo} but does not recurse */
- private CanvasViewInfo createView(CanvasViewInfo parent, ViewInfo root, int parentX,
- int parentY) {
- Object cookie = root.getCookie();
- UiViewElementNode node = null;
- if (cookie instanceof UiViewElementNode) {
- node = (UiViewElementNode) cookie;
- } else if (cookie instanceof MergeCookie) {
- cookie = ((MergeCookie) cookie).getCookie();
- if (cookie instanceof UiViewElementNode) {
- node = (UiViewElementNode) cookie;
- CanvasViewInfo view = createView(parent, root, parentX, parentY, node);
- if (root.getCookie() instanceof MergeCookie && view.mNodeSiblings == null) {
- List<CanvasViewInfo> v = mMergeNodeMap == null ?
- null : mMergeNodeMap.get(node);
- if (v != null) {
- v.add(view);
- } else {
- v = new ArrayList<CanvasViewInfo>();
- v.add(view);
- if (mMergeNodeMap == null) {
- mMergeNodeMap =
- new HashMap<UiViewElementNode, List<CanvasViewInfo>>();
- }
- mMergeNodeMap.put(node, v);
- }
- view.mNodeSiblings = v;
- }
-
- return view;
- }
- }
-
- return createView(parent, root, parentX, parentY, node);
- }
-
- /**
- * Creates a {@link CanvasViewInfo} for a given {@link ViewInfo} but does not recurse.
- * This method specifies an explicit {@link UiViewElementNode} to use rather than
- * relying on the view cookie in the info object.
- */
- private CanvasViewInfo createView(CanvasViewInfo parent, ViewInfo root, int parentX,
- int parentY, UiViewElementNode node) {
-
- int x = root.getLeft();
- int y = root.getTop();
- int w = root.getRight() - x;
- int h = root.getBottom() - y;
-
- x += parentX;
- y += parentY;
-
- Rectangle absRect = new Rectangle(x, y, w - 1, h - 1);
-
- if (w < SELECTION_MIN_SIZE) {
- int d = (SELECTION_MIN_SIZE - w) / 2;
- x -= d;
- w += SELECTION_MIN_SIZE - w;
- }
-
- if (h < SELECTION_MIN_SIZE) {
- int d = (SELECTION_MIN_SIZE - h) / 2;
- y -= d;
- h += SELECTION_MIN_SIZE - h;
- }
-
- Rectangle selectionRect = new Rectangle(x, y, w - 1, h - 1);
-
- return new CanvasViewInfo(parent, root.getClassName(), root.getViewObject(), node,
- absRect, selectionRect, root);
- }
-
- /** Create a subtree recursively until you run out of keys */
- private CanvasViewInfo createSubtree(CanvasViewInfo parent, ViewInfo viewInfo,
- int parentX, int parentY) {
- assert viewInfo.getCookie() != null;
-
- CanvasViewInfo view = createView(parent, viewInfo, parentX, parentY);
- // Bug workaround: Ensure that we never have a child node identical
- // to its parent node: this can happen for example when rendering a
- // ZoomControls view where the merge cookies point to the parent.
- if (parent != null && view.mUiViewNode == parent.mUiViewNode) {
- return null;
- }
-
- // Process children:
- parentX += viewInfo.getLeft();
- parentY += viewInfo.getTop();
-
- List<ViewInfo> children = viewInfo.getChildren();
-
- if (mLayoutLib5) {
- for (ViewInfo child : children) {
- Object cookie = child.getCookie();
- if (cookie instanceof UiViewElementNode || cookie instanceof MergeCookie) {
- CanvasViewInfo childView = createSubtree(view, child,
- parentX, parentY);
- if (childView != null) {
- view.addChild(childView);
- }
- } // else: null cookies, adapter item references, etc: No child views.
- }
-
- return view;
- }
-
- // See if we have any missing keys at this level
- int missingNodes = 0;
- int mergeNodes = 0;
- for (ViewInfo child : children) {
- // Only use children which have a ViewKey of the correct type.
- // We can't interact with those when they have a null key or
- // an incompatible type.
- Object cookie = child.getCookie();
- if (!(cookie instanceof UiViewElementNode)) {
- if (cookie instanceof MergeCookie) {
- mergeNodes++;
- } else {
- missingNodes++;
- }
- }
- }
-
- if (missingNodes == 0 && mergeNodes == 0) {
- // No missing nodes; this is the normal case, and we can just continue to
- // recursively add our children
- for (ViewInfo child : children) {
- CanvasViewInfo childView = createSubtree(view, child,
- parentX, parentY);
- view.addChild(childView);
- }
-
- // TBD: Emit placeholder views for keys that have no views?
- } else {
- // We don't have keys for one or more of the ViewInfos. There are many
- // possible causes: we are on an SDK platform that does not support
- // embedded_layout rendering, or we are including a view with a <merge>
- // as the root element.
-
- UiViewElementNode uiViewNode = view.getUiViewNode();
- String containerName = uiViewNode != null
- ? uiViewNode.getDescriptor().getXmlLocalName() : ""; //$NON-NLS-1$
- if (containerName.equals(SdkConstants.VIEW_INCLUDE)) {
- // This is expected -- we don't WANT to get node keys for the content
- // of an include since it's in a different file and should be treated
- // as a single unit that cannot be edited (hence, no CanvasViewInfo
- // children)
- } else {
- // We are getting children with null keys where we don't expect it;
- // this usually means that we are dealing with an Android platform
- // that does not support {@link Capability#EMBEDDED_LAYOUT}, or
- // that there are <merge> tags which are doing surprising things
- // to the view hierarchy
- LinkedList<UiViewElementNode> unused = new LinkedList<UiViewElementNode>();
- if (uiViewNode != null) {
- for (UiElementNode child : uiViewNode.getUiChildren()) {
- if (child instanceof UiViewElementNode) {
- unused.addLast((UiViewElementNode) child);
- }
- }
- }
- for (ViewInfo child : children) {
- Object cookie = child.getCookie();
- if (mergeNodes > 0 && cookie instanceof MergeCookie) {
- cookie = ((MergeCookie) cookie).getCookie();
- }
- if (cookie != null) {
- unused.remove(cookie);
- }
- }
-
- if (unused.size() > 0 || mergeNodes > 0) {
- if (unused.size() == missingNodes) {
- // The number of unmatched elements and ViewInfos are identical;
- // it's very likely that they match one to one, so just use these
- for (ViewInfo child : children) {
- if (child.getCookie() == null) {
- // Only create a flat (non-recursive) view
- CanvasViewInfo childView = createView(view, child, parentX,
- parentY, unused.removeFirst());
- view.addChild(childView);
- } else {
- CanvasViewInfo childView = createSubtree(view, child, parentX,
- parentY);
- view.addChild(childView);
- }
- }
- } else {
- // We have an uneven match. In this case we might be dealing
- // with <merge> etc.
- // We have no way to associate elements back with the
- // corresponding <include> tags if there are more than one of
- // them. That's not a huge tragedy since visually you are not
- // allowed to edit these anyway; we just need to make a visual
- // block for these for selection and outline purposes.
- addMismatched(view, parentX, parentY, children, unused);
- }
- } else {
- // No unused keys, but there are views without keys.
- // We can't represent these since all views must have node keys
- // such that you can operate on them. Just ignore these.
- for (ViewInfo child : children) {
- if (child.getCookie() != null) {
- CanvasViewInfo childView = createSubtree(view, child,
- parentX, parentY);
- view.addChild(childView);
- }
- }
- }
- }
- }
-
- return view;
- }
-
- /**
- * We have various {@link ViewInfo} children with null keys, and/or nodes in
- * the corresponding UI model that are not referenced by any of the {@link ViewInfo}
- * objects. This method attempts to account for this, by matching the views in
- * the right order.
- */
- private void addMismatched(CanvasViewInfo parentView, int parentX, int parentY,
- List<ViewInfo> children, LinkedList<UiViewElementNode> unused) {
- UiViewElementNode afterNode = null;
- UiViewElementNode beforeNode = null;
- // We have one important clue we can use when matching unused nodes
- // with views: if we have a view V1 with node N1, and a view V2 with node N2,
- // then we can only match unknown node UN with unknown node UV if
- // V1 < UV < V2 and N1 < UN < N2.
- // We can use these constraints to do the matching, for example by
- // a simple DAG traversal. However, since the number of unmatched nodes
- // will typically be very small, we'll just do a simple algorithm here
- // which checks forwards/backwards whether a match is valid.
- for (int index = 0, size = children.size(); index < size; index++) {
- ViewInfo child = children.get(index);
- if (child.getCookie() != null) {
- CanvasViewInfo childView = createSubtree(parentView, child, parentX, parentY);
- if (childView != null) {
- parentView.addChild(childView);
- }
- if (child.getCookie() instanceof UiViewElementNode) {
- afterNode = (UiViewElementNode) child.getCookie();
- }
- } else {
- beforeNode = nextViewNode(children, index);
-
- // Find first eligible node from unused
- // TOD: What if there are more eligible? We need to process ALL views
- // and all nodes in one go here
-
- UiViewElementNode matching = null;
- for (UiViewElementNode candidate : unused) {
- if (afterNode == null || isAfter(afterNode, candidate)) {
- if (beforeNode == null || isBefore(beforeNode, candidate)) {
- matching = candidate;
- break;
- }
- }
- }
-
- if (matching != null) {
- unused.remove(matching);
- CanvasViewInfo childView = createView(parentView, child, parentX, parentY,
- matching);
- parentView.addChild(childView);
- afterNode = matching;
- } else {
- // We have no node for the view -- what do we do??
- // Nothing - we only represent stuff in the outline that is in the
- // source model, not in the render
- }
- }
- }
-
- // Add zero-bounded boxes for all remaining nodes since they need to show
- // up in the outline, need to be selectable so you can press Delete, etc.
- if (unused.size() > 0) {
- Map<UiViewElementNode, Integer> rankMap =
- new HashMap<UiViewElementNode, Integer>();
- Map<UiViewElementNode, CanvasViewInfo> infoMap =
- new HashMap<UiViewElementNode, CanvasViewInfo>();
- UiElementNode parent = unused.get(0).getUiParent();
- if (parent != null) {
- int index = 0;
- for (UiElementNode child : parent.getUiChildren()) {
- UiViewElementNode node = (UiViewElementNode) child;
- rankMap.put(node, index++);
- }
- for (CanvasViewInfo child : parentView.getChildren()) {
- infoMap.put(child.getUiViewNode(), child);
- }
- List<Integer> usedIndexes = new ArrayList<Integer>();
- for (UiViewElementNode node : unused) {
- Integer rank = rankMap.get(node);
- if (rank != null) {
- usedIndexes.add(rank);
- }
- }
- Collections.sort(usedIndexes);
- for (int i = usedIndexes.size() - 1; i >= 0; i--) {
- Integer rank = usedIndexes.get(i);
- UiViewElementNode found = null;
- for (UiViewElementNode node : unused) {
- if (rankMap.get(node) == rank) {
- found = node;
- break;
- }
- }
- if (found != null) {
- Rectangle absRect = new Rectangle(parentX, parentY, 0, 0);
- String name = found.getDescriptor().getXmlLocalName();
- CanvasViewInfo v = new CanvasViewInfo(parentView, name, null, found,
- absRect, absRect, null /* viewInfo */);
- // Find corresponding index in the parent view
- List<CanvasViewInfo> siblings = parentView.getChildren();
- int insertPosition = siblings.size();
- for (int j = siblings.size() - 1; j >= 0; j--) {
- CanvasViewInfo sibling = siblings.get(j);
- UiViewElementNode siblingNode = sibling.getUiViewNode();
- if (siblingNode != null) {
- Integer siblingRank = rankMap.get(siblingNode);
- if (siblingRank != null && siblingRank < rank) {
- insertPosition = j + 1;
- break;
- }
- }
- }
- parentView.addChildAt(insertPosition, v);
- unused.remove(found);
- }
- }
- }
- // Add in any remaining
- for (UiViewElementNode node : unused) {
- Rectangle absRect = new Rectangle(parentX, parentY, 0, 0);
- String name = node.getDescriptor().getXmlLocalName();
- CanvasViewInfo v = new CanvasViewInfo(parentView, name, null, node, absRect,
- absRect, null /* viewInfo */);
- parentView.addChild(v);
- }
- }
- }
-
- private boolean isBefore(UiViewElementNode beforeNode, UiViewElementNode candidate) {
- UiElementNode parent = candidate.getUiParent();
- if (parent != null) {
- for (UiElementNode sibling : parent.getUiChildren()) {
- if (sibling == beforeNode) {
- return false;
- } else if (sibling == candidate) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isAfter(UiViewElementNode afterNode, UiViewElementNode candidate) {
- UiElementNode parent = candidate.getUiParent();
- if (parent != null) {
- for (UiElementNode sibling : parent.getUiChildren()) {
- if (sibling == afterNode) {
- return true;
- } else if (sibling == candidate) {
- return false;
- }
- }
- }
- return false;
- }
-
- private UiViewElementNode nextViewNode(List<ViewInfo> children, int index) {
- int size = children.size();
- for (; index < size; index++) {
- ViewInfo child = children.get(index);
- if (child.getCookie() instanceof UiViewElementNode) {
- return (UiViewElementNode) child.getCookie();
- }
- }
-
- return null;
- }
-
- /** Search for a subtree with valid keys and add those subtrees */
- private CanvasViewInfo addKeyedSubtrees(CanvasViewInfo parent, ViewInfo viewInfo,
- int parentX, int parentY) {
- // We don't include MergeCookies when searching down for the first non-null key,
- // since this means we are in a "Show Included In" context, and the include tag itself
- // (which the merge cookie is pointing to) is still in the including-document rather
- // than the included document. Therefore, we only accept real UiViewElementNodes here,
- // not MergeCookies.
- if (viewInfo.getCookie() != null) {
- CanvasViewInfo subtree = createSubtree(parent, viewInfo, parentX, parentY);
- if (parent != null && subtree != null) {
- parent.mChildren.add(subtree);
- }
- return subtree;
- } else {
- for (ViewInfo child : viewInfo.getChildren()) {
- addKeyedSubtrees(parent, child, parentX + viewInfo.getLeft(), parentY
- + viewInfo.getTop());
- }
-
- return null;
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java
deleted file mode 100644
index 263456984..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ClipboardSupport.java
+++ /dev/null
@@ -1,429 +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_NS_NAME;
-import static com.android.SdkConstants.NS_RESOURCES;
-import static com.android.SdkConstants.XMLNS_URI;
-
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IDragElement.IDragAttribute;
-import com.android.ide.common.api.INode;
-import com.android.ide.eclipse.adt.AdtPlugin;
-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.descriptors.ViewElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-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.swt.custom.StyledText;
-import org.eclipse.swt.dnd.Clipboard;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.widgets.Composite;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The {@link ClipboardSupport} class manages the native clipboard, providing operations
- * to copy, cut and paste view items, and can answer whether the clipboard contains
- * a transferable we care about.
- */
-public class ClipboardSupport {
- private static final boolean DEBUG = false;
-
- /** SWT clipboard instance. */
- private Clipboard mClipboard;
- private LayoutCanvas mCanvas;
-
- /**
- * Constructs a new {@link ClipboardSupport} tied to the given
- * {@link LayoutCanvas}.
- *
- * @param canvas The {@link LayoutCanvas} to provide clipboard support for.
- * @param parent The parent widget in the SWT hierarchy of the canvas.
- */
- public ClipboardSupport(LayoutCanvas canvas, Composite parent) {
- mCanvas = canvas;
-
- mClipboard = new Clipboard(parent.getDisplay());
- }
-
- /**
- * Frees up any resources held by the {@link ClipboardSupport}.
- */
- public void dispose() {
- if (mClipboard != null) {
- mClipboard.dispose();
- mClipboard = null;
- }
- }
-
- /**
- * Perform the "Copy" action, either from the Edit menu or from the context
- * menu.
- * <p/>
- * This sanitizes the selection, so it must be a copy. It then inserts the
- * selection both as text and as {@link SimpleElement}s in the clipboard.
- * (If there is selected text in the error label, then the error is used
- * as the text portion of the transferable.)
- *
- * @param selection A list of selection items to add to the clipboard;
- * <b>this should be a copy already - this method will not make a
- * copy</b>
- */
- public void copySelectionToClipboard(List<SelectionItem> selection) {
- SelectionManager.sanitize(selection);
-
- // The error message area shares the copy action with the canvas. Invoking the
- // copy action when there are errors visible *AND* the user has selected text there,
- // should include the error message as the text transferable.
- String message = null;
- GraphicalEditorPart graphicalEditor = mCanvas.getEditorDelegate().getGraphicalEditor();
- StyledText errorLabel = graphicalEditor.getErrorLabel();
- if (errorLabel.getSelectionCount() > 0) {
- message = errorLabel.getSelectionText();
- }
-
- if (selection.isEmpty()) {
- if (message != null) {
- mClipboard.setContents(
- new Object[] { message },
- new Transfer[] { TextTransfer.getInstance() }
- );
- }
- return;
- }
-
- Object[] data = new Object[] {
- SelectionItem.getAsElements(selection),
- message != null ? message : SelectionItem.getAsText(mCanvas, selection)
- };
-
- Transfer[] types = new Transfer[] {
- SimpleXmlTransfer.getInstance(),
- TextTransfer.getInstance()
- };
-
- mClipboard.setContents(data, types);
- }
-
- /**
- * Perform the "Cut" action, either from the Edit menu or from the context
- * menu.
- * <p/>
- * This sanitizes the selection, so it must be a copy. It uses the
- * {@link #copySelectionToClipboard(List)} method to copy the selection to
- * the clipboard. Finally it uses {@link #deleteSelection(String, List)} to
- * delete the selection with a "Cut" verb for the title.
- *
- * @param selection A list of selection items to add to the clipboard;
- * <b>this should be a copy already - this method will not make a
- * copy</b>
- */
- public void cutSelectionToClipboard(List<SelectionItem> selection) {
- copySelectionToClipboard(selection);
- deleteSelection(mCanvas.getCutLabel(), selection);
- }
-
- /**
- * Deletes the given selection.
- *
- * @param verb A translated verb for the action. Will be used for the
- * undo/redo title. Typically this should be
- * {@link Action#getText()} for either the cut or the delete
- * actions in the canvas.
- * @param selection The selection. Must not be null. Can be empty, in which
- * case nothing happens. The selection list will be sanitized so
- * the caller should pass in a copy.
- */
- public void deleteSelection(String verb, final List<SelectionItem> selection) {
- SelectionManager.sanitize(selection);
-
- if (selection.isEmpty()) {
- return;
- }
-
- // If all selected items have the same *kind* of parent, display that in the undo title.
- String title = null;
- for (SelectionItem cs : selection) {
- CanvasViewInfo vi = cs.getViewInfo();
- if (vi != null && vi.getParent() != null) {
- CanvasViewInfo parent = vi.getParent();
- assert parent != null;
- if (title == null) {
- title = parent.getName();
- } else if (!title.equals(parent.getName())) {
- // More than one kind of parent selected.
- title = null;
- break;
- }
- }
- }
-
- if (title != null) {
- // Typically the name is an FQCN. Just get the last segment.
- int pos = title.lastIndexOf('.');
- if (pos > 0 && pos < title.length() - 1) {
- title = title.substring(pos + 1);
- }
- }
- boolean multiple = mCanvas.getSelectionManager().hasMultiSelection();
- if (title == null) {
- title = String.format(
- multiple ? "%1$s elements" : "%1$s element",
- verb);
- } else {
- title = String.format(
- multiple ? "%1$s elements from %2$s" : "%1$s element from %2$s",
- verb, title);
- }
-
- // Implementation note: we don't clear the internal selection after removing
- // the elements. An update XML model event should happen when the model gets released
- // which will trigger a recompute of the layout, thus reloading the model thus
- // resetting the selection.
- mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(title, new Runnable() {
- @Override
- public void run() {
- // Segment the deleted nodes into clusters of siblings
- Map<NodeProxy, List<INode>> clusters =
- new HashMap<NodeProxy, List<INode>>();
- for (SelectionItem cs : selection) {
- NodeProxy node = cs.getNode();
- if (node == null) {
- continue;
- }
- INode parent = node.getParent();
- if (parent != null) {
- List<INode> children = clusters.get(parent);
- if (children == null) {
- children = new ArrayList<INode>();
- clusters.put((NodeProxy) parent, children);
- }
- children.add(node);
- }
- }
-
- // Notify parent views about children getting deleted
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- for (Map.Entry<NodeProxy, List<INode>> entry : clusters.entrySet()) {
- NodeProxy parent = entry.getKey();
- List<INode> children = entry.getValue();
- assert children != null && children.size() > 0;
- rulesEngine.callOnRemovingChildren(parent, children);
- parent.applyPendingChanges();
- }
-
- for (SelectionItem cs : selection) {
- CanvasViewInfo vi = cs.getViewInfo();
- // You can't delete the root element
- if (vi != null && !vi.isRoot()) {
- UiViewElementNode ui = vi.getUiViewNode();
- if (ui != null) {
- ui.deleteXmlNode();
- }
- }
- }
- }
- });
- }
-
- /**
- * Perform the "Paste" action, either from the Edit menu or from the context
- * menu.
- *
- * @param selection A list of selection items to add to the clipboard;
- * <b>this should be a copy already - this method will not make a
- * copy</b>
- */
- public void pasteSelection(List<SelectionItem> selection) {
-
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
- final SimpleElement[] pasted = (SimpleElement[]) mClipboard.getContents(sxt);
-
- if (pasted == null || pasted.length == 0) {
- return;
- }
-
- CanvasViewInfo lastRoot = mCanvas.getViewHierarchy().getRoot();
- if (lastRoot == null) {
- // Pasting in an empty document. Only paste the first element.
- pasteInEmptyDocument(pasted[0]);
- return;
- }
-
- // Otherwise use the current selection, if any, as a guide where to paste
- // using the first selected element only. If there's no selection use
- // the root as the insertion point.
- SelectionManager.sanitize(selection);
- final CanvasViewInfo target;
- if (selection.size() > 0) {
- SelectionItem cs = selection.get(0);
- target = cs.getViewInfo();
- } else {
- target = lastRoot;
- }
-
- final NodeProxy targetNode = mCanvas.getNodeFactory().create(target);
- mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel("Paste", new Runnable() {
- @Override
- public void run() {
- RulesEngine engine = mCanvas.getRulesEngine();
- NodeProxy node = engine.callOnPaste(targetNode, target.getViewObject(), pasted);
- node.applyPendingChanges();
- }
- });
- }
-
- /**
- * Paste a new root into an empty XML layout.
- * <p/>
- * In case of error (unknown FQCN, document not empty), silently do nothing.
- * In case of success, the new element will have some default attributes set (xmlns:android,
- * layout_width and height). The edit is wrapped in a proper undo.
- * <p/>
- * Implementation is similar to {@link #createDocumentRoot} except we also
- * copy all the attributes and inner elements recursively.
- */
- private void pasteInEmptyDocument(final IDragElement pastedElement) {
- String rootFqcn = pastedElement.getFqcn();
-
- // Need a valid empty document to create the new root
- final LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- final UiDocumentNode uiDoc = delegate.getUiRootNode();
- if (uiDoc == null || uiDoc.getUiChildren().size() > 0) {
- debugPrintf("Failed to paste document root for %1$s: document is not empty", rootFqcn);
- return;
- }
-
- // Find the view descriptor matching our FQCN
- final ViewElementDescriptor viewDesc = delegate.getFqcnViewDescriptor(rootFqcn);
- if (viewDesc == null) {
- // TODO this could happen if pasting a custom view not known in this project
- debugPrintf("Failed to paste document root, unknown FQCN %1$s", rootFqcn);
- return;
- }
-
- // Get the last segment of the FQCN for the undo title
- String title = rootFqcn;
- int pos = title.lastIndexOf('.');
- if (pos > 0 && pos < title.length() - 1) {
- title = title.substring(pos + 1);
- }
- title = String.format("Paste root %1$s in document", title);
-
- delegate.getEditor().wrapUndoEditXmlModel(title, new Runnable() {
- @Override
- public void run() {
- UiElementNode uiNew = uiDoc.appendNewUiChild(viewDesc);
-
- // A root node requires the Android XMLNS
- uiNew.setAttributeValue(ANDROID_NS_NAME, XMLNS_URI, NS_RESOURCES,
- true /*override*/);
-
- // Copy all the attributes from the pasted element
- for (IDragAttribute attr : pastedElement.getAttributes()) {
- uiNew.setAttributeValue(
- attr.getName(),
- attr.getUri(),
- attr.getValue(),
- true /*override*/);
- }
-
- // Adjust the attributes, adding the default layout_width/height
- // only if they are not present (the original element should have
- // them though.)
- DescriptorsUtils.setDefaultLayoutAttributes(uiNew, false /*updateLayout*/);
-
- uiNew.createXmlNode();
-
- // Now process all children
- for (IDragElement childElement : pastedElement.getInnerElements()) {
- addChild(uiNew, childElement);
- }
- }
-
- private void addChild(UiElementNode uiParent, IDragElement childElement) {
- String childFqcn = childElement.getFqcn();
- final ViewElementDescriptor childDesc =
- delegate.getFqcnViewDescriptor(childFqcn);
- if (childDesc == null) {
- // TODO this could happen if pasting a custom view
- debugPrintf("Failed to paste element, unknown FQCN %1$s", childFqcn);
- return;
- }
-
- UiElementNode uiChild = uiParent.appendNewUiChild(childDesc);
-
- // Copy all the attributes from the pasted element
- for (IDragAttribute attr : childElement.getAttributes()) {
- uiChild.setAttributeValue(
- attr.getName(),
- attr.getUri(),
- attr.getValue(),
- true /*override*/);
- }
-
- // Adjust the attributes, adding the default layout_width/height
- // only if they are not present (the original element should have
- // them though.)
- DescriptorsUtils.setDefaultLayoutAttributes(
- uiChild, false /*updateLayout*/);
-
- uiChild.createXmlNode();
-
- // Now process all grand children
- for (IDragElement grandChildElement : childElement.getInnerElements()) {
- addChild(uiChild, grandChildElement);
- }
- }
- });
- }
-
- /**
- * Returns true if we have a a simple xml transfer data object on the
- * clipboard.
- *
- * @return True if and only if the clipboard contains one of XML element
- * objects.
- */
- public boolean hasSxtOnClipboard() {
- // The paste operation is only available if we can paste our custom type.
- // We do not currently support pasting random text (e.g. XML). Maybe later.
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
- for (TransferData td : mClipboard.getAvailableTypes()) {
- if (sxt.isSupportedType(td)) {
- return true;
- }
- }
-
- return false;
- }
-
- private void debugPrintf(String message, Object... params) {
- if (DEBUG) AdtPlugin.printToConsole("Clipboard", String.format(message, params));
- }
-
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java
deleted file mode 100644
index 55930f6cd..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPoint.java
+++ /dev/null
@@ -1,195 +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 org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.events.MenuDetectEvent;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.graphics.Point;
-
-/**
- * A {@link ControlPoint} is a coordinate in the canvas control which corresponds
- * exactly to (0,0) at the top left of the canvas. It is unaffected by canvas
- * zooming.
- */
-public final class ControlPoint {
- /** Containing canvas which the point is relative to. */
- private final LayoutCanvas mCanvas;
-
- /** The X coordinate of the mouse coordinate. */
- public final int x;
-
- /** The Y coordinate of the mouse coordinate. */
- public final int y;
-
- /**
- * Constructs a new {@link ControlPoint} from the given event. The event
- * must be from a {@link MouseListener} associated with the
- * {@link LayoutCanvas} such that the {@link MouseEvent#x} and
- * {@link MouseEvent#y} fields are relative to the canvas.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The mouse event to construct the {@link ControlPoint}
- * from.
- * @return A {@link ControlPoint} which corresponds to the given
- * {@link MouseEvent}.
- */
- public static ControlPoint create(LayoutCanvas canvas, MouseEvent event) {
- // The mouse event coordinates should already be relative to the canvas
- // widget.
- assert event.widget == canvas : event.widget;
- return new ControlPoint(canvas, event.x, event.y);
- }
-
- /**
- * Constructs a new {@link ControlPoint} from the given menu detect event.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The menu detect event to construct the {@link ControlPoint} from.
- * @return A {@link ControlPoint} which corresponds to the given
- * {@link MenuDetectEvent}.
- */
- public static ControlPoint create(LayoutCanvas canvas, MenuDetectEvent event) {
- // The menu detect events are always display-relative.
- org.eclipse.swt.graphics.Point p = canvas.toControl(event.x, event.y);
- return new ControlPoint(canvas, p.x, p.y);
- }
-
- /**
- * Constructs a new {@link ControlPoint} from the given event. The event
- * must be from a {@link DragSourceListener} associated with the
- * {@link LayoutCanvas} such that the {@link DragSourceEvent#x} and
- * {@link DragSourceEvent#y} fields are relative to the canvas.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The mouse event to construct the {@link ControlPoint}
- * from.
- * @return A {@link ControlPoint} which corresponds to the given
- * {@link DragSourceEvent}.
- */
- public static ControlPoint create(LayoutCanvas canvas, DragSourceEvent event) {
- // The drag source event coordinates should already be relative to the
- // canvas widget.
- return new ControlPoint(canvas, event.x, event.y);
- }
-
- /**
- * Constructs a new {@link ControlPoint} from the given event.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The mouse event to construct the {@link ControlPoint}
- * from.
- * @return A {@link ControlPoint} which corresponds to the given
- * {@link DropTargetEvent}.
- */
- public static ControlPoint create(LayoutCanvas canvas, DropTargetEvent event) {
- // The drop target events are always relative to the display, so we must
- // first convert them to be canvas relative.
- org.eclipse.swt.graphics.Point p = canvas.toControl(event.x, event.y);
- return new ControlPoint(canvas, p.x, p.y);
- }
-
- /**
- * Constructs a new {@link ControlPoint} from the given x,y coordinates,
- * which must be relative to the given {@link LayoutCanvas}.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param x The mouse event x coordinate relative to the canvas
- * @param y The mouse event x coordinate relative to the canvas
- * @return A {@link ControlPoint} which corresponds to the given
- * coordinates.
- */
- public static ControlPoint create(LayoutCanvas canvas, int x, int y) {
- return new ControlPoint(canvas, x, y);
- }
-
- /**
- * Constructs a new canvas control coordinate with the given X and Y
- * coordinates. This is private; use one of the factory methods
- * {@link #create(LayoutCanvas, MouseEvent)},
- * {@link #create(LayoutCanvas, DragSourceEvent)} or
- * {@link #create(LayoutCanvas, DropTargetEvent)} instead.
- *
- * @param canvas The canvas which contains this coordinate
- * @param x The mouse x coordinate
- * @param y The mouse y coordinate
- */
- private ControlPoint(LayoutCanvas canvas, int x, int y) {
- mCanvas = canvas;
- this.x = x;
- this.y = y;
- }
-
- /**
- * Returns the equivalent {@link LayoutPoint} to this
- * {@link ControlPoint}.
- *
- * @return The equivalent {@link LayoutPoint} to this
- * {@link ControlPoint}.
- */
- public LayoutPoint toLayout() {
- int lx = mCanvas.getHorizontalTransform().inverseTranslate(x);
- int ly = mCanvas.getVerticalTransform().inverseTranslate(y);
-
- return LayoutPoint.create(mCanvas, lx, ly);
- }
-
- @Override
- public String toString() {
- return "ControlPoint [x=" + x + ", y=" + y + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + x;
- result = prime * result + y;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- ControlPoint other = (ControlPoint) obj;
- if (x != other.x)
- return false;
- if (y != other.y)
- return false;
- if (mCanvas != other.mCanvas) {
- return false;
- }
- return true;
- }
-
- /**
- * Returns this point as an SWT point in the display coordinate system
- *
- * @return this point as an SWT point in the display coordinate system
- */
- public Point toDisplayPoint() {
- return mCanvas.toDisplay(x, y);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CreateNewConfigJob.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CreateNewConfigJob.java
deleted file mode 100644
index 44cd0810f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CreateNewConfigJob.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2012 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 com.android.annotations.NonNull;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.resources.ResourceFolderType;
-import com.google.common.base.Charsets;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.IWorkspaceRoot;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PartInitException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-/** Job which creates a new layout file for a given configuration */
-class CreateNewConfigJob extends Job {
- private final GraphicalEditorPart mEditor;
- private final IFile mFromFile;
- private final FolderConfiguration mConfig;
-
- CreateNewConfigJob(
- @NonNull GraphicalEditorPart editor,
- @NonNull IFile fromFile,
- @NonNull FolderConfiguration config) {
- super("Create Alternate Layout");
- mEditor = editor;
- mFromFile = fromFile;
- mConfig = config;
- }
-
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- // get the folder name
- String folderName = mConfig.getFolderName(ResourceFolderType.LAYOUT);
- try {
- // look to see if it exists.
- // get the res folder
- IFolder res = (IFolder) mFromFile.getParent().getParent();
-
- IFolder newParentFolder = res.getFolder(folderName);
- AdtUtils.ensureExists(newParentFolder);
- final IFile file = newParentFolder.getFile(mFromFile.getName());
- if (file.exists()) {
- String message = String.format("File 'res/%1$s/%2$s' already exists!",
- folderName, mFromFile.getName());
- return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
- }
-
- // Read current document contents instead of from file: mFromFile.getContents()
- String text = mEditor.getEditorDelegate().getEditor().getStructuredDocument().get();
- ByteArrayInputStream input = new ByteArrayInputStream(text.getBytes(Charsets.UTF_8));
- file.create(input, false, monitor);
- input.close();
-
- // Ensure that the project resources updates itself to notice the new configuration.
- // In theory, this shouldn't be necessary, but we need to make sure the
- // resource manager knows about this immediately such that the call below
- // to find the best configuration takes the new folder into account.
- ResourceManager resourceManager = ResourceManager.getInstance();
- IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
- IFolder folder = root.getFolder(newParentFolder.getFullPath());
- resourceManager.getResourceFolder(folder);
-
- // Switch to the new file
- Display display = mEditor.getConfigurationChooser().getDisplay();
- display.asyncExec(new Runnable() {
- @Override
- public void run() {
- // The given old layout has been forked into a new layout
- // for a given configuration. This means that the old layout
- // is no longer a match for the configuration, which is
- // probably what it is still showing. We have to modify
- // its configuration to no longer be an impossible
- // configuration.
- ConfigurationChooser chooser = mEditor.getConfigurationChooser();
- chooser.onAlternateLayoutCreated();
-
- // Finally open the new layout
- try {
- AdtPlugin.openFile(file, null, false);
- } catch (PartInitException e) {
- AdtPlugin.log(e, null);
- }
- }
- });
- } catch (IOException e2) {
- String message = String.format(
- "Failed to create File 'res/%1$s/%2$s' : %3$s",
- folderName, mFromFile.getName(), e2.getMessage());
- AdtPlugin.displayError("Layout Creation", message);
-
- return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
- message, e2);
- } catch (CoreException e2) {
- String message = String.format(
- "Failed to create File 'res/%1$s/%2$s' : %3$s",
- folderName, mFromFile.getName(), e2.getMessage());
- AdtPlugin.displayError("Layout Creation", message);
-
- return e2.getStatus();
- }
-
- return Status.OK_STATUS;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java
deleted file mode 100644
index 1f97c8c54..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CustomViewFinder.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright (C) 2011 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.CLASS_VIEW;
-import static com.android.SdkConstants.CLASS_VIEWGROUP;
-import static com.android.SdkConstants.FN_FRAMEWORK_LIBRARY;
-
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-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.IProject;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jdt.core.Flags;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.IPackageFragment;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.search.IJavaSearchConstants;
-import org.eclipse.jdt.core.search.IJavaSearchScope;
-import org.eclipse.jdt.core.search.SearchEngine;
-import org.eclipse.jdt.core.search.SearchMatch;
-import org.eclipse.jdt.core.search.SearchParticipant;
-import org.eclipse.jdt.core.search.SearchPattern;
-import org.eclipse.jdt.core.search.SearchRequestor;
-import org.eclipse.jdt.internal.core.ResolvedBinaryType;
-import org.eclipse.jdt.internal.core.ResolvedSourceType;
-import org.eclipse.swt.widgets.Display;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The {@link CustomViewFinder} can look up the custom views and third party views
- * available for a given project.
- */
-@SuppressWarnings("restriction") // JDT model access for custom-view class lookup
-public class CustomViewFinder {
- /**
- * Qualified name for the per-project non-persistent property storing the
- * {@link CustomViewFinder} for this project
- */
- private final static QualifiedName CUSTOM_VIEW_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
- "viewfinder"); //$NON-NLS-1$
-
- /** Project that this view finder locates views for */
- private final IProject mProject;
-
- private final List<Listener> mListeners = new ArrayList<Listener>();
-
- private List<String> mCustomViews;
- private List<String> mThirdPartyViews;
- private boolean mRefreshing;
-
- /**
- * Constructs an {@link CustomViewFinder} for the given project. Don't use this method;
- * use the {@link #get} factory method instead.
- *
- * @param project project to create an {@link CustomViewFinder} for
- */
- private CustomViewFinder(IProject project) {
- mProject = project;
- }
-
- /**
- * Returns the {@link CustomViewFinder} for the given project
- *
- * @param project the project the finder is associated with
- * @return a {@CustomViewFinder} for the given project, never null
- */
- public static CustomViewFinder get(IProject project) {
- CustomViewFinder finder = null;
- try {
- finder = (CustomViewFinder) project.getSessionProperty(CUSTOM_VIEW_FINDER);
- } catch (CoreException e) {
- // Not a problem; we will just create a new one
- }
-
- if (finder == null) {
- finder = new CustomViewFinder(project);
- try {
- project.setSessionProperty(CUSTOM_VIEW_FINDER, finder);
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't store CustomViewFinder");
- }
- }
-
- return finder;
- }
-
- public void refresh() {
- refresh(null /*listener*/, true /* sync */);
- }
-
- public void refresh(final Listener listener) {
- refresh(listener, false /* sync */);
- }
-
- private void refresh(final Listener listener, boolean sync) {
- // Add this listener to the list of listeners which should be notified when the
- // search is done. (There could be more than one since multiple requests could
- // arrive for a slow search since the search is run in a different thread).
- if (listener != null) {
- synchronized (this) {
- mListeners.add(listener);
- }
- }
- synchronized (this) {
- if (listener != null) {
- mListeners.add(listener);
- }
- if (mRefreshing) {
- return;
- }
- mRefreshing = true;
- }
-
- FindViewsJob job = new FindViewsJob();
- job.schedule();
- if (sync) {
- try {
- job.join();
- } catch (InterruptedException e) {
- AdtPlugin.log(e, null);
- }
- }
- }
-
- public Collection<String> getCustomViews() {
- return mCustomViews == null ? null : Collections.unmodifiableCollection(mCustomViews);
- }
-
- public Collection<String> getThirdPartyViews() {
- return mThirdPartyViews == null
- ? null : Collections.unmodifiableCollection(mThirdPartyViews);
- }
-
- public Collection<String> getAllViews() {
- // Not yet initialized: return null
- if (mCustomViews == null) {
- return null;
- }
- List<String> all = new ArrayList<String>(mCustomViews.size() + mThirdPartyViews.size());
- all.addAll(mCustomViews);
- all.addAll(mThirdPartyViews);
- return all;
- }
-
- /**
- * Returns a pair of view lists - the custom views and the 3rd-party views.
- * This method performs no caching; it is the same as asking the custom view finder
- * to refresh itself and then waiting for the answer and returning it.
- *
- * @param project the Android project
- * @param layoutsOnly if true, only search for layouts
- * @return a pair of lists, the first containing custom views and the second
- * containing 3rd party views
- */
- public static Pair<List<String>,List<String>> findViews(
- final IProject project, boolean layoutsOnly) {
- CustomViewFinder finder = get(project);
-
- return finder.findViews(layoutsOnly);
- }
-
- private Pair<List<String>,List<String>> findViews(final boolean layoutsOnly) {
- final Set<String> customViews = new HashSet<String>();
- final Set<String> thirdPartyViews = new HashSet<String>();
-
- ProjectState state = Sdk.getProjectState(mProject);
- final List<IProject> libraries = state != null
- ? state.getFullLibraryProjects() : Collections.<IProject>emptyList();
-
- SearchRequestor requestor = new SearchRequestor() {
- @Override
- public void acceptSearchMatch(SearchMatch match) throws CoreException {
- // Ignore matches in comments
- if (match.isInsideDocComment()) {
- return;
- }
-
- Object element = match.getElement();
- if (element instanceof ResolvedBinaryType) {
- // Third party view
- ResolvedBinaryType type = (ResolvedBinaryType) element;
- IPackageFragment fragment = type.getPackageFragment();
- IPath path = fragment.getPath();
- String last = path.lastSegment();
- // Filter out android.jar stuff
- if (last.equals(FN_FRAMEWORK_LIBRARY)) {
- return;
- }
- if (!isValidView(type, layoutsOnly)) {
- return;
- }
-
- IProject matchProject = match.getResource().getProject();
- if (mProject == matchProject || libraries.contains(matchProject)) {
- String fqn = type.getFullyQualifiedName();
- thirdPartyViews.add(fqn);
- }
- } else if (element instanceof ResolvedSourceType) {
- // User custom view
- IProject matchProject = match.getResource().getProject();
- if (mProject == matchProject || libraries.contains(matchProject)) {
- ResolvedSourceType type = (ResolvedSourceType) element;
- if (!isValidView(type, layoutsOnly)) {
- return;
- }
- String fqn = type.getFullyQualifiedName();
- fqn = fqn.replace('$', '.');
- customViews.add(fqn);
- }
- }
- }
- };
- try {
- IJavaProject javaProject = BaseProjectHelper.getJavaProject(mProject);
- if (javaProject != null) {
- String className = layoutsOnly ? CLASS_VIEWGROUP : CLASS_VIEW;
- IType viewType = javaProject.findType(className);
- if (viewType != null) {
- IJavaSearchScope scope = SearchEngine.createHierarchyScope(viewType);
- SearchParticipant[] participants = new SearchParticipant[] {
- SearchEngine.getDefaultSearchParticipant()
- };
- int matchRule = SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE;
-
- SearchPattern pattern = SearchPattern.createPattern("*",
- IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
- matchRule);
- SearchEngine engine = new SearchEngine();
- engine.search(pattern, participants, scope, requestor,
- new NullProgressMonitor());
- }
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
-
-
- List<String> custom = new ArrayList<String>(customViews);
- List<String> thirdParty = new ArrayList<String>(thirdPartyViews);
-
- if (!layoutsOnly) {
- // Update our cached answers (unless we were filtered on only layouts)
- mCustomViews = custom;
- mThirdPartyViews = thirdParty;
- }
-
- return Pair.of(custom, thirdParty);
- }
-
- /**
- * Determines whether the given member is a valid android.view.View to be added to the
- * list of custom views or third party views. It checks that the view is public and
- * not abstract for example.
- */
- private static boolean isValidView(IType type, boolean layoutsOnly)
- throws JavaModelException {
- // Skip anonymous classes
- if (type.isAnonymous()) {
- return false;
- }
- int flags = type.getFlags();
- if (Flags.isAbstract(flags) || !Flags.isPublic(flags)) {
- return false;
- }
-
- // TODO: if (layoutsOnly) perhaps try to filter out AdapterViews and other ViewGroups
- // not willing to accept children via XML
-
- // See if the class has one of the acceptable constructors
- // needed for XML instantiation:
- // View(Context context)
- // View(Context context, AttributeSet attrs)
- // View(Context context, AttributeSet attrs, int defStyle)
- // We don't simply do three direct checks via type.getMethod() because the types
- // are not resolved, so we don't know for each parameter if we will get the
- // fully qualified or the unqualified class names.
- // Instead, iterate over the methods and look for a match.
- String typeName = type.getElementName();
- for (IMethod method : type.getMethods()) {
- // Only care about constructors
- if (!method.getElementName().equals(typeName)) {
- continue;
- }
-
- String[] parameterTypes = method.getParameterTypes();
- if (parameterTypes == null || parameterTypes.length < 1 || parameterTypes.length > 3) {
- continue;
- }
-
- String first = parameterTypes[0];
- // Look for the parameter type signatures -- produced by
- // JDT's Signature.createTypeSignature("Context", false /*isResolved*/);.
- // This is not a typo; they were copy/pasted from the actual parameter names
- // observed in the debugger examining these data structures.
- if (first.equals("QContext;") //$NON-NLS-1$
- || first.equals("Qandroid.content.Context;")) { //$NON-NLS-1$
- if (parameterTypes.length == 1) {
- return true;
- }
- String second = parameterTypes[1];
- if (second.equals("QAttributeSet;") //$NON-NLS-1$
- || second.equals("Qandroid.util.AttributeSet;")) { //$NON-NLS-1$
- if (parameterTypes.length == 2) {
- return true;
- }
- String third = parameterTypes[2];
- if (third.equals("I")) { //$NON-NLS-1$
- if (parameterTypes.length == 3) {
- return true;
- }
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * Interface implemented by clients of the {@link CustomViewFinder} to be notified
- * when a custom view search has completed. Will always be called on the SWT event
- * dispatch thread.
- */
- public interface Listener {
- void viewsUpdated(Collection<String> customViews, Collection<String> thirdPartyViews);
- }
-
- /**
- * Job for performing class search off the UI thread. This is marked as a system job
- * so that it won't show up in the progress monitor etc.
- */
- private class FindViewsJob extends Job {
- FindViewsJob() {
- super("Find Custom Views");
- setSystem(true);
- }
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- Pair<List<String>, List<String>> views = findViews(false);
- mCustomViews = views.getFirst();
- mThirdPartyViews = views.getSecond();
-
- // Notify listeners on SWT's UI thread
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- Collection<String> customViews =
- Collections.unmodifiableCollection(mCustomViews);
- Collection<String> thirdPartyViews =
- Collections.unmodifiableCollection(mThirdPartyViews);
- synchronized (this) {
- for (Listener l : mListeners) {
- l.viewsUpdated(customViews, thirdPartyViews);
- }
- mListeners.clear();
- mRefreshing = false;
- }
- }
- });
- return Status.OK_STATUS;
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java
deleted file mode 100644
index 7a41b5b15..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2012 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 com.android.annotations.NonNull;
-
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.IMenuCreator;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.util.IPropertyChangeListener;
-import org.eclipse.swt.events.HelpListener;
-import org.eclipse.swt.widgets.Event;
-
-/**
- * Implementation of {@link IAction} which delegates to a different
- * {@link IAction} which allows a subclass to wrap and customize some of the
- * behavior of a different action
- */
-public class DelegatingAction implements IAction {
- private final IAction mAction;
-
- /**
- * Construct a new delegate of the given action
- *
- * @param action the action to be delegated
- */
- public DelegatingAction(@NonNull IAction action) {
- mAction = action;
- }
-
- @Override
- public void addPropertyChangeListener(IPropertyChangeListener listener) {
- mAction.addPropertyChangeListener(listener);
- }
-
- @Override
- public int getAccelerator() {
- return mAction.getAccelerator();
- }
-
- @Override
- public String getActionDefinitionId() {
- return mAction.getActionDefinitionId();
- }
-
- @Override
- public String getDescription() {
- return mAction.getDescription();
- }
-
- @Override
- public ImageDescriptor getDisabledImageDescriptor() {
- return mAction.getDisabledImageDescriptor();
- }
-
- @Override
- public HelpListener getHelpListener() {
- return mAction.getHelpListener();
- }
-
- @Override
- public ImageDescriptor getHoverImageDescriptor() {
- return mAction.getHoverImageDescriptor();
- }
-
- @Override
- public String getId() {
- return mAction.getId();
- }
-
- @Override
- public ImageDescriptor getImageDescriptor() {
- return mAction.getImageDescriptor();
- }
-
- @Override
- public IMenuCreator getMenuCreator() {
- return mAction.getMenuCreator();
- }
-
- @Override
- public int getStyle() {
- return mAction.getStyle();
- }
-
- @Override
- public String getText() {
- return mAction.getText();
- }
-
- @Override
- public String getToolTipText() {
- return mAction.getToolTipText();
- }
-
- @Override
- public boolean isChecked() {
- return mAction.isChecked();
- }
-
- @Override
- public boolean isEnabled() {
- return mAction.isEnabled();
- }
-
- @Override
- public boolean isHandled() {
- return mAction.isHandled();
- }
-
- @Override
- public void removePropertyChangeListener(IPropertyChangeListener listener) {
- mAction.removePropertyChangeListener(listener);
- }
-
- @Override
- public void run() {
- mAction.run();
- }
-
- @Override
- public void runWithEvent(Event event) {
- mAction.runWithEvent(event);
- }
-
- @Override
- public void setActionDefinitionId(String id) {
- mAction.setActionDefinitionId(id);
- }
-
- @Override
- public void setChecked(boolean checked) {
- mAction.setChecked(checked);
- }
-
- @Override
- public void setDescription(String text) {
- mAction.setDescription(text);
- }
-
- @Override
- public void setDisabledImageDescriptor(ImageDescriptor newImage) {
- mAction.setDisabledImageDescriptor(newImage);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mAction.setEnabled(enabled);
- }
-
- @Override
- public void setHelpListener(HelpListener listener) {
- mAction.setHelpListener(listener);
- }
-
- @Override
- public void setHoverImageDescriptor(ImageDescriptor newImage) {
- mAction.setHoverImageDescriptor(newImage);
- }
-
- @Override
- public void setId(String id) {
- mAction.setId(id);
- }
-
- @Override
- public void setImageDescriptor(ImageDescriptor newImage) {
- mAction.setImageDescriptor(newImage);
- }
-
- @Override
- public void setMenuCreator(IMenuCreator creator) {
- mAction.setMenuCreator(creator);
- }
-
- @Override
- public void setText(String text) {
- mAction.setText(text);
- }
-
- @Override
- public void setToolTipText(String text) {
- mAction.setToolTipText(text);
- }
-
- @Override
- public void setAccelerator(int keycode) {
- mAction.setAccelerator(keycode);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java
deleted file mode 100644
index 145036bf3..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilities.java
+++ /dev/null
@@ -1,915 +0,0 @@
-/*
- * Copyright (C) 2011 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_ID;
-import static com.android.SdkConstants.ID_PREFIX;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.SdkConstants.TOOLS_URI;
-import static org.eclipse.wst.xml.core.internal.provisional.contenttype.ContentTypeIdForXML.ContentTypeID_XML;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-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.DescriptorsUtils;
-import com.android.utils.Pair;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
-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.sse.core.internal.provisional.text.IStructuredDocumentRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-/**
- * Various utility methods for manipulating DOM nodes.
- */
-@SuppressWarnings("restriction") // No replacement for restricted XML model yet
-public class DomUtilities {
- /**
- * Finds the nearest common parent of the two given nodes (which could be one of the
- * two nodes as well)
- *
- * @param node1 the first node to test
- * @param node2 the second node to test
- * @return the nearest common parent of the two given nodes
- */
- @Nullable
- public static Node getCommonAncestor(@NonNull Node node1, @NonNull Node node2) {
- while (node2 != null) {
- Node current = node1;
- while (current != null && current != node2) {
- current = current.getParentNode();
- }
- if (current == node2) {
- return current;
- }
- node2 = node2.getParentNode();
- }
-
- return null;
- }
-
- /**
- * Returns all elements below the given node (which can be a document,
- * element, etc). This will include the node itself, if it is an element.
- *
- * @param node the node to search from
- * @return all elements in the subtree formed by the node parameter
- */
- @NonNull
- public static List<Element> getAllElements(@NonNull Node node) {
- List<Element> elements = new ArrayList<Element>(64);
- addElements(node, elements);
- return elements;
- }
-
- private static void addElements(@NonNull Node node, @NonNull List<Element> elements) {
- if (node instanceof Element) {
- elements.add((Element) node);
- }
-
- NodeList childNodes = node.getChildNodes();
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- addElements(childNodes.item(i), elements);
- }
- }
-
- /**
- * Returns the depth of the given node (with the document node having depth 0,
- * and the document element having depth 1)
- *
- * @param node the node to test
- * @return the depth in the document
- */
- public static int getDepth(@NonNull Node node) {
- int depth = -1;
- while (node != null) {
- depth++;
- node = node.getParentNode();
- }
-
- return depth;
- }
-
- /**
- * Returns true if the given node has one or more element children
- *
- * @param node the node to test for element children
- * @return true if the node has one or more element children
- */
- public static boolean hasElementChildren(@NonNull Node node) {
- NodeList children = node.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns the DOM document for the given file
- *
- * @param file the XML file
- * @return the document, or null if not found or not parsed properly (no
- * errors are generated/thrown)
- */
- @Nullable
- public static Document getDocument(@NonNull IFile file) {
- IModelManager modelManager = StructuredModelManager.getModelManager();
- if (modelManager == null) {
- return null;
- }
- try {
- IStructuredModel model = modelManager.getExistingModelForRead(file);
- if (model == null) {
- model = modelManager.getModelForRead(file);
- }
- if (model != null) {
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- return domModel.getDocument();
- }
- try {
- } finally {
- model.releaseFromRead();
- }
- }
- } catch (Exception e) {
- // Ignore exceptions.
- }
-
- return null;
- }
-
- /**
- * Returns the DOM document for the given editor
- *
- * @param editor the XML editor
- * @return the document, or null if not found or not parsed properly (no
- * errors are generated/thrown)
- */
- @Nullable
- public static Document getDocument(@NonNull AndroidXmlEditor editor) {
- IStructuredModel model = editor.getModelForRead();
- try {
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- return domModel.getDocument();
- }
- } finally {
- if (model != null) {
- model.releaseFromRead();
- }
- }
-
- return null;
- }
-
-
- /**
- * Returns the XML DOM node corresponding to the given offset of the given
- * document.
- *
- * @param document The document to look in
- * @param offset The offset to look up the node for
- * @return The node containing the offset, or null
- */
- @Nullable
- public static Node getNode(@NonNull IDocument document, int offset) {
- Node node = null;
- IModelManager modelManager = StructuredModelManager.getModelManager();
- if (modelManager == null) {
- return null;
- }
- try {
- IStructuredModel model = modelManager.getExistingModelForRead(document);
- if (model != null) {
- try {
- for (; offset >= 0 && node == null; --offset) {
- node = (Node) model.getIndexedRegion(offset);
- }
- } finally {
- model.releaseFromRead();
- }
- }
- } catch (Exception e) {
- // Ignore exceptions.
- }
-
- return node;
- }
-
- /**
- * Returns the editing context at the given offset, as a pair of parent node and child
- * node. This is not the same as just calling {@link DomUtilities#getNode} and taking
- * its parent node, because special care has to be taken to return content element
- * positions.
- * <p>
- * For example, for the XML {@code <foo>^</foo>}, if the caret ^ is inside the foo
- * element, between the opening and closing tags, then the foo element is the parent,
- * and the child is null which represents a potential text node.
- * <p>
- * If the node is inside an element tag definition (between the opening and closing
- * bracket) then the child node will be the element and whatever parent (element or
- * document) will be its parent.
- * <p>
- * If the node is in a text node, then the text node will be the child and its parent
- * element or document node its parent.
- * <p>
- * Finally, if the caret is on a boundary of a text node, then the text node will be
- * considered the child, regardless of whether it is on the left or right of the
- * caret. For example, in the XML {@code <foo>^ </foo>} and in the XML
- * {@code <foo> ^</foo>}, in both cases the text node is preferred over the element.
- *
- * @param document the document to search in
- * @param offset the offset to look up
- * @return a pair of parent and child elements, where either the parent or the child
- * but not both can be null, and if non null the child.getParentNode() should
- * return the parent. Note that the method can also return null if no
- * document or model could be obtained or if the offset is invalid.
- */
- @Nullable
- public static Pair<Node, Node> getNodeContext(@NonNull IDocument document, int offset) {
- Node node = null;
- IModelManager modelManager = StructuredModelManager.getModelManager();
- if (modelManager == null) {
- return null;
- }
- try {
- IStructuredModel model = modelManager.getExistingModelForRead(document);
- if (model != null) {
- try {
- for (; offset >= 0 && node == null; --offset) {
- IndexedRegion indexedRegion = model.getIndexedRegion(offset);
- if (indexedRegion != null) {
- node = (Node) indexedRegion;
-
- if (node.getNodeType() == Node.TEXT_NODE) {
- return Pair.of(node.getParentNode(), node);
- }
-
- // Look at the structured document to see if
- // we have the special case where the caret is pointing at
- // a -potential- text node, e.g. <foo>^</foo>
- IStructuredDocument doc = model.getStructuredDocument();
- IStructuredDocumentRegion region =
- doc.getRegionAtCharacterOffset(offset);
-
- ITextRegion subRegion = region.getRegionAtCharacterOffset(offset);
- String type = subRegion.getType();
- if (DOMRegionContext.XML_END_TAG_OPEN.equals(type)) {
- // Try to return the text node if it's on the left
- // of this element node, such that replace strings etc
- // can be computed.
- Node lastChild = node.getLastChild();
- if (lastChild != null) {
- IndexedRegion previousRegion = (IndexedRegion) lastChild;
- if (previousRegion.getEndOffset() == offset) {
- return Pair.of(node, lastChild);
- }
- }
- return Pair.of(node, null);
- }
-
- return Pair.of(node.getParentNode(), node);
- }
- }
- } finally {
- model.releaseFromRead();
- }
- }
- } catch (Exception e) {
- // Ignore exceptions.
- }
-
- return null;
- }
-
- /**
- * Like {@link #getNode(IDocument, int)}, but has a bias parameter which lets you
- * indicate whether you want the search to look forwards or backwards.
- * This is vital when trying to compute a node range. Consider the following
- * XML fragment:
- * {@code
- * <a/><b/>[<c/><d/><e/>]<f/><g/>
- * }
- * Suppose we want to locate the nodes in the range indicated by the brackets above.
- * If we want to search for the node corresponding to the start position, should
- * we pick the node on its left or the node on its right? Similarly for the end
- * position. Clearly, we'll need to bias the search towards the right when looking
- * for the start position, and towards the left when looking for the end position.
- * The following method lets us do just that. When passed an offset which sits
- * on the edge of the computed node, it will pick the neighbor based on whether
- * "forward" is true or false, where forward means searching towards the right
- * and not forward is obviously towards the left.
- * @param document the document to search in
- * @param offset the offset to search for
- * @param forward if true, search forwards, otherwise search backwards when on node boundaries
- * @return the node which surrounds the given offset, or the node adjacent to the offset
- * where the side depends on the forward parameter
- */
- @Nullable
- public static Node getNode(@NonNull IDocument document, int offset, boolean forward) {
- Node node = getNode(document, offset);
-
- if (node instanceof IndexedRegion) {
- IndexedRegion region = (IndexedRegion) node;
-
- if (!forward && offset <= region.getStartOffset()) {
- Node left = node.getPreviousSibling();
- if (left == null) {
- left = node.getParentNode();
- }
-
- node = left;
- } else if (forward && offset >= region.getEndOffset()) {
- Node right = node.getNextSibling();
- if (right == null) {
- right = node.getParentNode();
- }
- node = right;
- }
- }
-
- return node;
- }
-
- /**
- * Returns a range of elements for the given caret range. Note that the two elements
- * may not be at the same level so callers may want to perform additional input
- * filtering.
- *
- * @param document the document to search in
- * @param beginOffset the beginning offset of the range
- * @param endOffset the ending offset of the range
- * @return a pair of begin+end elements, or null
- */
- @Nullable
- public static Pair<Element, Element> getElementRange(@NonNull IDocument document,
- int beginOffset, int endOffset) {
- Element beginElement = null;
- Element endElement = null;
- Node beginNode = getNode(document, beginOffset, true);
- Node endNode = beginNode;
- if (endOffset > beginOffset) {
- endNode = getNode(document, endOffset, false);
- }
-
- if (beginNode == null || endNode == null) {
- return null;
- }
-
- // Adjust offsets if you're pointing at text
- if (beginNode.getNodeType() != Node.ELEMENT_NODE) {
- // <foo> <bar1/> | <bar2/> </foo> => should pick <bar2/>
- beginElement = getNextElement(beginNode);
- if (beginElement == null) {
- // Might be inside the end of a parent, e.g.
- // <foo> <bar/> | </foo> => should pick <bar/>
- beginElement = getPreviousElement(beginNode);
- if (beginElement == null) {
- // We must be inside an empty element,
- // <foo> | </foo>
- // In that case just pick the parent.
- beginElement = getParentElement(beginNode);
- }
- }
- } else {
- beginElement = (Element) beginNode;
- }
-
- if (endNode.getNodeType() != Node.ELEMENT_NODE) {
- // In the following, | marks the caret position:
- // <foo> <bar1/> | <bar2/> </foo> => should pick <bar1/>
- endElement = getPreviousElement(endNode);
- if (endElement == null) {
- // Might be inside the beginning of a parent, e.g.
- // <foo> | <bar/></foo> => should pick <bar/>
- endElement = getNextElement(endNode);
- if (endElement == null) {
- // We must be inside an empty element,
- // <foo> | </foo>
- // In that case just pick the parent.
- endElement = getParentElement(endNode);
- }
- }
- } else {
- endElement = (Element) endNode;
- }
-
- if (beginElement != null && endElement != null) {
- return Pair.of(beginElement, endElement);
- }
-
- return null;
- }
-
- /**
- * Returns the next sibling element of the node, or null if there is no such element
- *
- * @param node the starting node
- * @return the next sibling element, or null
- */
- @Nullable
- public static Element getNextElement(@NonNull Node node) {
- while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
- node = node.getNextSibling();
- }
-
- return (Element) node; // may be null as well
- }
-
- /**
- * Returns the previous sibling element of the node, or null if there is no such element
- *
- * @param node the starting node
- * @return the previous sibling element, or null
- */
- @Nullable
- public static Element getPreviousElement(@NonNull Node node) {
- while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
- node = node.getPreviousSibling();
- }
-
- return (Element) node; // may be null as well
- }
-
- /**
- * Returns the closest ancestor element, or null if none
- *
- * @param node the starting node
- * @return the closest parent element, or null
- */
- @Nullable
- public static Element getParentElement(@NonNull Node node) {
- while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
- node = node.getParentNode();
- }
-
- return (Element) node; // may be null as well
- }
-
- /** Utility used by {@link #getFreeWidgetId(Element)} */
- private static void addLowercaseIds(@NonNull Element root, @NonNull Set<String> seen) {
- if (root.hasAttributeNS(ANDROID_URI, ATTR_ID)) {
- String id = root.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (id.startsWith(NEW_ID_PREFIX)) {
- // See getFreeWidgetId for details on locale
- seen.add(id.substring(NEW_ID_PREFIX.length()).toLowerCase(Locale.US));
- } else if (id.startsWith(ID_PREFIX)) {
- seen.add(id.substring(ID_PREFIX.length()).toLowerCase(Locale.US));
- } else {
- seen.add(id.toLowerCase(Locale.US));
- }
- }
- }
-
- /**
- * Returns a suitable new widget id (not including the {@code @id/} prefix) for the
- * given element, which is guaranteed to be unique in this document
- *
- * @param element the element to compute a new widget id for
- * @param reserved an optional set of extra, "reserved" set of ids that should be
- * considered taken
- * @param prefix an optional prefix to use for the generated name, or null to get a
- * default (which is currently the tag name)
- * @return a unique id, never null, which does not include the {@code @id/} prefix
- * @see DescriptorsUtils#getFreeWidgetId
- */
- public static String getFreeWidgetId(
- @NonNull Element element,
- @Nullable Set<String> reserved,
- @Nullable String prefix) {
- Set<String> ids = new HashSet<String>();
- if (reserved != null) {
- for (String id : reserved) {
- // Note that we perform locale-independent lowercase checks; in "Image" we
- // want the lowercase version to be "image", not "?mage" where ? is
- // the char LATIN SMALL LETTER DOTLESS I.
-
- ids.add(id.toLowerCase(Locale.US));
- }
- }
- addLowercaseIds(element.getOwnerDocument().getDocumentElement(), ids);
-
- if (prefix == null) {
- prefix = DescriptorsUtils.getBasename(element.getTagName());
- }
- String generated;
- int num = 1;
- do {
- generated = String.format("%1$s%2$d", prefix, num++); //$NON-NLS-1$
- } while (ids.contains(generated.toLowerCase(Locale.US)));
-
- return generated;
- }
-
- /**
- * Returns the element children of the given element
- *
- * @param element the parent element
- * @return a list of child elements, possibly empty but never null
- */
- @NonNull
- public static List<Element> getChildren(@NonNull Element element) {
- // Convenience to avoid lots of ugly DOM access casting
- NodeList children = element.getChildNodes();
- // An iterator would have been more natural (to directly drive the child list
- // iteration) but iterators can't be used in enhanced for loops...
- List<Element> result = new ArrayList<Element>(children.getLength());
- for (int i = 0, n = children.getLength(); i < n; i++) {
- Node node = children.item(i);
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- Element child = (Element) node;
- result.add(child);
- }
- }
-
- return result;
- }
-
- /**
- * Returns true iff the given elements are contiguous siblings
- *
- * @param elements the elements to be tested
- * @return true if the elements are contiguous siblings with no gaps
- */
- public static boolean isContiguous(@NonNull List<Element> elements) {
- if (elements.size() > 1) {
- // All elements must be siblings (e.g. same parent)
- Node parent = elements.get(0).getParentNode();
- if (!(parent instanceof Element)) {
- return false;
- }
- for (Element node : elements) {
- if (parent != node.getParentNode()) {
- return false;
- }
- }
-
- // Ensure that the siblings are contiguous; no gaps.
- // If we've selected all the children of the parent then we don't need
- // to look.
- List<Element> siblings = DomUtilities.getChildren((Element) parent);
- if (siblings.size() != elements.size()) {
- Set<Element> nodeSet = new HashSet<Element>(elements);
- boolean inRange = false;
- int remaining = elements.size();
- for (Element node : siblings) {
- boolean in = nodeSet.contains(node);
- if (in) {
- remaining--;
- if (remaining == 0) {
- break;
- }
- inRange = true;
- } else if (inRange) {
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- /**
- * Determines whether two element trees are equivalent. Two element trees are
- * equivalent if they represent the same DOM structure (elements, attributes, and
- * children in order). This is almost the same as simply checking whether the String
- * representations of the two nodes are identical, but this allows for minor
- * variations that are not semantically significant, such as variations in formatting
- * or ordering of the element attribute declarations, and the text children are
- * ignored (this is such that in for example layout where content is only used for
- * indentation the indentation differences are ignored). Null trees are never equal.
- *
- * @param element1 the first element to compare
- * @param element2 the second element to compare
- * @return true if the two element hierarchies are logically equal
- */
- public static boolean isEquivalent(@Nullable Element element1, @Nullable Element element2) {
- if (element1 == null || element2 == null) {
- return false;
- }
-
- if (!element1.getTagName().equals(element2.getTagName())) {
- return false;
- }
-
- // Check attribute map
- NamedNodeMap attributes1 = element1.getAttributes();
- NamedNodeMap attributes2 = element2.getAttributes();
-
- List<Attr> attributeNodes1 = new ArrayList<Attr>();
- for (int i = 0, n = attributes1.getLength(); i < n; i++) {
- Attr attribute = (Attr) attributes1.item(i);
- // Ignore tools uri namespace attributes for equivalency test
- if (TOOLS_URI.equals(attribute.getNamespaceURI())) {
- continue;
- }
- attributeNodes1.add(attribute);
- }
- List<Attr> attributeNodes2 = new ArrayList<Attr>();
- for (int i = 0, n = attributes2.getLength(); i < n; i++) {
- Attr attribute = (Attr) attributes2.item(i);
- // Ignore tools uri namespace attributes for equivalency test
- if (TOOLS_URI.equals(attribute.getNamespaceURI())) {
- continue;
- }
- attributeNodes2.add(attribute);
- }
-
- if (attributeNodes1.size() != attributeNodes2.size()) {
- return false;
- }
-
- if (attributes1.getLength() > 0) {
- Collections.sort(attributeNodes1, ATTRIBUTE_COMPARATOR);
- Collections.sort(attributeNodes2, ATTRIBUTE_COMPARATOR);
- for (int i = 0; i < attributeNodes1.size(); i++) {
- Attr attr1 = attributeNodes1.get(i);
- Attr attr2 = attributeNodes2.get(i);
- if (attr1.getLocalName() == null || attr2.getLocalName() == null) {
- if (!attr1.getName().equals(attr2.getName())) {
- return false;
- }
- } else if (!attr1.getLocalName().equals(attr2.getLocalName())) {
- return false;
- }
- if (!attr1.getValue().equals(attr2.getValue())) {
- return false;
- }
- if (attr1.getNamespaceURI() == null) {
- if (attr2.getNamespaceURI() != null) {
- return false;
- }
- } else if (attr2.getNamespaceURI() == null) {
- return false;
- } else if (!attr1.getNamespaceURI().equals(attr2.getNamespaceURI())) {
- return false;
- }
- }
- }
-
- NodeList children1 = element1.getChildNodes();
- NodeList children2 = element2.getChildNodes();
- int nextIndex1 = 0;
- int nextIndex2 = 0;
- while (true) {
- while (nextIndex1 < children1.getLength() &&
- children1.item(nextIndex1).getNodeType() != Node.ELEMENT_NODE) {
- nextIndex1++;
- }
-
- while (nextIndex2 < children2.getLength() &&
- children2.item(nextIndex2).getNodeType() != Node.ELEMENT_NODE) {
- nextIndex2++;
- }
-
- Element nextElement1 = (Element) (nextIndex1 < children1.getLength()
- ? children1.item(nextIndex1) : null);
- Element nextElement2 = (Element) (nextIndex2 < children2.getLength()
- ? children2.item(nextIndex2) : null);
- if (nextElement1 == null) {
- return nextElement2 == null;
- } else if (nextElement2 == null) {
- return false;
- } else if (!isEquivalent(nextElement1, nextElement2)) {
- return false;
- }
- nextIndex1++;
- nextIndex2++;
- }
- }
-
- /**
- * Finds the corresponding element in a document to a given element in another
- * document. Note that this does <b>not</b> do any kind of equivalence check
- * (see {@link #isEquivalent(Element, Element)}), and currently the search
- * is only by id; there is no structural search.
- *
- * @param element the element to find an equivalent for
- * @param document the document to search for an equivalent element in
- * @return an equivalent element, or null
- */
- @Nullable
- public static Element findCorresponding(@NonNull Element element, @NonNull Document document) {
- // Make sure the method is called correctly -- the element is for a different
- // document than the one we are searching
- assert element.getOwnerDocument() != document;
-
- // First search by id. This allows us to find the corresponding
- String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (id != null && id.length() > 0) {
- if (id.startsWith(ID_PREFIX)) {
- id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length());
- }
-
- return findCorresponding(document.getDocumentElement(), id);
- }
-
- // TODO: Search by structure - look in the document and
- // find a corresponding element in the same location in the structure,
- // e.g. 4th child of root, 3rd child, 6th child, then pick node with tag "foo".
-
- return null;
- }
-
- /** Helper method for {@link #findCorresponding(Element, Document)} */
- @Nullable
- private static Element findCorresponding(@NonNull Element element, @NonNull String targetId) {
- String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (id != null) { // Work around DOM bug
- if (id.equals(targetId)) {
- return element;
- } else if (id.startsWith(ID_PREFIX)) {
- id = NEW_ID_PREFIX + id.substring(ID_PREFIX.length());
- if (id.equals(targetId)) {
- return element;
- }
- }
- }
-
- NodeList children = element.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- Node node = children.item(i);
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- Element child = (Element) node;
- Element match = findCorresponding(child, targetId);
- if (match != null) {
- return match;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Parses the given XML string as a DOM document, using Eclipse's structured
- * XML model (which for example allows us to distinguish empty elements
- * (<foo/>) from elements with no children (<foo></foo>).
- *
- * @param xml the XML content to be parsed (must be well formed)
- * @return the DOM document, or null
- */
- @Nullable
- public static Document parseStructuredDocument(@NonNull String xml) {
- IStructuredModel model = createStructuredModel(xml);
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- return domModel.getDocument();
- }
-
- return null;
- }
-
- /**
- * Parses the given XML string and builds an Eclipse structured model for it.
- *
- * @param xml the XML content to be parsed (must be well formed)
- * @return the structured model
- */
- @Nullable
- public static IStructuredModel createStructuredModel(@NonNull String xml) {
- IStructuredModel model = createEmptyModel();
- IStructuredDocument document = model.getStructuredDocument();
- model.aboutToChangeModel();
- document.set(xml);
- model.changedModel();
-
- return model;
- }
-
- /**
- * Creates an empty Eclipse XML model
- *
- * @return a new Eclipse XML model
- */
- @NonNull
- public static IStructuredModel createEmptyModel() {
- IModelManager modelManager = StructuredModelManager.getModelManager();
- return modelManager.createUnManagedStructuredModelFor(ContentTypeID_XML);
- }
-
- /**
- * Creates an empty Eclipse XML document
- *
- * @return an empty Eclipse XML document
- */
- @Nullable
- public static Document createEmptyDocument() {
- IStructuredModel model = createEmptyModel();
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- return domModel.getDocument();
- }
-
- return null;
- }
-
- /**
- * Creates an empty non-Eclipse XML document.
- * This is used when you need to use XML operations not supported by
- * the Eclipse XML model (such as serialization).
- * <p>
- * The new document will not validate, will ignore comments, and will
- * support namespace.
- *
- * @return the new document
- */
- @Nullable
- public static Document createEmptyPlainDocument() {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(true);
- factory.setValidating(false);
- factory.setIgnoringComments(true);
- DocumentBuilder builder;
- try {
- builder = factory.newDocumentBuilder();
- return builder.newDocument();
- } catch (ParserConfigurationException e) {
- AdtPlugin.log(e, null);
- }
-
- return null;
- }
-
- /**
- * Parses the given XML string as a DOM document, using the JDK parser.
- * The parser does not validate, and is namespace aware.
- *
- * @param xml the XML content to be parsed (must be well formed)
- * @param logParserErrors if true, log parser errors to the log, otherwise
- * silently return null
- * @return the DOM document, or null
- */
- @Nullable
- public static Document parseDocument(@NonNull String xml, boolean logParserErrors) {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- InputSource is = new InputSource(new StringReader(xml));
- factory.setNamespaceAware(true);
- factory.setValidating(false);
- try {
- DocumentBuilder builder = factory.newDocumentBuilder();
- return builder.parse(is);
- } catch (Exception e) {
- if (logParserErrors) {
- AdtPlugin.log(e, null);
- }
- }
-
- return null;
- }
-
- /** Can be used to sort attributes by name */
- private static final Comparator<Attr> ATTRIBUTE_COMPARATOR = new Comparator<Attr>() {
- @Override
- public int compare(Attr a1, Attr a2) {
- return a1.getName().compareTo(a2.getName());
- }
- };
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DropGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DropGesture.java
deleted file mode 100644
index bb3be7f68..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DropGesture.java
+++ /dev/null
@@ -1,87 +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 org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.DropTargetListener;
-
-/**
- * A {@link DropGesture} is a {@link Gesture} which deals with drag and drop, so
- * it has additional hooks for indicating whether the current position is
- * "valid", and in general gets access to the system drag and drop data
- * structures. See the {@link Gesture} documentation for more details on whether
- * you should choose a plain {@link Gesture} or a {@link DropGesture}.
- */
-public abstract class DropGesture extends Gesture {
- /**
- * The cursor has entered the drop target boundaries.
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#dragEnter(DropTargetEvent)
- */
- public void dragEnter(DropTargetEvent event) {
- }
-
- /**
- * The cursor is moving over the drop target.
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#dragOver(DropTargetEvent)
- */
- public void dragOver(DropTargetEvent event) {
- }
-
- /**
- * The operation being performed has changed (usually due to the user
- * changing the selected modifier key(s) while dragging).
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#dragOperationChanged(DropTargetEvent)
- */
- public void dragOperationChanged(DropTargetEvent event) {
- }
-
- /**
- * The cursor has left the drop target boundaries OR the drop has been
- * canceled OR the data is about to be dropped.
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#dragLeave(DropTargetEvent)
- */
- public void dragLeave(DropTargetEvent event) {
- }
-
- /**
- * The drop is about to be performed. The drop target is given a last chance
- * to change the nature of the drop.
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#dropAccept(DropTargetEvent)
- */
- public void dropAccept(DropTargetEvent event) {
- }
-
- /**
- * The data is being dropped. The data field contains java format of the
- * data being dropped.
- *
- * @param event The {@link DropTargetEvent} for this drag and drop event
- * @see DropTargetListener#drop(DropTargetEvent)
- */
- public void drop(final DropTargetEvent event) {
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
deleted file mode 100644
index fc7127278..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java
+++ /dev/null
@@ -1,654 +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_ID;
-import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
-import static com.android.SdkConstants.FQCN_GESTURE_OVERLAY_VIEW;
-import static com.android.SdkConstants.FQCN_IMAGE_VIEW;
-import static com.android.SdkConstants.FQCN_LINEAR_LAYOUT;
-import static com.android.SdkConstants.FQCN_TEXT_VIEW;
-import static com.android.SdkConstants.GRID_VIEW;
-import static com.android.SdkConstants.LIST_VIEW;
-import static com.android.SdkConstants.SPINNER;
-import static com.android.SdkConstants.VIEW_FRAGMENT;
-
-import com.android.SdkConstants;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
-import com.android.ide.common.api.RuleAction.NestedAction;
-import com.android.ide.common.api.RuleAction.Toggle;
-import com.android.ide.common.layout.BaseViewRule;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeLayoutAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ChangeViewAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractIncludeAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.ExtractStyleAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.UnwrapAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.UseCompoundDrawableAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.WrapInAction;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ActionContributionItem;
-import org.eclipse.jface.action.ContributionItem;
-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.MenuManager;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Menu;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Helper class that is responsible for adding and managing the dynamic menu items
- * contributed by the {@link IViewRule} instances, based on the current selection
- * on the {@link LayoutCanvas}.
- * <p/>
- * This class is tied to a specific {@link LayoutCanvas} instance and a root {@link MenuManager}.
- * <p/>
- * Two instances of this are used: one created by {@link LayoutCanvas} and the other one
- * created by {@link OutlinePage}. Different root {@link MenuManager}s are populated, however
- * they are both linked to the current selection state of the {@link LayoutCanvas}.
- */
-class DynamicContextMenu {
- public static String DEFAULT_ACTION_SHORTCUT = "F2"; //$NON-NLS-1$
- public static int DEFAULT_ACTION_KEY = SWT.F2;
-
- /** The XML layout editor that contains the canvas that uses this menu. */
- private final LayoutEditorDelegate mEditorDelegate;
-
- /** The layout canvas that displays this context menu. */
- private final LayoutCanvas mCanvas;
-
- /** The root menu manager of the context menu. */
- private final MenuManager mMenuManager;
-
- /**
- * Creates a new helper responsible for adding and managing the dynamic menu items
- * contributed by the {@link IViewRule} instances, based on the current selection
- * on the {@link LayoutCanvas}.
- * @param editorDelegate the editor owning the menu
- * @param canvas The {@link LayoutCanvas} providing the selection, the node factory and
- * the rules engine.
- * @param rootMenu The root of the context menu displayed. In practice this may be the
- * context menu manager of the {@link LayoutCanvas} or the one from {@link OutlinePage}.
- */
- public DynamicContextMenu(
- LayoutEditorDelegate editorDelegate,
- LayoutCanvas canvas,
- MenuManager rootMenu) {
- mEditorDelegate = editorDelegate;
- mCanvas = canvas;
- mMenuManager = rootMenu;
-
- setupDynamicMenuActions();
- }
-
- /**
- * Setups the menu manager to receive dynamic menu contributions from the {@link IViewRule}s
- * when it's about to be shown.
- */
- private void setupDynamicMenuActions() {
- // Remember how many static actions we have. Then each time the menu is
- // shown, find dynamic contributions based on the current selection and insert
- // them at the beginning of the menu.
- final int numStaticActions = mMenuManager.getSize();
- mMenuManager.addMenuListener(new IMenuListener() {
- @Override
- public void menuAboutToShow(IMenuManager manager) {
-
- // Remove any previous dynamic contributions to keep only the
- // default static items.
- int n = mMenuManager.getSize() - numStaticActions;
- if (n > 0) {
- IContributionItem[] items = mMenuManager.getItems();
- for (int i = 0; i < n; i++) {
- mMenuManager.remove(items[i]);
- }
- }
-
- // Now add all the dynamic menu actions depending on the current selection.
- populateDynamicContextMenu();
- }
- });
-
- }
-
- /**
- * This method is invoked by <code>menuAboutToShow</code> on {@link #mMenuManager}.
- * All previous dynamic menu actions have been removed and this method can now insert
- * any new actions that depend on the current selection.
- */
- private void populateDynamicContextMenu() {
- // Create the actual menu contributions
- String endId = mMenuManager.getItems()[0].getId();
-
- Separator sep = new Separator();
- sep.setId("-dyn-gle-sep"); //$NON-NLS-1$
- mMenuManager.insertBefore(endId, sep);
- endId = sep.getId();
-
- List<SelectionItem> selections = mCanvas.getSelectionManager().getSelections();
- if (selections.size() == 0) {
- return;
- }
- List<INode> nodes = new ArrayList<INode>(selections.size());
- for (SelectionItem item : selections) {
- nodes.add(item.getNode());
- }
-
- List<IContributionItem> menuItems = getMenuItems(nodes);
- for (IContributionItem menuItem : menuItems) {
- mMenuManager.insertBefore(endId, menuItem);
- }
-
- insertTagSpecificMenus(endId);
- insertVisualRefactorings(endId);
- insertParentItems(endId);
- }
-
- /**
- * Returns the list of node-specific actions applicable to the given
- * collection of nodes
- *
- * @param nodes the collection of nodes to look up actions for
- * @return a list of contribution items applicable for all the nodes
- */
- private List<IContributionItem> getMenuItems(List<INode> nodes) {
- Map<INode, List<RuleAction>> allActions = new HashMap<INode, List<RuleAction>>();
- for (INode node : nodes) {
- List<RuleAction> actionList = getMenuActions((NodeProxy) node);
- allActions.put(node, actionList);
- }
-
- Set<String> availableIds = computeApplicableActionIds(allActions);
-
- // +10: Make room for separators too
- List<IContributionItem> items = new ArrayList<IContributionItem>(availableIds.size() + 10);
-
- // We'll use the actions returned by the first node. Even when there
- // are multiple items selected, we'll use the first action, but pass
- // the set of all selected nodes to that first action. Actions are required
- // to work this way to facilitate multi selection and actions which apply
- // to multiple nodes.
- NodeProxy first = (NodeProxy) nodes.get(0);
- List<RuleAction> firstSelectedActions = allActions.get(first);
- String defaultId = getDefaultActionId(first);
- for (RuleAction action : firstSelectedActions) {
- if (!availableIds.contains(action.getId())
- && !(action instanceof RuleAction.Separator)) {
- // This action isn't supported by all selected items.
- continue;
- }
-
- items.add(createContributionItem(action, nodes, defaultId));
- }
-
- return items;
- }
-
- private void insertParentItems(String endId) {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 1) {
- mMenuManager.insertBefore(endId, new Separator());
- INode parent = selection.get(0).getNode().getParent();
- while (parent != null) {
- String id = parent.getStringAttr(ANDROID_URI, ATTR_ID);
- String label;
- if (id != null && id.length() > 0) {
- label = BaseViewRule.stripIdPrefix(id);
- } else {
- // Use the view name, such as "Button", as the label
- label = parent.getFqcn();
- // Strip off package
- label = label.substring(label.lastIndexOf('.') + 1);
- }
- mMenuManager.insertBefore(endId, new NestedParentMenu(label, parent));
- parent = parent.getParent();
- }
- mMenuManager.insertBefore(endId, new Separator());
- }
- }
-
- private void insertVisualRefactorings(String endId) {
- // Extract As <include> refactoring, Wrap In Refactoring, etc.
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 0) {
- return;
- }
- // Only include the menu item if you are not right clicking on a root,
- // or on an included view, or on a non-contiguous selection
- mMenuManager.insertBefore(endId, new Separator());
- if (selection.size() == 1 && selection.get(0).getViewInfo() != null
- && selection.get(0).getViewInfo().getName().equals(FQCN_LINEAR_LAYOUT)) {
- CanvasViewInfo info = selection.get(0).getViewInfo();
- List<CanvasViewInfo> children = info.getChildren();
- if (children.size() == 2) {
- String first = children.get(0).getName();
- String second = children.get(1).getName();
- if ((first.equals(FQCN_IMAGE_VIEW) && second.equals(FQCN_TEXT_VIEW))
- || (first.equals(FQCN_TEXT_VIEW) && second.equals(FQCN_IMAGE_VIEW))) {
- mMenuManager.insertBefore(endId, UseCompoundDrawableAction.create(
- mEditorDelegate));
- }
- }
- }
- mMenuManager.insertBefore(endId, ExtractIncludeAction.create(mEditorDelegate));
- mMenuManager.insertBefore(endId, ExtractStyleAction.create(mEditorDelegate));
- mMenuManager.insertBefore(endId, WrapInAction.create(mEditorDelegate));
- if (selection.size() == 1 && !(selection.get(0).isRoot())) {
- mMenuManager.insertBefore(endId, UnwrapAction.create(mEditorDelegate));
- }
- if (selection.size() == 1 && (selection.get(0).isLayout() ||
- selection.get(0).getViewInfo().getName().equals(FQCN_GESTURE_OVERLAY_VIEW))) {
- mMenuManager.insertBefore(endId, ChangeLayoutAction.create(mEditorDelegate));
- } else {
- mMenuManager.insertBefore(endId, ChangeViewAction.create(mEditorDelegate));
- }
- mMenuManager.insertBefore(endId, new Separator());
- }
-
- /** "Preview List Content" pull-right menu for lists, "Preview Fragment" for fragments, etc. */
- private void insertTagSpecificMenus(String endId) {
-
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 0) {
- return;
- }
- for (SelectionItem item : selection) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- String name = node.getDescriptor().getXmlLocalName();
- boolean isGrid = name.equals(GRID_VIEW);
- boolean isSpinner = name.equals(SPINNER);
- if (name.equals(LIST_VIEW) || name.equals(EXPANDABLE_LIST_VIEW)
- || isGrid || isSpinner) {
- mMenuManager.insertBefore(endId, new Separator());
- mMenuManager.insertBefore(endId, new ListViewTypeMenu(mCanvas, isGrid, isSpinner));
- return;
- } else if (name.equals(VIEW_FRAGMENT) && selection.size() == 1) {
- mMenuManager.insertBefore(endId, new Separator());
- mMenuManager.insertBefore(endId, new FragmentMenu(mCanvas));
- return;
- }
- }
- }
-
- /**
- * Given a map from selection items to list of applicable actions (produced
- * by {@link #computeApplicableActions()}) this method computes the set of
- * common actions and returns the action ids of these actions.
- *
- * @param actions a map from selection item to list of actions applicable to
- * that selection item
- * @return set of action ids for the actions that are present in the action
- * lists for all selected items
- */
- private Set<String> computeApplicableActionIds(Map<INode, List<RuleAction>> actions) {
- if (actions.size() > 1) {
- // More than one view is selected, so we have to filter down the available
- // actions such that only those actions that are defined for all the views
- // are shown
- Map<String, Integer> idCounts = new HashMap<String, Integer>();
- for (Map.Entry<INode, List<RuleAction>> entry : actions.entrySet()) {
- List<RuleAction> actionList = entry.getValue();
- for (RuleAction action : actionList) {
- if (!action.supportsMultipleNodes()) {
- continue;
- }
- String id = action.getId();
- if (id != null) {
- assert id != null : action;
- Integer count = idCounts.get(id);
- if (count == null) {
- idCounts.put(id, Integer.valueOf(1));
- } else {
- idCounts.put(id, count + 1);
- }
- }
- }
- }
- Integer selectionCount = Integer.valueOf(actions.size());
- Set<String> validIds = new HashSet<String>(idCounts.size());
- for (Map.Entry<String, Integer> entry : idCounts.entrySet()) {
- Integer count = entry.getValue();
- if (selectionCount.equals(count)) {
- String id = entry.getKey();
- validIds.add(id);
- }
- }
- return validIds;
- } else {
- List<RuleAction> actionList = actions.values().iterator().next();
- Set<String> validIds = new HashSet<String>(actionList.size());
- for (RuleAction action : actionList) {
- String id = action.getId();
- validIds.add(id);
- }
- return validIds;
- }
- }
-
- /**
- * Returns the menu actions computed by the rule associated with this node.
- *
- * @param node the canvas node we need menu actions for
- * @return a list of {@link RuleAction} objects applicable to the node
- */
- private List<RuleAction> getMenuActions(NodeProxy node) {
- List<RuleAction> actions = mCanvas.getRulesEngine().callGetContextMenu(node);
- if (actions == null || actions.size() == 0) {
- return null;
- }
-
- return actions;
- }
-
- /**
- * Returns the default action id, or null
- *
- * @param node the node to look up the default action for
- * @return the action id, or null
- */
- private String getDefaultActionId(NodeProxy node) {
- return mCanvas.getRulesEngine().callGetDefaultActionId(node);
- }
-
- /**
- * Creates a {@link ContributionItem} for the given {@link RuleAction}.
- *
- * @param action the action to create a {@link ContributionItem} for
- * @param nodes the set of nodes the action should be applied to
- * @param defaultId if not non null, the id of an action which should be considered default
- * @return a new {@link ContributionItem} which implements the given action
- * on the given nodes
- */
- private ContributionItem createContributionItem(final RuleAction action,
- final List<INode> nodes, final String defaultId) {
- if (action instanceof RuleAction.Separator) {
- return new Separator();
- } else if (action instanceof NestedAction) {
- NestedAction parentAction = (NestedAction) action;
- return new ActionContributionItem(new NestedActionMenu(parentAction, nodes));
- } else if (action instanceof Choices) {
- Choices parentAction = (Choices) action;
- return new ActionContributionItem(new NestedChoiceMenu(parentAction, nodes));
- } else if (action instanceof Toggle) {
- return new ActionContributionItem(createToggleAction(action, nodes));
- } else {
- return new ActionContributionItem(createPlainAction(action, nodes, defaultId));
- }
- }
-
- private Action createToggleAction(final RuleAction action, final List<INode> nodes) {
- Toggle toggleAction = (Toggle) action;
- final boolean isChecked = toggleAction.isChecked();
- Action a = new Action(action.getTitle(), IAction.AS_CHECK_BOX) {
- @Override
- public void run() {
- String label = createActionLabel(action, nodes);
- mEditorDelegate.getEditor().wrapUndoEditXmlModel(label, new Runnable() {
- @Override
- public void run() {
- action.getCallback().action(action, nodes,
- null/* no valueId for a toggle */, !isChecked);
- applyPendingChanges();
- }
- });
- }
- };
- a.setId(action.getId());
- a.setChecked(isChecked);
- return a;
- }
-
- private IAction createPlainAction(final RuleAction action, final List<INode> nodes,
- final String defaultId) {
- IAction a = new Action(action.getTitle(), IAction.AS_PUSH_BUTTON) {
- @Override
- public void run() {
- String label = createActionLabel(action, nodes);
- mEditorDelegate.getEditor().wrapUndoEditXmlModel(label, new Runnable() {
- @Override
- public void run() {
- action.getCallback().action(action, nodes, null,
- Boolean.TRUE);
- applyPendingChanges();
- }
- });
- }
- };
-
- String id = action.getId();
- if (defaultId != null && id.equals(defaultId)) {
- a.setAccelerator(DEFAULT_ACTION_KEY);
- String text = a.getText();
- text = text + '\t' + DEFAULT_ACTION_SHORTCUT;
- a.setText(text);
-
- } else if (ATTR_ID.equals(id)) {
- // Keep in sync with {@link LayoutCanvas#handleKeyPressed}
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- a.setAccelerator('R' | SWT.MOD1 | SWT.MOD3);
- // Option+Command
- a.setText(a.getText().trim() + "\t\u2325\u2318R"); //$NON-NLS-1$
- } else if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX) {
- a.setAccelerator('R' | SWT.MOD2 | SWT.MOD3);
- a.setText(a.getText() + "\tShift+Alt+R"); //$NON-NLS-1$
- } else {
- a.setAccelerator('R' | SWT.MOD2 | SWT.MOD3);
- a.setText(a.getText() + "\tAlt+Shift+R"); //$NON-NLS-1$
- }
- }
- a.setId(id);
- return a;
- }
-
- private static String createActionLabel(final RuleAction action, final List<INode> nodes) {
- String label = action.getTitle();
- if (nodes.size() > 1) {
- label += String.format(" (%d elements)", nodes.size());
- }
- return label;
- }
-
- /**
- * The {@link NestedParentMenu} provides submenu content which adds actions
- * available on one of the selected node's parent nodes. This will be
- * similar to the menu content for the selected node, except the parent
- * menus will not be embedded within the nested menu.
- */
- private class NestedParentMenu extends SubmenuAction {
- INode mParent;
-
- NestedParentMenu(String title, INode parent) {
- super(title);
- mParent = parent;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- List<SelectionItem> selection = mCanvas.getSelectionManager().getSelections();
- if (selection.size() == 0) {
- return;
- }
-
- List<IContributionItem> menuItems = getMenuItems(Collections.singletonList(mParent));
- for (IContributionItem menuItem : menuItems) {
- menuItem.fill(menu, -1);
- }
- }
- }
-
- /**
- * The {@link NestedActionMenu} creates a lazily populated pull-right menu
- * where the children are {@link RuleAction}'s themselves.
- */
- private class NestedActionMenu extends SubmenuAction {
- private final NestedAction mParentAction;
- private final List<INode> mNodes;
-
- NestedActionMenu(NestedAction parentAction, List<INode> nodes) {
- super(parentAction.getTitle());
- mParentAction = parentAction;
- mNodes = nodes;
-
- assert mNodes.size() > 0;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- Map<INode, List<RuleAction>> allActions = new HashMap<INode, List<RuleAction>>();
- for (INode node : mNodes) {
- List<RuleAction> actionList = mParentAction.getNestedActions(node);
- allActions.put(node, actionList);
- }
-
- Set<String> availableIds = computeApplicableActionIds(allActions);
-
- NodeProxy first = (NodeProxy) mNodes.get(0);
- String defaultId = getDefaultActionId(first);
- List<RuleAction> firstSelectedActions = allActions.get(first);
-
- int count = 0;
- for (RuleAction firstAction : firstSelectedActions) {
- if (!availableIds.contains(firstAction.getId())
- && !(firstAction instanceof RuleAction.Separator)) {
- // This action isn't supported by all selected items.
- continue;
- }
-
- createContributionItem(firstAction, mNodes, defaultId).fill(menu, -1);
- count++;
- }
-
- if (count == 0) {
- addDisabledMessageItem("<Empty>");
- }
- }
- }
-
- private void applyPendingChanges() {
- LayoutCanvas canvas = mEditorDelegate.getGraphicalEditor().getCanvasControl();
- CanvasViewInfo root = canvas.getViewHierarchy().getRoot();
- if (root != null) {
- UiViewElementNode uiViewNode = root.getUiViewNode();
- NodeFactory nodeFactory = canvas.getNodeFactory();
- NodeProxy rootNode = nodeFactory.create(uiViewNode);
- if (rootNode != null) {
- rootNode.applyPendingChanges();
- }
- }
- }
-
- /**
- * The {@link NestedChoiceMenu} creates a lazily populated pull-right menu
- * where the items in the menu are strings
- */
- private class NestedChoiceMenu extends SubmenuAction {
- private final Choices mParentAction;
- private final List<INode> mNodes;
-
- NestedChoiceMenu(Choices parentAction, List<INode> nodes) {
- super(parentAction.getTitle());
- mParentAction = parentAction;
- mNodes = nodes;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- List<String> titles = mParentAction.getTitles();
- List<String> ids = mParentAction.getIds();
- String current = mParentAction.getCurrent();
- assert titles.size() == ids.size();
- String[] currentValues = current != null
- && current.indexOf(RuleAction.CHOICE_SEP) != -1 ?
- current.split(RuleAction.CHOICE_SEP_PATTERN) : null;
- for (int i = 0, n = Math.min(titles.size(), ids.size()); i < n; i++) {
- final String id = ids.get(i);
- if (id == null || id.equals(RuleAction.SEPARATOR)) {
- new Separator().fill(menu, -1);
- continue;
- }
-
- // Find out whether this item is selected
- boolean select = false;
- if (current != null) {
- // The current choice has a separator, so it's a flag with
- // multiple values selected. Compare keys with the split
- // values.
- if (currentValues != null) {
- if (current.indexOf(id) >= 0) {
- for (String value : currentValues) {
- if (id.equals(value)) {
- select = true;
- break;
- }
- }
- }
- } else {
- // current choice has no separator, simply compare to the key
- select = id.equals(current);
- }
- }
-
- String title = titles.get(i);
- IAction a = new Action(title,
- current != null ? IAction.AS_CHECK_BOX : IAction.AS_PUSH_BUTTON) {
- @Override
- public void runWithEvent(Event event) {
- run();
- }
- @Override
- public void run() {
- String label = createActionLabel(mParentAction, mNodes);
- mEditorDelegate.getEditor().wrapUndoEditXmlModel(label, new Runnable() {
- @Override
- public void run() {
- mParentAction.getCallback().action(mParentAction, mNodes, id,
- Boolean.TRUE);
- applyPendingChanges();
- }
- });
- }
- };
- a.setId(id);
- a.setEnabled(true);
- if (select) {
- a.setChecked(true);
- }
-
- new ActionContributionItem(a).fill(menu, -1);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/EmptyViewsOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/EmptyViewsOverlay.java
deleted file mode 100644
index daa3e0eae..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/EmptyViewsOverlay.java
+++ /dev/null
@@ -1,96 +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 org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-
-/**
- * The {@link EmptyViewsOverlay} paints bounding rectangles for any of the empty and
- * invisible container views in the scene.
- */
-public class EmptyViewsOverlay extends Overlay {
- /** The {@link ViewHierarchy} containing visible view information. */
- private final ViewHierarchy mViewHierarchy;
-
- /** Border color to paint the bounding boxes with. */
- private Color mBorderColor;
-
- /** Vertical scaling & scrollbar information. */
- private CanvasTransform mVScale;
-
- /** Horizontal scaling & scrollbar information. */
- private CanvasTransform mHScale;
-
- /**
- * Constructs a new {@link EmptyViewsOverlay} linked to the given view hierarchy.
- *
- * @param viewHierarchy The {@link ViewHierarchy} to render.
- * @param hScale The {@link CanvasTransform} to use to transfer horizontal layout
- * coordinates to screen coordinates.
- * @param vScale The {@link CanvasTransform} to use to transfer vertical layout coordinates
- * to screen coordinates.
- */
- public EmptyViewsOverlay(
- ViewHierarchy viewHierarchy,
- CanvasTransform hScale,
- CanvasTransform vScale) {
- super();
- mViewHierarchy = viewHierarchy;
- mHScale = hScale;
- mVScale = vScale;
- }
-
- @Override
- public void create(Device device) {
- mBorderColor = new Color(device, SwtDrawingStyle.EMPTY.getStrokeColor());
- }
-
- @Override
- public void dispose() {
- if (mBorderColor != null) {
- mBorderColor.dispose();
- mBorderColor = null;
- }
- }
-
- @Override
- public void paint(GC gc) {
- gc.setForeground(mBorderColor);
- gc.setLineDash(null);
- gc.setLineStyle(SwtDrawingStyle.EMPTY.getLineStyle());
- int oldAlpha = gc.getAlpha();
- gc.setAlpha(SwtDrawingStyle.EMPTY.getStrokeAlpha());
- gc.setLineWidth(SwtDrawingStyle.EMPTY.getLineWidth());
-
- for (CanvasViewInfo info : mViewHierarchy.getInvisibleViews()) {
- Rectangle r = info.getAbsRect();
-
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.width);
- int h = mVScale.scale(r.height);
-
- // +1: See explanation in equivalent code in {@link OutlineOverlay#paint}
- gc.drawRectangle(x, y, w + 1, h + 1);
- }
-
- gc.setAlpha(oldAlpha);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExportScreenshotAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExportScreenshotAction.java
deleted file mode 100644
index ac3328db2..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ExportScreenshotAction.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2012 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.DOT_PNG;
-
-import com.android.ide.eclipse.adt.AdtPlugin;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-import javax.imageio.ImageIO;
-
-/** Saves the current layout editor's rendered image to disk */
-class ExportScreenshotAction extends Action {
- private final LayoutCanvas mCanvas;
-
- ExportScreenshotAction(LayoutCanvas canvas) {
- super("Export Screenshot...");
- mCanvas = canvas;
- }
-
- @Override
- public void run() {
- Shell shell = AdtPlugin.getShell();
-
- ImageOverlay imageOverlay = mCanvas.getImageOverlay();
- BufferedImage image = imageOverlay.getAwtImage();
- if (image != null) {
- FileDialog dialog = new FileDialog(shell, SWT.SAVE);
- dialog.setFilterExtensions(new String[] { "*.png" }); //$NON-NLS-1$
- String path = dialog.open();
- if (path != null) {
- if (!path.endsWith(DOT_PNG)) {
- path = path + DOT_PNG;
- }
- File file = new File(path);
- if (file.exists()) {
- MessageDialog d = new MessageDialog(null, "File Already Exists", null,
- String.format(
- "%1$s already exists.\nWould you like to replace it?",
- path),
- MessageDialog.QUESTION, new String[] {
- // Yes will be moved to the end because it's the default
- "Yes", "No"
- }, 0);
- int result = d.open();
- if (result != 0) {
- return;
- }
- }
- try {
- ImageIO.write(image, "PNG", file); //$NON-NLS-1$
- } catch (IOException e) {
- AdtPlugin.log(e, null);
- }
- }
- } else {
- MessageDialog.openError(shell, "Error", "Image not available");
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java
deleted file mode 100644
index f7085fc12..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2011 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_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_CLASS;
-import static com.android.SdkConstants.ATTR_NAME;
-import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_FRAGMENT_LAYOUT;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
-import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
-import com.android.resources.ResourceType;
-import com.android.utils.Pair;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ActionContributionItem;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.widgets.Menu;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Fragment context menu allowing a layout to be chosen for previewing in the fragment frame.
- */
-public class FragmentMenu extends SubmenuAction {
- private static final String R_LAYOUT_RESOURCE_PREFIX = "R.layout."; //$NON-NLS-1$
- private static final String ANDROID_R_PREFIX = "android.R.layout"; //$NON-NLS-1$
-
- /** Associated canvas */
- private final LayoutCanvas mCanvas;
-
- /**
- * Creates a "Preview Fragment" menu
- *
- * @param canvas associated canvas
- */
- public FragmentMenu(LayoutCanvas canvas) {
- super("Fragment Layout");
- mCanvas = canvas;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- IAction action = new PickLayoutAction("Choose Layout...");
- new ActionContributionItem(action).fill(menu, -1);
-
- SelectionManager selectionManager = mCanvas.getSelectionManager();
- List<SelectionItem> selections = selectionManager.getSelections();
- if (selections.size() == 0) {
- return;
- }
-
- SelectionItem first = selections.get(0);
- UiViewElementNode node = first.getViewInfo().getUiViewNode();
- if (node == null) {
- return;
- }
- Element element = (Element) node.getXmlNode();
-
- String selected = getSelectedLayout();
- if (selected != null) {
- if (selected.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX)) {
- selected = selected.substring(ANDROID_LAYOUT_RESOURCE_PREFIX.length());
- }
- }
-
- String fqcn = getFragmentClass(element);
- if (fqcn != null) {
- // Look up the corresponding activity class and try to figure out
- // which layouts it is referring to and list these here as reasonable
- // guesses
- IProject project = mCanvas.getEditorDelegate().getEditor().getProject();
- String source = null;
- try {
- IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
- IType type = javaProject.findType(fqcn);
- if (type != null) {
- source = type.getSource();
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- // Find layouts. This is based on just skimming the Fragment class and looking
- // for layout references of the form R.layout.*.
- if (source != null) {
- String self = mCanvas.getLayoutResourceName();
- // Pair of <title,layout> to be displayed to the user
- List<Pair<String, String>> layouts = new ArrayList<Pair<String, String>>();
-
- if (source.contains("extends ListFragment")) { //$NON-NLS-1$
- layouts.add(Pair.of("list_content", //$NON-NLS-1$
- "@android:layout/list_content")); //$NON-NLS-1$
- }
-
- int index = 0;
- while (true) {
- index = source.indexOf(R_LAYOUT_RESOURCE_PREFIX, index);
- if (index == -1) {
- break;
- } else {
- index += R_LAYOUT_RESOURCE_PREFIX.length();
- int end = index;
- while (end < source.length()) {
- char c = source.charAt(end);
- if (!Character.isJavaIdentifierPart(c)) {
- break;
- }
- end++;
- }
- if (end > index) {
- String title = source.substring(index, end);
- String layout;
- // Is this R.layout part of an android.R.layout?
- int len = ANDROID_R_PREFIX.length() + 1; // prefix length to check
- if (index > len && source.startsWith(ANDROID_R_PREFIX, index - len)) {
- layout = ANDROID_LAYOUT_RESOURCE_PREFIX + title;
- } else {
- layout = LAYOUT_RESOURCE_PREFIX + title;
- }
- if (!self.equals(title)) {
- layouts.add(Pair.of(title, layout));
- }
- }
- }
-
- index++;
- }
-
- if (layouts.size() > 0) {
- new Separator().fill(menu, -1);
- for (Pair<String, String> layout : layouts) {
- action = new SetFragmentLayoutAction(layout.getFirst(),
- layout.getSecond(), selected);
- new ActionContributionItem(action).fill(menu, -1);
- }
- }
- }
- }
-
- if (selected != null) {
- new Separator().fill(menu, -1);
- action = new SetFragmentLayoutAction("Clear", null, null);
- new ActionContributionItem(action).fill(menu, -1);
- }
- }
-
- /**
- * Returns the class name of the fragment associated with the given {@code <fragment>}
- * element.
- *
- * @param element the element for the fragment tag
- * @return the fully qualified fragment class name, or null
- */
- @Nullable
- public static String getFragmentClass(@NonNull Element element) {
- String fqcn = element.getAttribute(ATTR_CLASS);
- if (fqcn == null || fqcn.length() == 0) {
- fqcn = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
- }
- if (fqcn != null && fqcn.length() > 0) {
- return fqcn;
- } else {
- return null;
- }
- }
-
- /**
- * Returns the layout to be shown for the given {@code <fragment>} node.
- *
- * @param node the node corresponding to the {@code <fragment>} element
- * @return the resource path to a layout to render for this fragment, or null
- */
- @Nullable
- public static String getFragmentLayout(@NonNull Node node) {
- String layout = LayoutMetadata.getProperty(
- node, LayoutMetadata.KEY_FRAGMENT_LAYOUT);
- if (layout != null) {
- return layout;
- }
-
- return null;
- }
-
- /** Returns the name of the currently displayed layout in the fragment, or null */
- @Nullable
- private String getSelectedLayout() {
- SelectionManager selectionManager = mCanvas.getSelectionManager();
- for (SelectionItem item : selectionManager.getSelections()) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- String layout = getFragmentLayout(node.getXmlNode());
- if (layout != null) {
- return layout;
- }
- }
- }
- return null;
- }
-
- /**
- * Set the given layout as the new fragment layout
- *
- * @param layout the layout resource name to show in this fragment
- */
- public void setNewLayout(@Nullable String layout) {
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- GraphicalEditorPart graphicalEditor = delegate.getGraphicalEditor();
- SelectionManager selectionManager = mCanvas.getSelectionManager();
-
- for (SelectionItem item : selectionManager.getSnapshot()) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- Node xmlNode = node.getXmlNode();
- LayoutMetadata.setProperty(delegate.getEditor(), xmlNode, KEY_FRAGMENT_LAYOUT,
- layout);
- }
- }
-
- // Refresh
- graphicalEditor.recomputeLayout();
- mCanvas.redraw();
- }
-
- /** Action to set the given layout as the new layout in a fragment */
- private class SetFragmentLayoutAction extends Action {
- private final String mLayout;
-
- public SetFragmentLayoutAction(String title, String layout, String selected) {
- super(title, IAction.AS_RADIO_BUTTON);
- mLayout = layout;
-
- if (layout != null && layout.equals(selected)) {
- setChecked(true);
- }
- }
-
- @Override
- public void run() {
- if (isChecked()) {
- setNewLayout(mLayout);
- }
- }
- }
-
- /**
- * Action which brings up the "Create new XML File" wizard, pre-selected with the
- * animation category
- */
- private class PickLayoutAction extends Action {
-
- public PickLayoutAction(String title) {
- super(title, IAction.AS_PUSH_BUTTON);
- }
-
- @Override
- public void run() {
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- IFile file = delegate.getEditor().getInputFile();
- GraphicalEditorPart editor = delegate.getGraphicalEditor();
- ResourceChooser dlg = ResourceChooser.create(editor, ResourceType.LAYOUT)
- .setInputValidator(CyclicDependencyValidator.create(file))
- .setInitialSize(85, 10)
- .setCurrentResource(getSelectedLayout());
- int result = dlg.open();
- if (result == ResourceChooser.CLEAR_RETURN_CODE) {
- setNewLayout(null);
- } else if (result == Window.OK) {
- String newType = dlg.getCurrentResource();
- setNewLayout(newType);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java
deleted file mode 100644
index 354517e76..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java
+++ /dev/null
@@ -1,645 +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 com.android.annotations.NonNull;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.IColor;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.SWTException;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.FontMetrics;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.RGB;
-
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Wraps an SWT {@link GC} into an {@link IGraphics} interface so that {@link IViewRule} objects
- * can directly draw on the canvas.
- * <p/>
- * The actual wrapped GC object is only non-null during the context of a paint operation.
- */
-public class GCWrapper implements IGraphics {
-
- /**
- * The actual SWT {@link GC} being wrapped. This can change during the lifetime of the
- * object. It is generally set to something during an onPaint method and then changed
- * to null when not in the context of a paint.
- */
- private GC mGc;
-
- /**
- * Current style being used for drawing.
- */
- private SwtDrawingStyle mCurrentStyle = SwtDrawingStyle.INVALID;
-
- /**
- * Implementation of IColor wrapping an SWT color.
- */
- private static class ColorWrapper implements IColor {
- private final Color mColor;
-
- public ColorWrapper(Color color) {
- mColor = color;
- }
-
- public Color getColor() {
- return mColor;
- }
- }
-
- /** A map of registered colors. All these colors must be disposed at the end. */
- private final HashMap<Integer, ColorWrapper> mColorMap = new HashMap<Integer, ColorWrapper>();
-
- /**
- * A map of the {@link SwtDrawingStyle} stroke colors that we have actually
- * used (to be disposed)
- */
- private final Map<DrawingStyle, Color> mStyleStrokeMap = new EnumMap<DrawingStyle, Color>(
- DrawingStyle.class);
-
- /**
- * A map of the {@link SwtDrawingStyle} fill colors that we have actually
- * used (to be disposed)
- */
- private final Map<DrawingStyle, Color> mStyleFillMap = new EnumMap<DrawingStyle, Color>(
- DrawingStyle.class);
-
- /** The cached pixel height of the default current font. */
- private int mFontHeight = 0;
-
- /** The scaling of the canvas in X. */
- private final CanvasTransform mHScale;
- /** The scaling of the canvas in Y. */
- private final CanvasTransform mVScale;
-
- public GCWrapper(CanvasTransform hScale, CanvasTransform vScale) {
- mHScale = hScale;
- mVScale = vScale;
- mGc = null;
- }
-
- void setGC(GC gc) {
- mGc = gc;
- }
-
- private GC getGc() {
- return mGc;
- }
-
- void checkGC() {
- if (mGc == null) {
- throw new RuntimeException("IGraphics used without a valid context.");
- }
- }
-
- void dispose() {
- for (ColorWrapper c : mColorMap.values()) {
- c.getColor().dispose();
- }
- mColorMap.clear();
-
- for (Color c : mStyleStrokeMap.values()) {
- c.dispose();
- }
- mStyleStrokeMap.clear();
-
- for (Color c : mStyleFillMap.values()) {
- c.dispose();
- }
- mStyleFillMap.clear();
- }
-
- //-------------
-
- @Override
- public @NonNull IColor registerColor(int rgb) {
- checkGC();
-
- Integer key = Integer.valueOf(rgb);
- ColorWrapper c = mColorMap.get(key);
- if (c == null) {
- c = new ColorWrapper(new Color(getGc().getDevice(),
- (rgb >> 16) & 0xFF,
- (rgb >> 8) & 0xFF,
- (rgb >> 0) & 0xFF));
- mColorMap.put(key, c);
- }
-
- return c;
- }
-
- /** Returns the (cached) pixel height of the current font. */
- @Override
- public int getFontHeight() {
- if (mFontHeight < 1) {
- checkGC();
- FontMetrics fm = getGc().getFontMetrics();
- mFontHeight = fm.getHeight();
- }
- return mFontHeight;
- }
-
- @Override
- public @NonNull IColor getForeground() {
- Color c = getGc().getForeground();
- return new ColorWrapper(c);
- }
-
- @Override
- public @NonNull IColor getBackground() {
- Color c = getGc().getBackground();
- return new ColorWrapper(c);
- }
-
- @Override
- public int getAlpha() {
- return getGc().getAlpha();
- }
-
- @Override
- public void setForeground(@NonNull IColor color) {
- checkGC();
- getGc().setForeground(((ColorWrapper) color).getColor());
- }
-
- @Override
- public void setBackground(@NonNull IColor color) {
- checkGC();
- getGc().setBackground(((ColorWrapper) color).getColor());
- }
-
- @Override
- public void setAlpha(int alpha) {
- checkGC();
- try {
- getGc().setAlpha(alpha);
- } catch (SWTException e) {
- // This means that we cannot set the alpha on this platform; this is
- // an acceptable no-op.
- }
- }
-
- @Override
- public void setLineStyle(@NonNull LineStyle style) {
- int swtStyle = 0;
- switch (style) {
- case LINE_SOLID:
- swtStyle = SWT.LINE_SOLID;
- break;
- case LINE_DASH:
- swtStyle = SWT.LINE_DASH;
- break;
- case LINE_DOT:
- swtStyle = SWT.LINE_DOT;
- break;
- case LINE_DASHDOT:
- swtStyle = SWT.LINE_DASHDOT;
- break;
- case LINE_DASHDOTDOT:
- swtStyle = SWT.LINE_DASHDOTDOT;
- break;
- default:
- assert false : style;
- break;
- }
-
- if (swtStyle != 0) {
- checkGC();
- getGc().setLineStyle(swtStyle);
- }
- }
-
- @Override
- public void setLineWidth(int width) {
- checkGC();
- if (width > 0) {
- getGc().setLineWidth(width);
- }
- }
-
- // lines
-
- @Override
- public void drawLine(int x1, int y1, int x2, int y2) {
- checkGC();
- useStrokeAlpha();
- x1 = mHScale.translate(x1);
- y1 = mVScale.translate(y1);
- x2 = mHScale.translate(x2);
- y2 = mVScale.translate(y2);
- getGc().drawLine(x1, y1, x2, y2);
- }
-
- @Override
- public void drawLine(@NonNull Point p1, @NonNull Point p2) {
- drawLine(p1.x, p1.y, p2.x, p2.y);
- }
-
- // rectangles
-
- @Override
- public void drawRect(int x1, int y1, int x2, int y2) {
- checkGC();
- useStrokeAlpha();
- int x = mHScale.translate(x1);
- int y = mVScale.translate(y1);
- int w = mHScale.scale(x2 - x1);
- int h = mVScale.scale(y2 - y1);
- getGc().drawRectangle(x, y, w, h);
- }
-
- @Override
- public void drawRect(@NonNull Point p1, @NonNull Point p2) {
- drawRect(p1.x, p1.y, p2.x, p2.y);
- }
-
- @Override
- public void drawRect(@NonNull Rect r) {
- checkGC();
- useStrokeAlpha();
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.w);
- int h = mVScale.scale(r.h);
- getGc().drawRectangle(x, y, w, h);
- }
-
- @Override
- public void fillRect(int x1, int y1, int x2, int y2) {
- checkGC();
- useFillAlpha();
- int x = mHScale.translate(x1);
- int y = mVScale.translate(y1);
- int w = mHScale.scale(x2 - x1);
- int h = mVScale.scale(y2 - y1);
- getGc().fillRectangle(x, y, w, h);
- }
-
- @Override
- public void fillRect(@NonNull Point p1, @NonNull Point p2) {
- fillRect(p1.x, p1.y, p2.x, p2.y);
- }
-
- @Override
- public void fillRect(@NonNull Rect r) {
- checkGC();
- useFillAlpha();
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.w);
- int h = mVScale.scale(r.h);
- getGc().fillRectangle(x, y, w, h);
- }
-
- // circles (actually ovals)
-
- public void drawOval(int x1, int y1, int x2, int y2) {
- checkGC();
- useStrokeAlpha();
- int x = mHScale.translate(x1);
- int y = mVScale.translate(y1);
- int w = mHScale.scale(x2 - x1);
- int h = mVScale.scale(y2 - y1);
- getGc().drawOval(x, y, w, h);
- }
-
- public void drawOval(Point p1, Point p2) {
- drawOval(p1.x, p1.y, p2.x, p2.y);
- }
-
- public void drawOval(Rect r) {
- checkGC();
- useStrokeAlpha();
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.w);
- int h = mVScale.scale(r.h);
- getGc().drawOval(x, y, w, h);
- }
-
- public void fillOval(int x1, int y1, int x2, int y2) {
- checkGC();
- useFillAlpha();
- int x = mHScale.translate(x1);
- int y = mVScale.translate(y1);
- int w = mHScale.scale(x2 - x1);
- int h = mVScale.scale(y2 - y1);
- getGc().fillOval(x, y, w, h);
- }
-
- public void fillOval(Point p1, Point p2) {
- fillOval(p1.x, p1.y, p2.x, p2.y);
- }
-
- public void fillOval(Rect r) {
- checkGC();
- useFillAlpha();
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.w);
- int h = mVScale.scale(r.h);
- getGc().fillOval(x, y, w, h);
- }
-
-
- // strings
-
- @Override
- public void drawString(@NonNull String string, int x, int y) {
- checkGC();
- useStrokeAlpha();
- x = mHScale.translate(x);
- y = mVScale.translate(y);
- // Background fill of text is not useful because it does not
- // use the alpha; we instead supply a separate method (drawBoxedStrings) which
- // first paints a semi-transparent mask for the text to sit on
- // top of (this ensures that the text is readable regardless of
- // colors of the pixels below the text)
- getGc().drawString(string, x, y, true /*isTransparent*/);
- }
-
- @Override
- public void drawBoxedStrings(int x, int y, @NonNull List<?> strings) {
- checkGC();
-
- x = mHScale.translate(x);
- y = mVScale.translate(y);
-
- // Compute bounds of the box by adding up the sum of the text heights
- // and the max of the text widths
- int width = 0;
- int height = 0;
- int lineHeight = getGc().getFontMetrics().getHeight();
- for (Object s : strings) {
- org.eclipse.swt.graphics.Point extent = getGc().stringExtent(s.toString());
- height += extent.y;
- width = Math.max(width, extent.x);
- }
-
- // Paint a box below the text
- int padding = 2;
- useFillAlpha();
- getGc().fillRectangle(x - padding, y - padding, width + 2 * padding, height + 2 * padding);
-
- // Finally draw strings on top
- useStrokeAlpha();
- int lineY = y;
- for (Object s : strings) {
- getGc().drawString(s.toString(), x, lineY, true /* isTransparent */);
- lineY += lineHeight;
- }
- }
-
- @Override
- public void drawString(@NonNull String string, @NonNull Point topLeft) {
- drawString(string, topLeft.x, topLeft.y);
- }
-
- // Styles
-
- @Override
- public void useStyle(@NonNull DrawingStyle style) {
- checkGC();
-
- // Look up the specific SWT style which defines the actual
- // colors and attributes to be used for the logical drawing style.
- SwtDrawingStyle swtStyle = SwtDrawingStyle.of(style);
- RGB stroke = swtStyle.getStrokeColor();
- if (stroke != null) {
- Color color = getStrokeColor(style, stroke);
- mGc.setForeground(color);
- }
- RGB fill = swtStyle.getFillColor();
- if (fill != null) {
- Color color = getFillColor(style, fill);
- mGc.setBackground(color);
- }
- mGc.setLineWidth(swtStyle.getLineWidth());
- mGc.setLineStyle(swtStyle.getLineStyle());
- if (swtStyle.getLineStyle() == SWT.LINE_CUSTOM) {
- mGc.setLineDash(new int[] {
- 8, 4
- });
- }
- mCurrentStyle = swtStyle;
- }
-
- /** Uses the stroke alpha for subsequent drawing operations. */
- private void useStrokeAlpha() {
- mGc.setAlpha(mCurrentStyle.getStrokeAlpha());
- }
-
- /** Uses the fill alpha for subsequent drawing operations. */
- private void useFillAlpha() {
- mGc.setAlpha(mCurrentStyle.getFillAlpha());
- }
-
- /**
- * Get the SWT stroke color (foreground/border) to use for the given style,
- * using the provided color description if we haven't seen this color yet.
- * The color will also be placed in the {@link #mStyleStrokeMap} such that
- * it can be disposed of at cleanup time.
- *
- * @param style The drawing style for which we want a color
- * @param defaultColorDesc The RGB values to initialize the color to if we
- * haven't seen this color before
- * @return The color object
- */
- private Color getStrokeColor(DrawingStyle style, RGB defaultColorDesc) {
- return getStyleColor(style, defaultColorDesc, mStyleStrokeMap);
- }
-
- /**
- * Get the SWT fill (background/interior) color to use for the given style,
- * using the provided color description if we haven't seen this color yet.
- * The color will also be placed in the {@link #mStyleStrokeMap} such that
- * it can be disposed of at cleanup time.
- *
- * @param style The drawing style for which we want a color
- * @param defaultColorDesc The RGB values to initialize the color to if we
- * haven't seen this color before
- * @return The color object
- */
- private Color getFillColor(DrawingStyle style, RGB defaultColorDesc) {
- return getStyleColor(style, defaultColorDesc, mStyleFillMap);
- }
-
- /**
- * Get the SWT color to use for the given style, using the provided color
- * description if we haven't seen this color yet. The color will also be
- * placed in the map referenced by the map parameter such that it can be
- * disposed of at cleanup time.
- *
- * @param style The drawing style for which we want a color
- * @param defaultColorDesc The RGB values to initialize the color to if we
- * haven't seen this color before
- * @param map The color map to use
- * @return The color object
- */
- private Color getStyleColor(DrawingStyle style, RGB defaultColorDesc,
- Map<DrawingStyle, Color> map) {
- Color color = map.get(style);
- if (color == null) {
- color = new Color(getGc().getDevice(), defaultColorDesc);
- map.put(style, color);
- }
-
- return color;
- }
-
- // dots
-
- @Override
- public void drawPoint(int x, int y) {
- checkGC();
- useStrokeAlpha();
- x = mHScale.translate(x);
- y = mVScale.translate(y);
-
- getGc().drawPoint(x, y);
- }
-
- // arrows
-
- private static final int MIN_LENGTH = 10;
-
-
- @Override
- public void drawArrow(int x1, int y1, int x2, int y2, int size) {
- int arrowWidth = size;
- int arrowHeight = size;
-
- checkGC();
- useStrokeAlpha();
- x1 = mHScale.translate(x1);
- y1 = mVScale.translate(y1);
- x2 = mHScale.translate(x2);
- y2 = mVScale.translate(y2);
- GC graphics = getGc();
-
- // Make size adjustments to ensure that the arrow has enough width to be visible
- if (x1 == x2 && Math.abs(y1 - y2) < MIN_LENGTH) {
- int delta = (MIN_LENGTH - Math.abs(y1 - y2)) / 2;
- if (y1 < y2) {
- y1 -= delta;
- y2 += delta;
- } else {
- y1 += delta;
- y2-= delta;
- }
-
- } else if (y1 == y2 && Math.abs(x1 - x2) < MIN_LENGTH) {
- int delta = (MIN_LENGTH - Math.abs(x1 - x2)) / 2;
- if (x1 < x2) {
- x1 -= delta;
- x2 += delta;
- } else {
- x1 += delta;
- x2-= delta;
- }
- }
-
- graphics.drawLine(x1, y1, x2, y2);
-
- // Arrowhead:
-
- if (x1 == x2) {
- // Vertical
- if (y2 > y1) {
- graphics.drawLine(x2 - arrowWidth, y2 - arrowHeight, x2, y2);
- graphics.drawLine(x2 + arrowWidth, y2 - arrowHeight, x2, y2);
- } else {
- graphics.drawLine(x2 - arrowWidth, y2 + arrowHeight, x2, y2);
- graphics.drawLine(x2 + arrowWidth, y2 + arrowHeight, x2, y2);
- }
- } else if (y1 == y2) {
- // Horizontal
- if (x2 > x1) {
- graphics.drawLine(x2 - arrowHeight, y2 - arrowWidth, x2, y2);
- graphics.drawLine(x2 - arrowHeight, y2 + arrowWidth, x2, y2);
- } else {
- graphics.drawLine(x2 + arrowHeight, y2 - arrowWidth, x2, y2);
- graphics.drawLine(x2 + arrowHeight, y2 + arrowWidth, x2, y2);
- }
- } else {
- // Compute angle:
- int dy = y2 - y1;
- int dx = x2 - x1;
- double angle = Math.atan2(dy, dx);
- double lineLength = Math.sqrt(dy * dy + dx * dx);
-
- // Imagine a line of the same length as the arrow, but with angle 0.
- // Its two arrow lines are at (-arrowWidth, -arrowHeight) relative
- // to the endpoint (x1 + lineLength, y1) stretching up to (x2,y2).
- // We compute the positions of (ax,ay) for the point above and
- // below this line and paint the lines to it:
- double ax = x1 + lineLength - arrowHeight;
- double ay = y1 - arrowWidth;
- int rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1);
- int ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1);
- graphics.drawLine(x2, y2, rx, ry);
-
- ay = y1 + arrowWidth;
- rx = (int) (Math.cos(angle) * (ax-x1) - Math.sin(angle) * (ay-y1) + x1);
- ry = (int) (Math.sin(angle) * (ax-x1) + Math.cos(angle) * (ay-y1) + y1);
- graphics.drawLine(x2, y2, rx, ry);
- }
-
- /* TODO: Experiment with filled arrow heads?
- if (x1 == x2) {
- // Vertical
- if (y2 > y1) {
- for (int i = 0; i < arrowWidth; i++) {
- graphics.drawLine(x2 - arrowWidth + i, y2 - arrowWidth + i,
- x2 + arrowWidth - i, y2 - arrowWidth + i);
- }
- } else {
- for (int i = 0; i < arrowWidth; i++) {
- graphics.drawLine(x2 - arrowWidth + i, y2 + arrowWidth - i,
- x2 + arrowWidth - i, y2 + arrowWidth - i);
- }
- }
- } else if (y1 == y2) {
- // Horizontal
- if (x2 > x1) {
- for (int i = 0; i < arrowHeight; i++) {
- graphics.drawLine(x2 - arrowHeight + i, y2 - arrowHeight + i, x2
- - arrowHeight + i, y2 + arrowHeight - i);
- }
- } else {
- for (int i = 0; i < arrowHeight; i++) {
- graphics.drawLine(x2 + arrowHeight - i, y2 - arrowHeight + i, x2
- + arrowHeight - i, y2 + arrowHeight - i);
- }
- }
- } else {
- // Arbitrary angle -- need to use trig
- // TODO: Implement this
- }
- */
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Gesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Gesture.java
deleted file mode 100644
index a35d19078..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Gesture.java
+++ /dev/null
@@ -1,156 +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 com.android.utils.Pair;
-
-import org.eclipse.swt.events.KeyEvent;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A gesture is a mouse or keyboard driven user operation, such as a
- * swipe-select or a resize. It can be thought of as a session, since it is
- * initiated, updated during user manipulation, and finally completed or
- * canceled. A gesture is associated with a single undo transaction (although
- * some gestures don't actually edit anything, such as a selection), and a
- * gesture can have a number of graphics {@link Overlay}s which are added and
- * cleaned up on behalf of the gesture by the system.
- * <p/>
- * Gestures are typically mouse oriented. If a mouse wishes to integrate
- * with the native drag &amp; drop support, it should also implement
- * the {@link DropGesture} interface, which is a sub interface of this
- * {@link Gesture} interface. There are pros and cons to using native drag
- * &amp; drop, so various gestures will differ in whether they use it.
- * In particular, you should use drag &amp; drop if your gesture should:
- * <ul>
- * <li> Show a native drag &amp; drop cursor
- * <li> Copy or move data, especially if this applies outside the canvas
- * control window or even the application itself
- * </ul>
- * You might want to avoid using native drag &amp; drop if your gesture should:
- * <ul>
- * <li> Continue updating itself even when the mouse cursor leaves the
- * canvas window (in a drag &amp; gesture, as soon as you leave the canvas
- * the drag source is no longer informed of mouse updates, whereas a regular
- * mouse listener is)
- * <li> Respond to modifier keys (for example, if toggling the Shift key
- * should constrain motion as is common during resizing, and so on)
- * <li> Use no special cursor (for example, during a marquee selection gesture we
- * don't want a native drag &amp; drop cursor)
- * </ul>
- * <p/>
- * Examples of gestures:
- * <ul>
- * <li>Move (dragging to reorder or change hierarchy of views or change visual
- * layout attributes)
- * <li>Marquee (swiping out a rectangle to make a selection)
- * <li>Resize (dragging some edge or corner of a widget to change its size, for
- * example to some new fixed size, or to "attach" it to some other edge.)
- * <li>Inline Editing (editing the text of some text-oriented widget like a
- * label or a button)
- * <li>Link (associate two or more widgets in some way, such as an
- * "is required" widget linked to a text field)
- * </ul>
- */
-public abstract class Gesture {
- /** Start mouse coordinate, in control coordinates. */
- protected ControlPoint mStart;
-
- /** Initial SWT mask when the gesture started. */
- protected int mStartMask;
-
- /**
- * Returns a list of overlays, from bottom to top (where the later overlays
- * are painted on top of earlier ones if they overlap).
- *
- * @return A list of overlays to paint for this gesture, if applicable.
- * Should not be null, but can be empty.
- */
- public List<Overlay> createOverlays() {
- return Collections.emptyList();
- }
-
- /**
- * Handles initialization of this gesture. Called when the gesture is
- * starting.
- *
- * @param pos The most recent mouse coordinate applicable to this
- * gesture, relative to the canvas control.
- * @param startMask The initial SWT mask for the gesture, if known, or
- * otherwise 0.
- */
- public void begin(ControlPoint pos, int startMask) {
- mStart = pos;
- mStartMask = startMask;
- }
-
- /**
- * Handles updating of the gesture state for a new mouse position.
- *
- * @param pos The most recent mouse coordinate applicable to this
- * gesture, relative to the canvas control.
- */
- public void update(ControlPoint pos) {
- }
-
- /**
- * Handles termination of the gesture. This method is called when the
- * gesture has terminated (either through successful completion, or because
- * it was canceled).
- *
- * @param pos The most recent mouse coordinate applicable to this
- * gesture, relative to the canvas control.
- * @param canceled True if the gesture was canceled, and false otherwise.
- */
- public void end(ControlPoint pos, boolean canceled) {
- }
-
- /**
- * Handles a key press during the gesture. May be called repeatedly when the
- * user is holding the key for several seconds.
- *
- * @param event The SWT event for the key press,
- * @return true if this gesture consumed the key press, otherwise return false
- */
- public boolean keyPressed(KeyEvent event) {
- return false;
- }
-
- /**
- * Handles a key release during the gesture.
- *
- * @param event The SWT event for the key release,
- * @return true if this gesture consumed the key press, otherwise return false
- */
- public boolean keyReleased(KeyEvent event) {
- return false;
- }
-
- /**
- * Returns whether tooltips should be display below and to the right of the mouse
- * cursor.
- *
- * @return a pair of booleans, the first indicating whether the tooltip should be
- * below and the second indicating whether the tooltip should be displayed to
- * the right of the mouse cursor.
- */
- public Pair<Boolean, Boolean> getTooltipPosition() {
- return Pair.of(true, true);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
deleted file mode 100644
index 98bc25e37..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
+++ /dev/null
@@ -1,930 +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 com.android.SdkConstants;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.utils.Pair;
-
-import org.eclipse.jface.action.IStatusLineManager;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DragSource;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.DropTargetListener;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseMoveListener;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.events.TypedEvent;
-import org.eclipse.swt.graphics.Cursor;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IEditorSite;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The {@link GestureManager} is is the central manager of gestures; it is responsible
- * for recognizing when particular gestures should begin and terminate. It
- * listens to the drag, mouse and keyboard systems to find out when to start
- * gestures and in order to update the gestures along the way.
- */
-public class GestureManager {
- /** The canvas which owns this GestureManager. */
- private final LayoutCanvas mCanvas;
-
- /** The currently executing gesture, or null. */
- private Gesture mCurrentGesture;
-
- /** A listener for drop target events. */
- private final DropTargetListener mDropListener = new CanvasDropListener();
-
- /** A listener for drag source events. */
- private final DragSourceListener mDragSourceListener = new CanvasDragSourceListener();
-
- /** Tooltip shown during the gesture, or null */
- private GestureToolTip mTooltip;
-
- /**
- * The list of overlays associated with {@link #mCurrentGesture}. Will be
- * null before it has been initialized lazily by the paint routine (the
- * initialized value can never be null, but it can be an empty collection).
- */
- private List<Overlay> mOverlays;
-
- /**
- * Most recently seen mouse position (x coordinate). We keep a copy of this
- * value since we sometimes need to know it when we aren't told about the
- * mouse position (such as when a keystroke is received, such as an arrow
- * key in order to tweak the current drop position)
- */
- protected int mLastMouseX;
-
- /**
- * Most recently seen mouse position (y coordinate). We keep a copy of this
- * value since we sometimes need to know it when we aren't told about the
- * mouse position (such as when a keystroke is received, such as an arrow
- * key in order to tweak the current drop position)
- */
- protected int mLastMouseY;
-
- /**
- * Most recently seen mouse mask. We keep a copy of this since in some
- * scenarios (such as on a drag gesture) we don't get access to it.
- */
- protected int mLastStateMask;
-
- /**
- * Listener for mouse motion, click and keyboard events.
- */
- private Listener mListener;
-
- /**
- * When we the drag leaves, we don't know if that's the last we'll see of
- * this drag or if it's just temporarily outside the canvas and it will
- * return. We want to restore it if it comes back. This is also necessary
- * because even on a drop we'll receive a
- * {@link DropTargetListener#dragLeave} right before the drop, and we need
- * to restore it in the drop. Therefore, when we lose a {@link DropGesture}
- * to a {@link DropTargetListener#dragLeave}, we store a reference to the
- * current gesture as a {@link #mZombieGesture}, since the gesture is dead
- * but might be brought back to life if we see a subsequent
- * {@link DropTargetListener#dragEnter} before another gesture begins.
- */
- private DropGesture mZombieGesture;
-
- /**
- * Flag tracking whether we've set a message or error message on the global status
- * line (since we only want to clear that message if we have set it ourselves).
- * This is the actual message rather than a boolean such that (if we can get our
- * hands on the global message) we can check to see if the current message is the
- * one we set and only in that case clear it when it is no longer applicable.
- */
- private String mDisplayingMessage;
-
- /**
- * Constructs a new {@link GestureManager} for the given
- * {@link LayoutCanvas}.
- *
- * @param canvas The canvas which controls this {@link GestureManager}
- */
- public GestureManager(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- /**
- * Returns the canvas associated with this GestureManager.
- *
- * @return The {@link LayoutCanvas} associated with this GestureManager.
- * Never null.
- */
- public LayoutCanvas getCanvas() {
- return mCanvas;
- }
-
- /**
- * Returns the current gesture, if one is in progress, and otherwise returns
- * null.
- *
- * @return The current gesture or null.
- */
- public Gesture getCurrentGesture() {
- return mCurrentGesture;
- }
-
- /**
- * Paints the overlays associated with the current gesture, if any.
- *
- * @param gc The graphics object to paint into.
- */
- public void paint(GC gc) {
- if (mCurrentGesture == null) {
- return;
- }
-
- if (mOverlays == null) {
- mOverlays = mCurrentGesture.createOverlays();
- Device device = gc.getDevice();
- for (Overlay overlay : mOverlays) {
- overlay.create(device);
- }
- }
- for (Overlay overlay : mOverlays) {
- overlay.paint(gc);
- }
- }
-
- /**
- * Registers all the listeners needed by the {@link GestureManager}.
- *
- * @param dragSource The drag source in the {@link LayoutCanvas} to listen
- * to.
- * @param dropTarget The drop target in the {@link LayoutCanvas} to listen
- * to.
- */
- public void registerListeners(DragSource dragSource, DropTarget dropTarget) {
- assert mListener == null;
- mListener = new Listener();
- mCanvas.addMouseMoveListener(mListener);
- mCanvas.addMouseListener(mListener);
- mCanvas.addKeyListener(mListener);
-
- if (dragSource != null) {
- dragSource.addDragListener(mDragSourceListener);
- }
- if (dropTarget != null) {
- dropTarget.addDropListener(mDropListener);
- }
- }
-
- /**
- * Unregisters all the listeners previously registered by
- * {@link #registerListeners}.
- *
- * @param dragSource The drag source in the {@link LayoutCanvas} to stop
- * listening to.
- * @param dropTarget The drop target in the {@link LayoutCanvas} to stop
- * listening to.
- */
- public void unregisterListeners(DragSource dragSource, DropTarget dropTarget) {
- if (mCanvas.isDisposed()) {
- // If the LayoutCanvas is already disposed, we shouldn't try to unregister
- // the listeners; they are already not active and an attempt to remove the
- // listener will throw a widget-is-disposed exception.
- mListener = null;
- return;
- }
-
- if (mListener != null) {
- mCanvas.removeMouseMoveListener(mListener);
- mCanvas.removeMouseListener(mListener);
- mCanvas.removeKeyListener(mListener);
- mListener = null;
- }
-
- if (dragSource != null) {
- dragSource.removeDragListener(mDragSourceListener);
- }
- if (dropTarget != null) {
- dropTarget.removeDropListener(mDropListener);
- }
- }
-
- /**
- * Starts the given gesture.
- *
- * @param mousePos The most recent mouse coordinate applicable to the new
- * gesture, in control coordinates.
- * @param gesture The gesture to initiate
- */
- private void startGesture(ControlPoint mousePos, Gesture gesture, int mask) {
- if (mCurrentGesture != null) {
- finishGesture(mousePos, true);
- assert mCurrentGesture == null;
- }
-
- if (gesture != null) {
- mCurrentGesture = gesture;
- mCurrentGesture.begin(mousePos, mask);
- }
- }
-
- /**
- * Updates the current gesture, if any, for the given event.
- *
- * @param mousePos The most recent mouse coordinate applicable to the new
- * gesture, in control coordinates.
- * @param event The event corresponding to this update. May be null. Don't
- * make any assumptions about the type of this event - for
- * example, it may not always be a MouseEvent, it could be a
- * DragSourceEvent, etc.
- */
- private void updateMouse(ControlPoint mousePos, TypedEvent event) {
- if (mCurrentGesture != null) {
- mCurrentGesture.update(mousePos);
- }
- }
-
- /**
- * Finish the given gesture, either from successful completion or from
- * cancellation.
- *
- * @param mousePos The most recent mouse coordinate applicable to the new
- * gesture, in control coordinates.
- * @param canceled True if and only if the gesture was canceled.
- */
- private void finishGesture(ControlPoint mousePos, boolean canceled) {
- if (mCurrentGesture != null) {
- mCurrentGesture.end(mousePos, canceled);
- if (mOverlays != null) {
- for (Overlay overlay : mOverlays) {
- overlay.dispose();
- }
- mOverlays = null;
- }
- mCurrentGesture = null;
- mZombieGesture = null;
- mLastStateMask = 0;
- updateMessage(null);
- updateCursor(mousePos);
- mCanvas.redraw();
- }
- }
-
- /**
- * Update the cursor to show the type of operation we expect on a mouse press:
- * <ul>
- * <li>Over a selection handle, show a directional cursor depending on the position of
- * the selection handle
- * <li>Over a widget, show a move (hand) cursor
- * <li>Otherwise, show the default arrow cursor
- * </ul>
- */
- void updateCursor(ControlPoint controlPoint) {
- // We don't hover on the root since it's not a widget per see and it is always there.
- SelectionManager selectionManager = mCanvas.getSelectionManager();
-
- if (!selectionManager.isEmpty()) {
- Display display = mCanvas.getDisplay();
- Pair<SelectionItem, SelectionHandle> handlePair =
- selectionManager.findHandle(controlPoint);
- if (handlePair != null) {
- SelectionHandle handle = handlePair.getSecond();
- int cursorType = handle.getSwtCursorType();
- Cursor cursor = display.getSystemCursor(cursorType);
- if (cursor != mCanvas.getCursor()) {
- mCanvas.setCursor(cursor);
- }
- return;
- }
-
- // See if it's over a selected view
- LayoutPoint layoutPoint = controlPoint.toLayout();
- for (SelectionItem item : selectionManager.getSelections()) {
- if (item.getRect().contains(layoutPoint.x, layoutPoint.y)
- && !item.isRoot()) {
- Cursor cursor = display.getSystemCursor(SWT.CURSOR_HAND);
- if (cursor != mCanvas.getCursor()) {
- mCanvas.setCursor(cursor);
- }
- return;
- }
- }
- }
-
- if (mCanvas.getCursor() != null) {
- mCanvas.setCursor(null);
- }
- }
-
- /**
- * Update the Eclipse status message with any feedback messages from the given
- * {@link DropFeedback} object, or clean up if there is no more feedback to process
- * @param feedback the feedback whose message we want to display, or null to clear the
- * message if previously set
- */
- void updateMessage(DropFeedback feedback) {
- IEditorSite editorSite = mCanvas.getEditorDelegate().getEditor().getEditorSite();
- IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
- if (feedback == null) {
- if (mDisplayingMessage != null) {
- status.setMessage(null);
- status.setErrorMessage(null);
- mDisplayingMessage = null;
- }
- } else if (feedback.errorMessage != null) {
- if (!feedback.errorMessage.equals(mDisplayingMessage)) {
- mDisplayingMessage = feedback.errorMessage;
- status.setErrorMessage(mDisplayingMessage);
- }
- } else if (feedback.message != null) {
- if (!feedback.message.equals(mDisplayingMessage)) {
- mDisplayingMessage = feedback.message;
- status.setMessage(mDisplayingMessage);
- }
- } else if (mDisplayingMessage != null) {
- // TODO: Can we check the existing message and only clear it if it's the
- // same as the one we set?
- mDisplayingMessage = null;
- status.setMessage(null);
- status.setErrorMessage(null);
- }
-
- // Tooltip
- if (feedback != null && feedback.tooltip != null) {
- Pair<Boolean,Boolean> position = mCurrentGesture.getTooltipPosition();
- boolean below = position.getFirst();
- if (feedback.tooltipY != null) {
- below = feedback.tooltipY == SegmentType.BOTTOM;
- }
- boolean toRightOf = position.getSecond();
- if (feedback.tooltipX != null) {
- toRightOf = feedback.tooltipX == SegmentType.RIGHT;
- }
- if (mTooltip == null) {
- mTooltip = new GestureToolTip(mCanvas, below, toRightOf);
- }
- mTooltip.update(feedback.tooltip, below, toRightOf);
- } else if (mTooltip != null) {
- mTooltip.dispose();
- mTooltip = null;
- }
- }
-
- /**
- * Returns the current mouse position as a {@link ControlPoint}
- *
- * @return the current mouse position as a {@link ControlPoint}
- */
- public ControlPoint getCurrentControlPoint() {
- return ControlPoint.create(mCanvas, mLastMouseX, mLastMouseY);
- }
-
- /**
- * Returns the current SWT modifier key mask as an {@link IViewRule} modifier mask
- *
- * @return the current SWT modifier key mask as an {@link IViewRule} modifier mask
- */
- public int getRuleModifierMask() {
- int swtMask = mLastStateMask;
- int modifierMask = 0;
- if ((swtMask & SWT.MOD1) != 0) {
- modifierMask |= DropFeedback.MODIFIER1;
- }
- if ((swtMask & SWT.MOD2) != 0) {
- modifierMask |= DropFeedback.MODIFIER2;
- }
- if ((swtMask & SWT.MOD3) != 0) {
- modifierMask |= DropFeedback.MODIFIER3;
- }
- return modifierMask;
- }
-
- /**
- * Helper class which implements the {@link MouseMoveListener},
- * {@link MouseListener} and {@link KeyListener} interfaces.
- */
- private class Listener implements MouseMoveListener, MouseListener, MouseTrackListener,
- KeyListener {
-
- // --- MouseMoveListener ---
-
- @Override
- public void mouseMove(MouseEvent e) {
- mLastMouseX = e.x;
- mLastMouseY = e.y;
- mLastStateMask = e.stateMask;
-
- ControlPoint controlPoint = ControlPoint.create(mCanvas, e);
- if ((e.stateMask & SWT.BUTTON_MASK) != 0) {
- if (mCurrentGesture != null) {
- updateMouse(controlPoint, e);
- mCanvas.redraw();
- }
- } else {
- updateCursor(controlPoint);
- mCanvas.hover(e);
- mCanvas.getPreviewManager().moved(controlPoint);
- }
- }
-
- // --- MouseListener ---
-
- @Override
- public void mouseUp(MouseEvent e) {
- ControlPoint mousePos = ControlPoint.create(mCanvas, e);
-
- if (mCurrentGesture == null) {
- // If clicking on a configuration preview, just process it there
- if (mCanvas.getPreviewManager().click(mousePos)) {
- return;
- }
-
- // Just a click, select
- Pair<SelectionItem, SelectionHandle> handlePair =
- mCanvas.getSelectionManager().findHandle(mousePos);
- if (handlePair == null) {
- mCanvas.getSelectionManager().select(e);
- }
- }
- if (mCurrentGesture == null) {
- updateCursor(mousePos);
- } else if (mCurrentGesture instanceof DropGesture) {
- // Mouse Up shouldn't be delivered in the middle of a drag & drop -
- // but this can happen on some versions of Linux
- // (see http://code.google.com/p/android/issues/detail?id=19057 )
- // and if we process the mouseUp it will abort the remainder of
- // the drag & drop operation, so ignore this event!
- } else {
- finishGesture(mousePos, false);
- }
- mCanvas.redraw();
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- mLastMouseX = e.x;
- mLastMouseY = e.y;
- mLastStateMask = e.stateMask;
-
- // Not yet used. Should be, for Mac and Linux.
- }
-
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- // SWT delivers a double click event even if you click two different buttons
- // in rapid succession. In any case, we only want to let you double click the
- // first button to warp to XML:
- if (e.button == 1) {
- // Warp to the text editor and show the corresponding XML for the
- // double-clicked widget
- LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout();
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
- if (vi != null) {
- mCanvas.show(vi);
- }
- }
- }
-
- // --- MouseTrackListener ---
-
- @Override
- public void mouseEnter(MouseEvent e) {
- ControlPoint mousePos = ControlPoint.create(mCanvas, e);
- mCanvas.getPreviewManager().enter(mousePos);
- }
-
- @Override
- public void mouseExit(MouseEvent e) {
- ControlPoint mousePos = ControlPoint.create(mCanvas, e);
- mCanvas.getPreviewManager().exit(mousePos);
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- }
-
- // --- KeyListener ---
-
- @Override
- public void keyPressed(KeyEvent e) {
- mLastStateMask = e.stateMask;
- // Workaround for the fact that in keyPressed the current state
- // mask is not yet updated
- if (e.keyCode == SWT.SHIFT) {
- mLastStateMask |= SWT.MOD2;
- }
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- if (e.keyCode == SWT.COMMAND) {
- mLastStateMask |= SWT.MOD1;
- }
- } else {
- if (e.keyCode == SWT.CTRL) {
- mLastStateMask |= SWT.MOD1;
- }
- }
-
- // Give gestures a first chance to see and consume the key press
- if (mCurrentGesture != null) {
- // unless it's "Escape", which cancels the gesture
- if (e.keyCode == SWT.ESC) {
- ControlPoint controlPoint = ControlPoint.create(mCanvas,
- mLastMouseX, mLastMouseY);
- finishGesture(controlPoint, true);
- return;
- }
-
- if (mCurrentGesture.keyPressed(e)) {
- return;
- }
- }
-
- // Fall back to canvas actions for the key press
- mCanvas.handleKeyPressed(e);
- }
-
- @Override
- public void keyReleased(KeyEvent e) {
- mLastStateMask = e.stateMask;
- // Workaround for the fact that in keyPressed the current state
- // mask is not yet updated
- if (e.keyCode == SWT.SHIFT) {
- mLastStateMask &= ~SWT.MOD2;
- }
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- if (e.keyCode == SWT.COMMAND) {
- mLastStateMask &= ~SWT.MOD1;
- }
- } else {
- if (e.keyCode == SWT.CTRL) {
- mLastStateMask &= ~SWT.MOD1;
- }
- }
-
- if (mCurrentGesture != null) {
- mCurrentGesture.keyReleased(e);
- }
- }
- }
-
- /** Listener for Drag &amp; Drop events. */
- private class CanvasDropListener implements DropTargetListener {
- public CanvasDropListener() {
- }
-
- /**
- * The cursor has entered the drop target boundaries. {@inheritDoc}
- */
- @Override
- public void dragEnter(DropTargetEvent event) {
- mCanvas.showInvisibleViews(true);
- mCanvas.getEditorDelegate().getGraphicalEditor().dismissHoverPalette();
-
- if (mCurrentGesture == null) {
- Gesture newGesture = mZombieGesture;
- if (newGesture == null) {
- newGesture = new MoveGesture(mCanvas);
- } else {
- mZombieGesture = null;
- }
- startGesture(ControlPoint.create(mCanvas, event),
- newGesture, 0);
- }
-
- if (mCurrentGesture instanceof DropGesture) {
- ((DropGesture) mCurrentGesture).dragEnter(event);
- }
- }
-
- /**
- * The cursor is moving over the drop target. {@inheritDoc}
- */
- @Override
- public void dragOver(DropTargetEvent event) {
- if (mCurrentGesture instanceof DropGesture) {
- ((DropGesture) mCurrentGesture).dragOver(event);
- }
- }
-
- /**
- * The cursor has left the drop target boundaries OR data is about to be
- * dropped. {@inheritDoc}
- */
- @Override
- public void dragLeave(DropTargetEvent event) {
- if (mCurrentGesture instanceof DropGesture) {
- DropGesture dropGesture = (DropGesture) mCurrentGesture;
- dropGesture.dragLeave(event);
- finishGesture(ControlPoint.create(mCanvas, event), true);
- mZombieGesture = dropGesture;
- }
-
- mCanvas.showInvisibleViews(false);
- }
-
- /**
- * The drop is about to be performed. The drop target is given a last
- * chance to change the nature of the drop. {@inheritDoc}
- */
- @Override
- public void dropAccept(DropTargetEvent event) {
- Gesture gesture = mCurrentGesture != null ? mCurrentGesture : mZombieGesture;
- if (gesture instanceof DropGesture) {
- ((DropGesture) gesture).dropAccept(event);
- }
- }
-
- /**
- * The data is being dropped. {@inheritDoc}
- */
- @Override
- public void drop(final DropTargetEvent event) {
- // See if we had a gesture just prior to the drop (we receive a dragLeave
- // right before the drop which we don't know whether means the cursor has
- // left the canvas for good or just before a drop)
- Gesture gesture = mCurrentGesture != null ? mCurrentGesture : mZombieGesture;
- mZombieGesture = null;
-
- if (gesture instanceof DropGesture) {
- ((DropGesture) gesture).drop(event);
-
- finishGesture(ControlPoint.create(mCanvas, event), true);
- }
- }
-
- /**
- * The operation being performed has changed (e.g. modifier key).
- * {@inheritDoc}
- */
- @Override
- public void dragOperationChanged(DropTargetEvent event) {
- if (mCurrentGesture instanceof DropGesture) {
- ((DropGesture) mCurrentGesture).dragOperationChanged(event);
- }
- }
- }
-
- /**
- * Our canvas {@link DragSourceListener}. Handles drag being started and
- * finished and generating the drag data.
- */
- private class CanvasDragSourceListener implements DragSourceListener {
-
- /**
- * The current selection being dragged. This may be a subset of the
- * canvas selection due to the "sanitize" pass. Can be empty but never
- * null.
- */
- private final ArrayList<SelectionItem> mDragSelection = new ArrayList<SelectionItem>();
-
- private SimpleElement[] mDragElements;
-
- /**
- * The user has begun the actions required to drag the widget.
- * <p/>
- * Initiate a drag only if there is one or more item selected. If
- * there's none, try to auto-select the one under the cursor.
- * {@inheritDoc}
- */
- @Override
- public void dragStart(DragSourceEvent e) {
- LayoutPoint p = LayoutPoint.create(mCanvas, e);
- ControlPoint controlPoint = ControlPoint.create(mCanvas, e);
- SelectionManager selectionManager = mCanvas.getSelectionManager();
-
- // See if the mouse is over a selection handle; if so, start a resizing
- // gesture.
- Pair<SelectionItem, SelectionHandle> handle =
- selectionManager.findHandle(controlPoint);
- if (handle != null) {
- startGesture(controlPoint, new ResizeGesture(mCanvas, handle.getFirst(),
- handle.getSecond()), mLastStateMask);
- e.detail = DND.DROP_NONE;
- e.doit = false;
- mCanvas.redraw();
- return;
- }
-
- // We need a selection (simple or multiple) to do any transfer.
- // If there's a selection *and* the cursor is over this selection,
- // use all the currently selected elements.
- // If there is no selection or the cursor is not over a selected
- // element, *change* the selection to match the element under the
- // cursor and use that. If nothing can be selected, abort the drag
- // operation.
- List<SelectionItem> selections = selectionManager.getSelections();
- mDragSelection.clear();
- SelectionItem primary = null;
-
- if (!selections.isEmpty()) {
- // Is the cursor on top of a selected element?
- boolean insideSelection = false;
-
- for (SelectionItem cs : selections) {
- if (!cs.isRoot() && cs.getRect().contains(p.x, p.y)) {
- primary = cs;
- insideSelection = true;
- break;
- }
- }
-
- if (!insideSelection) {
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
- if (vi != null && !vi.isRoot() && !vi.isHidden()) {
- primary = selectionManager.selectSingle(vi);
- insideSelection = true;
- }
- }
-
- if (insideSelection) {
- // We should now have a proper selection that matches the
- // cursor. Let's use this one. We make a copy of it since
- // the "sanitize" pass below might remove some of the
- // selected objects.
- if (selections.size() == 1) {
- // You are dragging just one element - this might or
- // might not be the root, but if it's the root that is
- // fine since we will let you drag the root if it is the
- // only thing you are dragging.
- mDragSelection.addAll(selections);
- } else {
- // Only drag non-root items.
- for (SelectionItem cs : selections) {
- if (!cs.isRoot() && !cs.isHidden()) {
- mDragSelection.add(cs);
- } else if (cs == primary) {
- primary = null;
- }
- }
- }
- }
- }
-
- // If you are dragging a non-selected item, select it
- if (mDragSelection.isEmpty()) {
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
- if (vi != null && !vi.isRoot() && !vi.isHidden()) {
- primary = selectionManager.selectSingle(vi);
- mDragSelection.addAll(selections);
- }
- }
-
- SelectionManager.sanitize(mDragSelection);
-
- e.doit = !mDragSelection.isEmpty();
- int imageCount = mDragSelection.size();
- if (e.doit) {
- mDragElements = SelectionItem.getAsElements(mDragSelection, primary);
- GlobalCanvasDragInfo.getInstance().startDrag(mDragElements,
- mDragSelection.toArray(new SelectionItem[imageCount]),
- mCanvas, new Runnable() {
- @Override
- public void run() {
- mCanvas.getClipboardSupport().deleteSelection("Remove",
- mDragSelection);
- }
- });
- }
-
- // If you drag on the -background-, we make that into a marquee
- // selection
- if (!e.doit || (imageCount == 1
- && (mDragSelection.get(0).isRoot() || mDragSelection.get(0).isHidden()))) {
- boolean toggle = (mLastStateMask & (SWT.CTRL | SWT.SHIFT | SWT.COMMAND)) != 0;
- startGesture(controlPoint,
- new MarqueeGesture(mCanvas, toggle), mLastStateMask);
- e.detail = DND.DROP_NONE;
- e.doit = false;
- } else {
- // Otherwise, the drag means you are moving something
- mCanvas.showInvisibleViews(true);
- startGesture(controlPoint, new MoveGesture(mCanvas), 0);
-
- // Render drag-images: Copy portions of the full screen render.
- Image image = mCanvas.getImageOverlay().getImage();
- if (image != null) {
- /**
- * Transparency of the dragged image ([0-255]). We're using 30%
- * translucency to make the image faint and not obscure the drag
- * feedback below it.
- */
- final byte DRAG_TRANSPARENCY = (byte) (0.3 * 255);
-
- List<Rectangle> rectangles = new ArrayList<Rectangle>(imageCount);
- if (imageCount > 0) {
- ImageData data = image.getImageData();
- Rectangle imageRectangle = new Rectangle(0, 0, data.width, data.height);
- for (SelectionItem item : mDragSelection) {
- Rectangle bounds = item.getRect();
- // Some bounds can be outside the rendered rectangle (for
- // example, in an absolute layout, you can have negative
- // coordinates), so create the intersection of these bounds.
- Rectangle clippedBounds = imageRectangle.intersection(bounds);
- rectangles.add(clippedBounds);
- }
- Rectangle boundingBox = ImageUtils.getBoundingRectangle(rectangles);
- double scale = mCanvas.getHorizontalTransform().getScale();
- e.image = SwtUtils.drawRectangles(image, rectangles, boundingBox, scale,
- DRAG_TRANSPARENCY);
-
- // Set the image offset such that we preserve the relative
- // distance between the mouse pointer and the top left corner of
- // the dragged view
- int deltaX = (int) (scale * (boundingBox.x - p.x));
- int deltaY = (int) (scale * (boundingBox.y - p.y));
- e.offsetX = -deltaX;
- e.offsetY = -deltaY;
-
- // View rules may need to know it as well
- GlobalCanvasDragInfo dragInfo = GlobalCanvasDragInfo.getInstance();
- Rect dragBounds = null;
- int width = (int) (scale * boundingBox.width);
- int height = (int) (scale * boundingBox.height);
- dragBounds = new Rect(deltaX, deltaY, width, height);
- dragInfo.setDragBounds(dragBounds);
-
- // Record the baseline such that we can perform baseline alignment
- // on the node as it's dragged around
- NodeProxy firstNode =
- mCanvas.getNodeFactory().create(mDragSelection.get(0).getViewInfo());
- dragInfo.setDragBaseline(firstNode.getBaseline());
- }
- }
- }
-
- // No hover during drag (since no mouse over events are delivered
- // during a drag to keep the hovers up to date anyway)
- mCanvas.clearHover();
-
- mCanvas.redraw();
- }
-
- /**
- * Callback invoked when data is needed for the event, typically right
- * before drop. The drop side decides what type of transfer to use and
- * this side must now provide the adequate data. {@inheritDoc}
- */
- @Override
- public void dragSetData(DragSourceEvent e) {
- if (TextTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = SelectionItem.getAsText(mCanvas, mDragSelection);
- return;
- }
-
- if (SimpleXmlTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = mDragElements;
- return;
- }
-
- // otherwise we failed
- e.detail = DND.DROP_NONE;
- e.doit = false;
- }
-
- /**
- * Callback invoked when the drop has been finished either way. On a
- * successful move, remove the originating elements.
- */
- @Override
- public void dragFinished(DragSourceEvent e) {
- // Clear the selection
- mDragSelection.clear();
- mDragElements = null;
- GlobalCanvasDragInfo.getInstance().stopDrag();
-
- finishGesture(ControlPoint.create(mCanvas, e), e.detail == DND.DROP_NONE);
- mCanvas.showInvisibleViews(false);
- mCanvas.redraw();
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java
deleted file mode 100644
index a49e79cbf..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureToolTip.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2011 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 org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.FontData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * A dedicated tooltip used during gestures, for example to show the resize dimensions.
- * <p>
- * This is necessary because {@link org.eclipse.jface.window.ToolTip} causes flicker when
- * used to dynamically update the position and text of the tip, and it does not seem to
- * have setter methods to update the text or position without recreating the tip.
- */
-public class GestureToolTip {
- /** Minimum number of milliseconds to wait between alignment changes */
- private static final int TIMEOUT_MS = 750;
-
- /**
- * The alpha to use for the tooltip window (which sadly will apply to the tooltip text
- * as well.)
- */
- private static final int SHELL_TRANSPARENCY = 220;
-
- /** The size of the font displayed in the tooltip */
- private static final int FONT_SIZE = 9;
-
- /** Horizontal delta from the mouse cursor to shift the tooltip by */
- private static final int OFFSET_X = 20;
-
- /** Vertical delta from the mouse cursor to shift the tooltip by */
- private static final int OFFSET_Y = 20;
-
- /** The label which displays the tooltip */
- private CLabel mLabel;
-
- /** The shell holding the tooltip */
- private Shell mShell;
-
- /** The font shown in the label; held here such that it can be disposed of after use */
- private Font mFont;
-
- /** Is the tooltip positioned below the given anchor? */
- private boolean mBelow;
-
- /** Is the tooltip positioned to the right of the given anchor? */
- private boolean mToRightOf;
-
- /** Is an alignment change pending? */
- private boolean mTimerPending;
-
- /** The new value for {@link #mBelow} when the timer expires */
- private boolean mPendingBelow;
-
- /** The new value for {@link #mToRightOf} when the timer expires */
- private boolean mPendingRight;
-
- /** The time stamp (from {@link System#currentTimeMillis()} of the last alignment change */
- private long mLastAlignmentTime;
-
- /**
- * Creates a new tooltip over the given parent with the given relative position.
- *
- * @param parent the parent control
- * @param below if true, display the tooltip below the mouse cursor otherwise above
- * @param toRightOf if true, display the tooltip to the right of the mouse cursor,
- * otherwise to the left
- */
- public GestureToolTip(Composite parent, boolean below, boolean toRightOf) {
- mBelow = below;
- mToRightOf = toRightOf;
- mLastAlignmentTime = System.currentTimeMillis();
-
- mShell = new Shell(parent.getShell(), SWT.ON_TOP | SWT.TOOL | SWT.NO_FOCUS);
- mShell.setLayout(new FillLayout());
- mShell.setAlpha(SHELL_TRANSPARENCY);
-
- Display display = parent.getDisplay();
- mLabel = new CLabel(mShell, SWT.SHADOW_NONE);
- mLabel.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
- mLabel.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
-
- Font systemFont = display.getSystemFont();
- FontData[] fd = systemFont.getFontData();
- for (int i = 0; i < fd.length; i++) {
- fd[i].setHeight(FONT_SIZE);
- }
- mFont = new Font(display, fd);
- mLabel.setFont(mFont);
-
- mShell.setVisible(false);
- }
-
- /**
- * Show the tooltip at the given position and with the given text. Note that the
- * position may not be applied immediately; to prevent flicker alignment changes
- * are queued up with a timer (unless it's been a while since the last change, in
- * which case the update is applied immediately.)
- *
- * @param text the new text to be displayed
- * @param below if true, display the tooltip below the mouse cursor otherwise above
- * @param toRightOf if true, display the tooltip to the right of the mouse cursor,
- * otherwise to the left
- */
- public void update(final String text, boolean below, boolean toRightOf) {
- // If the alignment has not changed recently, just apply the change immediately
- // instead of within a delay
- if (!mTimerPending && (below != mBelow || toRightOf != mToRightOf)
- && (System.currentTimeMillis() - mLastAlignmentTime >= TIMEOUT_MS)) {
- mBelow = below;
- mToRightOf = toRightOf;
- mLastAlignmentTime = System.currentTimeMillis();
- }
-
- Point location = mShell.getDisplay().getCursorLocation();
-
- mLabel.setText(text);
-
- // Pack the label to its minimum size -- unless we are positioning the tooltip
- // on the left. Because of the way SWT works (at least on the OSX) this sometimes
- // creates flicker, because when we switch to a longer string (such as when
- // switching from "52dp" to "wrap_content" during a resize) the window size will
- // change first, and then the location will update later - so there will be a
- // brief flash of the longer label before it is moved to the right position on the
- // left. To work around this, we simply pass false to pack such that it will reuse
- // its cached size, which in practice means that for labels on the right, the
- // label will grow but not shrink.
- // This workaround is disabled because it doesn't work well in Eclipse 3.5; the
- // labels don't grow when they should. Re-enable when we drop 3.5 support.
- //boolean changed = mToRightOf;
- boolean changed = true;
-
- mShell.pack(changed);
- Point size = mShell.getSize();
-
- // Position the tooltip to the left or right, and above or below, according
- // to the saved state of these flags, not the current parameters. We don't want
- // to flicker, instead we react on a timer to changes in alignment below.
- if (mBelow) {
- location.y += OFFSET_Y;
- } else {
- location.y -= OFFSET_Y;
- location.y -= size.y;
- }
-
- if (mToRightOf) {
- location.x += OFFSET_X;
- } else {
- location.x -= OFFSET_X;
- location.x -= size.x;
- }
-
- mShell.setLocation(location);
-
- if (!mShell.isVisible()) {
- mShell.setVisible(true);
- }
-
- // Has the orientation changed?
- mPendingBelow = below;
- mPendingRight = toRightOf;
- if (below != mBelow || toRightOf != mToRightOf) {
- // Yes, so schedule a timer (unless one is already scheduled)
- if (!mTimerPending) {
- mTimerPending = true;
- final Runnable timer = new Runnable() {
- @Override
- public void run() {
- mTimerPending = false;
- // Check whether the alignment is still different than the target
- // (since we may change back and forth repeatedly during the timeout)
- if (mBelow != mPendingBelow || mToRightOf != mPendingRight) {
- mBelow = mPendingBelow;
- mToRightOf = mPendingRight;
- mLastAlignmentTime = System.currentTimeMillis();
- if (mShell != null && mShell.isVisible()) {
- update(text, mBelow, mToRightOf);
- }
- }
- }
- };
- mShell.getDisplay().timerExec(TIMEOUT_MS, timer);
- }
- }
- }
-
- /** Hide the tooltip and dispose of any associated resources */
- public void dispose() {
- mShell.dispose();
- mFont.dispose();
-
- mShell = null;
- mFont = null;
- mLabel = null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java
deleted file mode 100644
index b918b00bf..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java
+++ /dev/null
@@ -1,182 +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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.Rect;
-
-
-/**
- * This singleton is used to keep track of drag'n'drops initiated within this
- * session of Eclipse. A drag can be initiated from a palette or from a canvas
- * and its content is an Android View fully-qualified class name.
- * <p/>
- * Overall this is a workaround: the issue is that the drag'n'drop SWT API does not
- * allow us to know the transfered data during the initial drag -- only when the
- * data is dropped do we know what it is about (and to be more exact there is a workaround
- * to do just that which works on Windows but not on Linux/Mac SWT).
- * <p/>
- * In the GLE we'd like to adjust drag feedback to the data being actually dropped.
- * The singleton instance of this class will be used to track the data currently dragged
- * off a canvas or its palette and then set back to null when the drag'n'drop is finished.
- * <p/>
- * Note that when a drag starts in one instance of Eclipse and the dragOver/drop is done
- * in a <em>separate</em> instance of Eclipse, the dragged FQCN won't be registered here
- * and will be null.
- */
-final class GlobalCanvasDragInfo {
-
- private static final GlobalCanvasDragInfo sInstance = new GlobalCanvasDragInfo();
-
- private SimpleElement[] mCurrentElements = null;
- private SelectionItem[] mCurrentSelection;
- private Object mSourceCanvas = null;
- private Runnable mRemoveSourceHandler;
- private Rect mDragBounds;
- private int mDragBaseline = -1;
-
- /** Private constructor. Use {@link #getInstance()} to retrieve the singleton. */
- private GlobalCanvasDragInfo() {
- // pass
- }
-
- /** Returns the singleton instance. */
- public static GlobalCanvasDragInfo getInstance() {
- return sInstance;
- }
-
- /**
- * Registers the XML elements being dragged.
- *
- * @param elements The elements being dragged
- * @param primary the "primary" element among the elements; when there is a
- * single item dragged this will be the same, but in
- * multi-selection it will be the element under the mouse as the
- * selection was initiated
- * @param selection The selection (which can be null, for example when the
- * user drags from the palette)
- * @param sourceCanvas An object representing the source we are dragging
- * from (used for identity comparisons only)
- * @param removeSourceHandler A runnable (or null) which can clean up the
- * source. It should only be invoked if the drag operation is a
- * move, not a copy.
- */
- public void startDrag(
- @NonNull SimpleElement[] elements,
- @Nullable SelectionItem[] selection,
- @Nullable Object sourceCanvas,
- @Nullable Runnable removeSourceHandler) {
- mCurrentElements = elements;
- mCurrentSelection = selection;
- mSourceCanvas = sourceCanvas;
- mRemoveSourceHandler = removeSourceHandler;
- }
-
- /** Unregisters elements being dragged. */
- public void stopDrag() {
- mCurrentElements = null;
- mCurrentSelection = null;
- mSourceCanvas = null;
- mRemoveSourceHandler = null;
- mDragBounds = null;
- }
-
- public boolean isDragging() {
- return mCurrentElements != null;
- }
-
- /** Returns the elements being dragged. */
- @NonNull
- public SimpleElement[] getCurrentElements() {
- return mCurrentElements;
- }
-
- /** Returns the selection originally dragged.
- * Can be null if the drag did not start in a canvas.
- */
- public SelectionItem[] getCurrentSelection() {
- return mCurrentSelection;
- }
-
- /**
- * Returns the object that call {@link #startDrag(SimpleElement[], SelectionItem[], Object)}.
- * Can be null.
- * This is not meant to access the object indirectly, it is just meant to compare if the
- * source and the destination of the drag'n'drop are the same, so object identity
- * is all what matters.
- */
- public Object getSourceCanvas() {
- return mSourceCanvas;
- }
-
- /**
- * Removes source of the drag. This should only be called when the drag and
- * drop operation is a move (not a copy).
- */
- public void removeSource() {
- if (mRemoveSourceHandler != null) {
- mRemoveSourceHandler.run();
- mRemoveSourceHandler = null;
- }
- }
-
- /**
- * Get the bounds of the drag, relative to the starting mouse position. For example,
- * if you have a rectangular view of size 100x80, and you start dragging at position
- * (15,20) from the top left corner of this rectangle, then the drag bounds would be
- * (-15,-20, 100x80).
- * <p>
- * NOTE: The coordinate units will be in SWT/control pixels, not Android view pixels.
- * In other words, they are affected by the canvas zoom: If you zoom the view and the
- * bounds of a view grow, the drag bounds will be larger.
- *
- * @return the drag bounds, or null if there are no bounds for the current drag
- */
- public Rect getDragBounds() {
- return mDragBounds;
- }
-
- /**
- * Set the bounds of the drag, relative to the starting mouse position. See
- * {@link #getDragBounds()} for details on the semantics of the drag bounds.
- *
- * @param dragBounds the new drag bounds, or null if there are no drag bounds
- */
- public void setDragBounds(Rect dragBounds) {
- mDragBounds = dragBounds;
- }
-
- /**
- * Returns the baseline of the drag, or -1 if not applicable
- *
- * @return the current SWT modifier key mask as an {@link IViewRule} modifier mask
- */
- public int getDragBaseline() {
- return mDragBaseline;
- }
-
- /**
- * Sets the baseline of the drag
- *
- * @param baseline the new baseline
- */
- public void setDragBaseline(int baseline) {
- mDragBaseline = baseline;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
deleted file mode 100644
index 0f5762da6..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ /dev/null
@@ -1,2937 +0,0 @@
-/*
- * Copyright (C) 2009 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_PKG;
-import static com.android.SdkConstants.ANDROID_STRING_PREFIX;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_CONTEXT;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.FD_GEN_SOURCES;
-import static com.android.SdkConstants.GRID_LAYOUT;
-import static com.android.SdkConstants.SCROLL_VIEW;
-import static com.android.SdkConstants.STRING_PREFIX;
-import static com.android.SdkConstants.VALUE_FALSE;
-import static com.android.SdkConstants.VALUE_FILL_PARENT;
-import static com.android.SdkConstants.VALUE_MATCH_PARENT;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.ide.common.rendering.RenderSecurityManager.ENABLED_PROPERTY;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE_STATE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_FOLDER;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_TARGET;
-import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor.viewNeedsPackage;
-import static org.eclipse.wb.core.controls.flyout.IFlyoutPreferences.DOCK_EAST;
-import static org.eclipse.wb.core.controls.flyout.IFlyoutPreferences.DOCK_WEST;
-import static org.eclipse.wb.core.controls.flyout.IFlyoutPreferences.STATE_COLLAPSED;
-import static org.eclipse.wb.core.controls.flyout.IFlyoutPreferences.STATE_OPEN;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.RenderSecurityException;
-import com.android.ide.common.rendering.RenderSecurityManager;
-import com.android.ide.common.rendering.StaticRenderSession;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.common.sdk.LoadStatus;
-import com.android.ide.eclipse.adt.AdtConstants;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.IPageImageProvider;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate;
-import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ChangeFlags;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
-import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationClient;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationMatcher;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PaletteControl.PalettePage;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory;
-import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
-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.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
-import com.android.resources.Density;
-import com.android.resources.ResourceFolderType;
-import com.android.resources.ResourceType;
-import com.android.sdklib.IAndroidTarget;
-import com.android.tools.lint.detector.api.LintUtils;
-import com.android.utils.Pair;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jdt.core.IClasspathEntry;
-import org.eclipse.jdt.core.IJavaElement;
-import org.eclipse.jdt.core.IJavaModelMarker;
-import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IPackageFragment;
-import org.eclipse.jdt.core.IPackageFragmentRoot;
-import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.internal.ui.preferences.BuildPathsPropertyPage;
-import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction;
-import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
-import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.source.ISourceViewer;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.ISelectionProvider;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.custom.StyleRange;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.text.edits.MalformedTreeException;
-import org.eclipse.text.edits.MultiTextEdit;
-import org.eclipse.text.edits.ReplaceEdit;
-import org.eclipse.ui.IActionBars;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IEditorSite;
-import org.eclipse.ui.INullSelectionListener;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.IWorkbenchPartSite;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.dialogs.PreferencesUtil;
-import org.eclipse.ui.ide.IDE;
-import org.eclipse.ui.part.EditorPart;
-import org.eclipse.ui.part.FileEditorInput;
-import org.eclipse.ui.part.IPageSite;
-import org.eclipse.ui.part.PageBookView;
-import org.eclipse.wb.core.controls.flyout.FlyoutControlComposite;
-import org.eclipse.wb.core.controls.flyout.IFlyoutListener;
-import org.eclipse.wb.core.controls.flyout.PluginFlyoutPreferences;
-import org.eclipse.wb.internal.core.editor.structure.PageSiteComposite;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Graphical layout editor part, version 2.
- * <p/>
- * The main component of the editor part is the {@link LayoutCanvasViewer}, which
- * actually delegates its work to the {@link LayoutCanvas} control.
- * <p/>
- * The {@link LayoutCanvasViewer} is set as the site's {@link ISelectionProvider}:
- * when the selection changes in the canvas, it is thus broadcasted to anyone listening
- * on the site's selection service.
- * <p/>
- * This part is also an {@link ISelectionListener}. It listens to the site's selection
- * service and thus receives selection changes from itself as well as the associated
- * outline and property sheet (these are registered by {@link LayoutEditorDelegate#delegateGetAdapter(Class)}).
- *
- * @since GLE2
- */
-public class GraphicalEditorPart extends EditorPart
- implements IPageImageProvider, INullSelectionListener, IFlyoutListener,
- ConfigurationClient {
-
- /*
- * Useful notes:
- * To understand Drag & drop:
- * http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
- *
- * To understand the site's selection listener, selection provider, and the
- * confusion of different-yet-similarly-named interfaces, consult this:
- * http://www.eclipse.org/articles/Article-WorkbenchSelections/article.html
- *
- * To summarize the selection mechanism:
- * - The workbench site selection service can be seen as "centralized"
- * service that registers selection providers and selection listeners.
- * - The editor part and the outline are selection providers.
- * - The editor part, the outline and the property sheet are listeners
- * which all listen to each others indirectly.
- */
-
- /** Property key for the window preferences for the structure flyout */
- private static final String PREF_STRUCTURE = "design.structure"; //$NON-NLS-1$
-
- /** Property key for the window preferences for the palette flyout */
- private static final String PREF_PALETTE = "design.palette"; //$NON-NLS-1$
-
- /**
- * Session-property on files which specifies the initial config state to be used on
- * this file
- */
- public final static QualifiedName NAME_INITIAL_STATE =
- new QualifiedName(AdtPlugin.PLUGIN_ID, "initialstate");//$NON-NLS-1$
-
- /**
- * Session-property on files which specifies the inclusion-context (reference to another layout
- * which should be "including" this layout) when the file is opened
- */
- public final static QualifiedName NAME_INCLUDE =
- new QualifiedName(AdtPlugin.PLUGIN_ID, "includer");//$NON-NLS-1$
-
- /** Reference to the layout editor */
- private final LayoutEditorDelegate mEditorDelegate;
-
- /** Reference to the file being edited. Can also be used to access the {@link IProject}. */
- private IFile mEditedFile;
-
- /** The configuration chooser at the top of the layout editor. */
- private ConfigurationChooser mConfigChooser;
-
- /** The sash that splits the palette from the error view.
- * The error view is shown only when needed. */
- private SashForm mSashError;
-
- /** The palette displayed on the left of the sash. */
- private PaletteControl mPalette;
-
- /** The layout canvas displayed to the right of the sash. */
- private LayoutCanvasViewer mCanvasViewer;
-
- /** The Rules Engine associated with this editor. It is project-specific. */
- private RulesEngine mRulesEngine;
-
- /** Styled text displaying the most recent error in the error view. */
- private StyledText mErrorLabel;
-
- /**
- * The resource reference to a file that should surround this file (e.g. include this file
- * visually), or null if not applicable
- */
- private Reference mIncludedWithin;
-
- private Map<ResourceType, Map<String, ResourceValue>> mConfiguredFrameworkRes;
- private Map<ResourceType, Map<String, ResourceValue>> mConfiguredProjectRes;
- private ProjectCallback mProjectCallback;
- private boolean mNeedsRecompute = false;
- private TargetListener mTargetListener;
- private ResourceResolver mResourceResolver;
- private ReloadListener mReloadListener;
- private int mMinSdkVersion;
- private int mTargetSdkVersion;
- private LayoutActionBar mActionBar;
- private OutlinePage mOutlinePage;
- private FlyoutControlComposite mStructureFlyout;
- private FlyoutControlComposite mPaletteComposite;
- private PropertyFactory mPropertyFactory;
- private boolean mRenderedOnce;
- private final Object mCredential = new Object();
-
- /**
- * Flags which tracks whether this editor is currently active which is set whenever
- * {@link #activated()} is called and clear whenever {@link #deactivated()} is called.
- * This is used to suppress repeated calls to {@link #activate()} to avoid doing
- * unnecessary work.
- */
- private boolean mActive;
-
- /**
- * Constructs a new {@link GraphicalEditorPart}
- *
- * @param editorDelegate the associated XML editor delegate
- */
- public GraphicalEditorPart(@NonNull LayoutEditorDelegate editorDelegate) {
- mEditorDelegate = editorDelegate;
- setPartName("Graphical Layout");
- }
-
- // ------------------------------------
- // Methods overridden from base classes
- //------------------------------------
-
- /**
- * Initializes the editor part with a site and input.
- * {@inheritDoc}
- */
- @Override
- public void init(IEditorSite site, IEditorInput input) throws PartInitException {
- setSite(site);
- useNewEditorInput(input);
-
- if (mTargetListener == null) {
- mTargetListener = new TargetListener();
- AdtPlugin.getDefault().addTargetListener(mTargetListener);
-
- // Trigger a check to see if the SDK needs to be reloaded (which will
- // invoke onSdkLoaded asynchronously as needed).
- AdtPlugin.getDefault().refreshSdk();
- }
- }
-
- private void useNewEditorInput(IEditorInput input) throws PartInitException {
- // The contract of init() mentions we need to fail if we can't understand the input.
- if (!(input instanceof FileEditorInput)) {
- throw new PartInitException("Input is not of type FileEditorInput: " + //$NON-NLS-1$
- input == null ? "null" : input.toString()); //$NON-NLS-1$
- }
- }
-
- @Override
- public Image getPageImage() {
- return IconFactory.getInstance().getIcon("editor_page_design"); //$NON-NLS-1$
- }
-
- @Override
- public void createPartControl(Composite parent) {
-
- Display d = parent.getDisplay();
-
- GridLayout gl = new GridLayout(1, false);
- parent.setLayout(gl);
- gl.marginHeight = gl.marginWidth = 0;
-
- // Check whether somebody has requested an initial state for the newly opened file.
- // The initial state is a serialized version of the state compatible with
- // {@link ConfigurationComposite#CONFIG_STATE}.
- String initialState = null;
- IFile file = mEditedFile;
- if (file == null) {
- IEditorInput input = mEditorDelegate.getEditor().getEditorInput();
- if (input instanceof FileEditorInput) {
- file = ((FileEditorInput) input).getFile();
- }
- }
-
- if (file != null) {
- try {
- initialState = (String) file.getSessionProperty(NAME_INITIAL_STATE);
- if (initialState != null) {
- // Only use once
- file.setSessionProperty(NAME_INITIAL_STATE, null);
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't read session property %1$s", NAME_INITIAL_STATE);
- }
- }
-
- IPreferenceStore preferenceStore = AdtPlugin.getDefault().getPreferenceStore();
- PluginFlyoutPreferences preferences;
- preferences = new PluginFlyoutPreferences(preferenceStore, PREF_PALETTE);
- preferences.initializeDefaults(DOCK_WEST, STATE_OPEN, 200);
- mPaletteComposite = new FlyoutControlComposite(parent, SWT.NONE, preferences);
- mPaletteComposite.setTitleText("Palette");
- mPaletteComposite.setMinWidth(100);
- Composite paletteParent = mPaletteComposite.getFlyoutParent();
- Composite editorParent = mPaletteComposite.getClientParent();
- mPaletteComposite.setListener(this);
-
- mPaletteComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- PageSiteComposite paletteComposite = new PageSiteComposite(paletteParent, SWT.BORDER);
- paletteComposite.setTitleText("Palette");
- paletteComposite.setTitleImage(IconFactory.getInstance().getIcon("palette"));
- PalettePage decor = new PalettePage(this);
- paletteComposite.setPage(decor);
- mPalette = (PaletteControl) decor.getControl();
- decor.createToolbarItems(paletteComposite.getToolBar());
-
- // Create the shared structure+editor area
- preferences = new PluginFlyoutPreferences(preferenceStore, PREF_STRUCTURE);
- preferences.initializeDefaults(DOCK_EAST, STATE_OPEN, 300);
- mStructureFlyout = new FlyoutControlComposite(editorParent, SWT.NONE, preferences);
- mStructureFlyout.setTitleText("Structure");
- mStructureFlyout.setMinWidth(150);
- mStructureFlyout.setListener(this);
-
- Composite layoutBarAndCanvas = new Composite(mStructureFlyout.getClientParent(), SWT.NONE);
- GridLayout gridLayout = new GridLayout(1, false);
- gridLayout.horizontalSpacing = 0;
- gridLayout.verticalSpacing = 0;
- gridLayout.marginWidth = 0;
- gridLayout.marginHeight = 0;
- layoutBarAndCanvas.setLayout(gridLayout);
-
- mConfigChooser = new ConfigurationChooser(this, layoutBarAndCanvas, initialState);
- mConfigChooser.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- mActionBar = new LayoutActionBar(layoutBarAndCanvas, SWT.NONE, this);
- GridData detailsData = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
- mActionBar.setLayoutData(detailsData);
- if (file != null) {
- mActionBar.updateErrorIndicator(file);
- }
-
- mSashError = new SashForm(layoutBarAndCanvas, SWT.VERTICAL | SWT.BORDER);
- mSashError.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- mCanvasViewer = new LayoutCanvasViewer(mEditorDelegate, mRulesEngine, mSashError, SWT.NONE);
- mSashError.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- mErrorLabel = new StyledText(mSashError, SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL);
- mErrorLabel.setEditable(false);
- mErrorLabel.setBackground(d.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
- mErrorLabel.setForeground(d.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
- mErrorLabel.addMouseListener(new ErrorLabelListener());
-
- mSashError.setWeights(new int[] { 80, 20 });
- mSashError.setMaximizedControl(mCanvasViewer.getControl());
-
- // Create the structure views. We really should do this *lazily*, but that
- // seems to cause a bug: property sheet won't update. Track this down later.
- createStructureViews(mStructureFlyout.getFlyoutParent(), false);
- showStructureViews(false, false, false);
-
- // Initialize the state
- reloadPalette();
-
- IWorkbenchPartSite site = getSite();
- site.setSelectionProvider(mCanvasViewer);
- site.getPage().addSelectionListener(this);
- }
-
- private void createStructureViews(Composite parent, boolean createPropertySheet) {
- mOutlinePage = new OutlinePage(this);
- mOutlinePage.setShowPropertySheet(createPropertySheet);
- mOutlinePage.setShowHeader(true);
-
- IPageSite pageSite = new IPageSite() {
-
- @Override
- public IWorkbenchPage getPage() {
- return getSite().getPage();
- }
-
- @Override
- public ISelectionProvider getSelectionProvider() {
- return getSite().getSelectionProvider();
- }
-
- @Override
- public Shell getShell() {
- return getSite().getShell();
- }
-
- @Override
- public IWorkbenchWindow getWorkbenchWindow() {
- return getSite().getWorkbenchWindow();
- }
-
- @Override
- public void setSelectionProvider(ISelectionProvider provider) {
- getSite().setSelectionProvider(provider);
- }
-
- @Override
- public Object getAdapter(Class adapter) {
- return getSite().getAdapter(adapter);
- }
-
- @Override
- public Object getService(Class api) {
- return getSite().getService(api);
- }
-
- @Override
- public boolean hasService(Class api) {
- return getSite().hasService(api);
- }
-
- @Override
- public void registerContextMenu(String menuId, MenuManager menuManager,
- ISelectionProvider selectionProvider) {
- }
-
- @Override
- public IActionBars getActionBars() {
- return null;
- }
- };
- mOutlinePage.init(pageSite);
- mOutlinePage.createControl(parent);
- mOutlinePage.addSelectionChangedListener(new ISelectionChangedListener() {
- @Override
- public void selectionChanged(SelectionChangedEvent event) {
- getCanvasControl().getSelectionManager().setSelection(event.getSelection());
- }
- });
- }
-
- /** Shows the embedded (within the layout editor) outline and or properties */
- void showStructureViews(final boolean showOutline, final boolean showProperties,
- final boolean updateLayout) {
- Display display = mConfigChooser.getDisplay();
- if (display.getThread() != Thread.currentThread()) {
- display.asyncExec(new Runnable() {
- @Override
- public void run() {
- if (!mConfigChooser.isDisposed()) {
- showStructureViews(showOutline, showProperties, updateLayout);
- }
- }
-
- });
- return;
- }
-
- boolean show = showOutline || showProperties;
-
- Control[] children = mStructureFlyout.getFlyoutParent().getChildren();
- if (children.length == 0) {
- if (show) {
- createStructureViews(mStructureFlyout.getFlyoutParent(), showProperties);
- }
- return;
- }
-
- mOutlinePage.setShowPropertySheet(showProperties);
-
- Control control = children[0];
- if (show != control.getVisible()) {
- control.setVisible(show);
- mOutlinePage.setActive(show); // disable/re-enable listeners etc
- if (show) {
- ISelection selection = getCanvasControl().getSelectionManager().getSelection();
- mOutlinePage.selectionChanged(getEditorDelegate().getEditor(), selection);
- }
- if (updateLayout) {
- mStructureFlyout.layout();
- }
- // TODO: *dispose* the non-showing widgets to save memory?
- }
- }
-
- /**
- * Returns the property factory associated with this editor
- *
- * @return the factory
- */
- @NonNull
- public PropertyFactory getPropertyFactory() {
- if (mPropertyFactory == null) {
- mPropertyFactory = new PropertyFactory(this);
- }
-
- return mPropertyFactory;
- }
-
- /**
- * 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 (mOutlinePage != null) {
- mOutlinePage.setModel(rootViewInfo);
- }
- }
-
- /**
- * Listens to workbench selections that does NOT come from {@link LayoutEditorDelegate}
- * (those are generated by ourselves).
- * <p/>
- * Selection can be null, as indicated by this class implementing
- * {@link INullSelectionListener}.
- */
- @Override
- public void selectionChanged(IWorkbenchPart part, ISelection selection) {
- Object delegate = part instanceof IEditorPart ?
- LayoutEditorDelegate.fromEditor((IEditorPart) part) : null;
- if (delegate == null) {
- if (part instanceof PageBookView) {
- PageBookView pbv = (PageBookView) part;
- org.eclipse.ui.part.IPage currentPage = pbv.getCurrentPage();
- if (currentPage instanceof OutlinePage) {
- LayoutCanvas canvas = getCanvasControl();
- if (canvas != null && canvas.getOutlinePage() != currentPage) {
- // The notification is not for this view; ignore
- // (can happen when there are multiple pages simultaneously
- // visible)
- return;
- }
- }
- }
- mCanvasViewer.setSelection(selection);
- }
- }
-
- @Override
- public void dispose() {
- getSite().getPage().removeSelectionListener(this);
- getSite().setSelectionProvider(null);
-
- if (mTargetListener != null) {
- AdtPlugin.getDefault().removeTargetListener(mTargetListener);
- mTargetListener = null;
- }
-
- if (mReloadListener != null) {
- LayoutReloadMonitor.getMonitor().removeListener(mReloadListener);
- mReloadListener = null;
- }
-
- if (mCanvasViewer != null) {
- mCanvasViewer.dispose();
- mCanvasViewer = null;
- }
- super.dispose();
- }
-
- /**
- * Select the visual element corresponding to the given XML node
- * @param xmlNode The Node whose element we want to select
- */
- public void select(Node xmlNode) {
- mCanvasViewer.getCanvas().getSelectionManager().select(xmlNode);
- }
-
- // ---- Implements ConfigurationClient ----
- @Override
- public void aboutToChange(int flags) {
- if ((flags & CFG_TARGET) != 0) {
- IAndroidTarget oldTarget = mConfigChooser.getConfiguration().getTarget();
- preRenderingTargetChangeCleanUp(oldTarget);
- }
- }
-
- @Override
- public boolean changed(int flags) {
- mConfiguredFrameworkRes = mConfiguredProjectRes = null;
- mResourceResolver = null;
-
- if (mEditedFile == null) {
- return true;
- }
-
- // Before doing the normal process, test for the following case.
- // - the editor is being opened (or reset for a new input)
- // - the file being opened is not the best match for any possible configuration
- // - another random compatible config was chosen in the config composite.
- // The result is that 'match' will not be the file being edited, but because this is not
- // due to a config change, we should not trigger opening the actual best match (also,
- // because the editor is still opening the MatchingStrategy woudln't answer true
- // and the best match file would open in a different editor).
- // So the solution is that if the editor is being created, we just call recomputeLayout
- // without looking for a better matching layout file.
- if (mEditorDelegate.getEditor().isCreatingPages()) {
- recomputeLayout();
- } else {
- boolean affectsFileSelection = (flags & Configuration.MASK_FILE_ATTRS) != 0;
- IFile best = null;
- // get the resources of the file's project.
- if (affectsFileSelection) {
- best = ConfigurationMatcher.getBestFileMatch(mConfigChooser);
- }
- if (best != null) {
- if (!best.equals(mEditedFile)) {
- try {
- // tell the editor that the next replacement file is due to a config
- // change.
- mEditorDelegate.setNewFileOnConfigChange(true);
-
- boolean reuseEditor = AdtPrefs.getPrefs().isSharedLayoutEditor();
- if (!reuseEditor) {
- String data = ConfigurationDescription.getDescription(best);
- if (data == null) {
- // Not previously opened: duplicate the current state as
- // much as possible
- data = mConfigChooser.getConfiguration().toPersistentString();
- ConfigurationDescription.setDescription(best, data);
- }
- }
-
- // ask the IDE to open the replacement file.
- IDE.openEditor(getSite().getWorkbenchWindow().getActivePage(), best,
- CommonXmlEditor.ID);
-
- // we're done!
- return reuseEditor;
- } catch (PartInitException e) {
- // FIXME: do something!
- }
- }
-
- // at this point, we have not opened a new file.
-
- // Store the state in the current file
- mConfigChooser.saveConstraints();
-
- // Even though the layout doesn't change, the config changed, and referenced
- // resources need to be updated.
- recomputeLayout();
- } else if (affectsFileSelection) {
- // display the error.
- Configuration configuration = mConfigChooser.getConfiguration();
- FolderConfiguration currentConfig = configuration.getFullConfig();
- displayError(
- "No resources match the configuration\n" +
- " \n" +
- "\t%1$s\n" +
- " \n" +
- "Change the configuration or create:\n" +
- " \n" +
- "\tres/%2$s/%3$s\n" +
- " \n" +
- "You can also click the 'Create New...' item in the configuration " +
- "dropdown menu above.",
- currentConfig.toDisplayString(),
- currentConfig.getFolderName(ResourceFolderType.LAYOUT),
- mEditedFile.getName());
- } else {
- // Something else changed, such as the theme - just recompute existing
- // layout
- mConfigChooser.saveConstraints();
- recomputeLayout();
- }
- }
-
- if ((flags & CFG_TARGET) != 0) {
- Configuration configuration = mConfigChooser.getConfiguration();
- IAndroidTarget target = configuration.getTarget();
- Sdk current = Sdk.getCurrent();
- if (current != null) {
- AndroidTargetData targetData = current.getTargetData(target);
- updateCapabilities(targetData);
- }
- }
-
- if ((flags & (CFG_DEVICE | CFG_DEVICE_STATE)) != 0) {
- // When the device changes, zoom the view to fit, but only up to 100% (e.g. zoom
- // out to fit the content, or zoom back in if we were zoomed out more from the
- // previous view, but only up to 100% such that we never blow up pixels
- if (mActionBar.isZoomingAllowed()) {
- getCanvasControl().setFitScale(true, true /*allowZoomIn*/);
- }
- }
-
- reloadPalette();
-
- getCanvasControl().getPreviewManager().configurationChanged(flags);
-
- return true;
- }
-
- @Override
- public void setActivity(@NonNull String activity) {
- ManifestInfo manifest = ManifestInfo.get(mEditedFile.getProject());
- String pkg = manifest.getPackage();
- if (activity.startsWith(pkg) && activity.length() > pkg.length()
- && activity.charAt(pkg.length()) == '.') {
- activity = activity.substring(pkg.length());
- }
- CommonXmlEditor editor = getEditorDelegate().getEditor();
- Element element = editor.getUiRootNode().getXmlDocument().getDocumentElement();
- AdtUtils.setToolsAttribute(editor,
- element, "Choose Activity", ATTR_CONTEXT,
- activity, false /*reveal*/, false /*append*/);
- }
-
- /**
- * Returns a {@link ProjectResources} for the framework resources based on the current
- * configuration selection.
- * @return the framework resources or null if not found.
- */
- @Override
- @Nullable
- public ResourceRepository getFrameworkResources() {
- return getFrameworkResources(getRenderingTarget());
- }
-
- /**
- * Returns a {@link ProjectResources} for the framework resources of a given
- * target.
- * @param target the target for which to return the framework resources.
- * @return the framework resources or null if not found.
- */
- @Override
- @Nullable
- public ResourceRepository getFrameworkResources(@Nullable IAndroidTarget target) {
- if (target != null) {
- AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
-
- if (data != null) {
- return data.getFrameworkResources();
- }
- }
-
- return null;
- }
-
- @Override
- @Nullable
- public ProjectResources getProjectResources() {
- if (mEditedFile != null) {
- ResourceManager manager = ResourceManager.getInstance();
- return manager.getProjectResources(mEditedFile.getProject());
- }
-
- return null;
- }
-
-
- @Override
- @NonNull
- public Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources() {
- if (mConfiguredFrameworkRes == null && mConfigChooser != null) {
- ResourceRepository frameworkRes = getFrameworkResources();
-
- if (frameworkRes == null) {
- AdtPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework");
- } else {
- // get the framework resource values based on the current config
- mConfiguredFrameworkRes = frameworkRes.getConfiguredResources(
- mConfigChooser.getConfiguration().getFullConfig());
- }
- }
-
- return mConfiguredFrameworkRes;
- }
-
- @Override
- @NonNull
- public Map<ResourceType, Map<String, ResourceValue>> getConfiguredProjectResources() {
- if (mConfiguredProjectRes == null && mConfigChooser != null) {
- ProjectResources project = getProjectResources();
-
- // get the project resource values based on the current config
- mConfiguredProjectRes = project.getConfiguredResources(
- mConfigChooser.getConfiguration().getFullConfig());
- }
-
- return mConfiguredProjectRes;
- }
-
- @Override
- public void createConfigFile() {
- LayoutCreatorDialog dialog = new LayoutCreatorDialog(mConfigChooser.getShell(),
- mEditedFile.getName(), mConfigChooser.getConfiguration().getFullConfig());
- if (dialog.open() != Window.OK) {
- return;
- }
-
- FolderConfiguration config = new FolderConfiguration();
- dialog.getConfiguration(config);
-
- // Creates a new layout file from the specified {@link FolderConfiguration}.
- CreateNewConfigJob job = new CreateNewConfigJob(this, mEditedFile, config);
- job.schedule();
- }
-
- /**
- * Returns the resource name of the file that is including this current layout, if any
- * (may be null)
- *
- * @return the resource name of an including layout, or null
- */
- @Override
- public Reference getIncludedWithin() {
- return mIncludedWithin;
- }
-
- @Override
- @Nullable
- public LayoutCanvas getCanvas() {
- return getCanvasControl();
- }
-
- /**
- * Listens to target changed in the current project, to trigger a new layout rendering.
- */
- private class TargetListener implements ITargetChangeListener {
-
- @Override
- public void onProjectTargetChange(IProject changedProject) {
- if (changedProject != null && changedProject.equals(getProject())) {
- updateEditor();
- }
- }
-
- @Override
- public void onTargetLoaded(IAndroidTarget loadedTarget) {
- IAndroidTarget target = getRenderingTarget();
- if (target != null && target.equals(loadedTarget)) {
- updateEditor();
- }
- }
-
- @Override
- public void onSdkLoaded() {
- // get the current rendering target to unload it
- IAndroidTarget oldTarget = getRenderingTarget();
- preRenderingTargetChangeCleanUp(oldTarget);
-
- computeSdkVersion();
-
- // get the project target
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
- if (target != null) {
- mConfigChooser.onSdkLoaded(target);
- changed(CFG_FOLDER | CFG_TARGET);
- }
- }
- }
-
- private void updateEditor() {
- mEditorDelegate.getEditor().commitPages(false /* onSave */);
-
- // because the target changed we must reset the configured resources.
- mConfiguredFrameworkRes = mConfiguredProjectRes = null;
- mResourceResolver = null;
-
- // make sure we remove the custom view loader, since its parent class loader is the
- // bridge class loader.
- mProjectCallback = null;
-
- // recreate the ui root node always, this will also call onTargetChange
- // on the config composite
- mEditorDelegate.delegateInitUiRootNode(true /*force*/);
- }
-
- private IProject getProject() {
- return getEditorDelegate().getEditor().getProject();
- }
- }
-
- /** Refresh the configured project resources associated with this editor */
- public void refreshProjectResources() {
- mConfiguredProjectRes = null;
- mResourceResolver = null;
- }
-
- /**
- * Returns the currently edited file
- *
- * @return the currently edited file, or null
- */
- public IFile getEditedFile() {
- return mEditedFile;
- }
-
- /**
- * Returns the project for the currently edited file, or null
- *
- * @return the project containing the edited file, or null
- */
- public IProject getProject() {
- if (mEditedFile != null) {
- return mEditedFile.getProject();
- } else {
- return null;
- }
- }
-
- // ----------------
-
- /**
- * Save operation in the Graphical Editor Part.
- * <p/>
- * In our workflow, the model is owned by the Structured XML Editor.
- * The graphical layout editor just displays it -- thus we don't really
- * save anything here.
- * <p/>
- * This must NOT call the parent editor part. At the contrary, the parent editor
- * part will call this *after* having done the actual save operation.
- * <p/>
- * The only action this editor must do is mark the undo command stack as
- * being no longer dirty.
- */
- @Override
- public void doSave(IProgressMonitor monitor) {
- // TODO implement a command stack
-// getCommandStack().markSaveLocation();
-// firePropertyChange(PROP_DIRTY);
- }
-
- /**
- * Save operation in the Graphical Editor Part.
- * <p/>
- * In our workflow, the model is owned by the Structured XML Editor.
- * The graphical layout editor just displays it -- thus we don't really
- * save anything here.
- */
- @Override
- public void doSaveAs() {
- // pass
- }
-
- /**
- * In our workflow, the model is owned by the Structured XML Editor.
- * The graphical layout editor just displays it -- thus we don't really
- * save anything here.
- */
- @Override
- public boolean isDirty() {
- return false;
- }
-
- /**
- * In our workflow, the model is owned by the Structured XML Editor.
- * The graphical layout editor just displays it -- thus we don't really
- * save anything here.
- */
- @Override
- public boolean isSaveAsAllowed() {
- return false;
- }
-
- @Override
- public void setFocus() {
- // TODO Auto-generated method stub
-
- }
-
- /**
- * Responds to a page change that made the Graphical editor page the activated page.
- */
- public void activated() {
- if (!mActive) {
- mActive = true;
-
- syncDockingState();
- mActionBar.updateErrorIndicator();
-
- boolean changed = mConfigChooser.syncRenderState();
- if (changed) {
- // Will also force recomputeLayout()
- return;
- }
-
- if (mNeedsRecompute) {
- recomputeLayout();
- }
-
- mCanvasViewer.getCanvas().syncPreviewMode();
- }
- }
-
- /**
- * The global docking state version. This number is incremented each time
- * the user customizes the window layout in any layout.
- */
- private static int sDockingStateVersion;
-
- /**
- * The window docking state version that this window is currently showing;
- * when a different window is reconfigured, the global version number is
- * incremented, and when this window is shown, and the current version is
- * less than the global version, the window layout will be synced.
- */
- private int mDockingStateVersion;
-
- /**
- * Syncs the window docking state.
- * <p>
- * The layout editor lets you change the docking state -- e.g. you can minimize the
- * palette, and drag the structure view to the bottom, and so on. When you restart
- * the IDE, the window comes back up with your customized state.
- * <p>
- * <b>However</b>, when you have multiple editor files open, if you minimize the palette
- * in one editor and then switch to another, the other editor will have the old window
- * state. That's because each editor has its own set of windows.
- * <p>
- * This method fixes this. Whenever a window is shown, this method is called, and the
- * docking state is synced such that the editor will match the current persistent docking
- * state.
- */
- private void syncDockingState() {
- if (mDockingStateVersion == sDockingStateVersion) {
- // No changes to apply
- return;
- }
- mDockingStateVersion = sDockingStateVersion;
-
- IPreferenceStore preferenceStore = AdtPlugin.getDefault().getPreferenceStore();
- PluginFlyoutPreferences preferences;
- preferences = new PluginFlyoutPreferences(preferenceStore, PREF_PALETTE);
- mPaletteComposite.apply(preferences);
- preferences = new PluginFlyoutPreferences(preferenceStore, PREF_STRUCTURE);
- mStructureFlyout.apply(preferences);
- mPaletteComposite.layout();
- mStructureFlyout.layout();
- mPaletteComposite.redraw(); // the structure view is nested within the palette
- }
-
- /**
- * Responds to a page change that made the Graphical editor page the deactivated page
- */
- public void deactivated() {
- mActive = false;
-
- LayoutCanvas canvas = getCanvasControl();
- if (canvas != null) {
- canvas.deactivated();
- }
- }
-
- /**
- * Opens and initialize the editor with a new file.
- * @param file the file being edited.
- */
- public void openFile(IFile file) {
- mEditedFile = file;
- mConfigChooser.setFile(mEditedFile);
-
- if (mReloadListener == null) {
- mReloadListener = new ReloadListener();
- LayoutReloadMonitor.getMonitor().addListener(mEditedFile.getProject(), mReloadListener);
- }
-
- if (mRulesEngine == null) {
- mRulesEngine = new RulesEngine(this, mEditedFile.getProject());
- if (mCanvasViewer != null) {
- mCanvasViewer.getCanvas().setRulesEngine(mRulesEngine);
- }
- }
-
- // Pick up hand-off data: somebody requesting this file to be opened may have
- // requested that it should be opened as included within another file
- if (mEditedFile != null) {
- try {
- mIncludedWithin = (Reference) mEditedFile.getSessionProperty(NAME_INCLUDE);
- if (mIncludedWithin != null) {
- // Only use once
- mEditedFile.setSessionProperty(NAME_INCLUDE, null);
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't access session property %1$s", NAME_INCLUDE);
- }
- }
-
- computeSdkVersion();
- }
-
- /**
- * Resets the editor with a replacement file.
- * @param file the replacement file.
- */
- public void replaceFile(IFile file) {
- mEditedFile = file;
- mConfigChooser.replaceFile(mEditedFile);
- computeSdkVersion();
- }
-
- /**
- * Resets the editor with a replacement file coming from a config change in the config
- * selector.
- * @param file the replacement file.
- */
- public void changeFileOnNewConfig(IFile file) {
- mEditedFile = file;
- mConfigChooser.changeFileOnNewConfig(mEditedFile);
- }
-
- /**
- * Responds to a target change for the project of the edited file
- */
- public void onTargetChange() {
- AndroidTargetData targetData = mConfigChooser.onXmlModelLoaded();
- updateCapabilities(targetData);
-
- changed(CFG_FOLDER | CFG_TARGET);
- }
-
- /** Updates the capabilities for the given target data (which may be null) */
- private void updateCapabilities(AndroidTargetData targetData) {
- if (targetData != null) {
- LayoutLibrary layoutLib = targetData.getLayoutLibrary();
- if (mIncludedWithin != null && !layoutLib.supports(Capability.EMBEDDED_LAYOUT)) {
- showIn(null);
- }
- }
- }
-
- /**
- * Returns the {@link CommonXmlDelegate} for this editor
- *
- * @return the {@link CommonXmlDelegate} for this editor
- */
- @NonNull
- public LayoutEditorDelegate getEditorDelegate() {
- return mEditorDelegate;
- }
-
- /**
- * Returns the {@link RulesEngine} associated with this editor
- *
- * @return the {@link RulesEngine} associated with this editor, never null
- */
- public RulesEngine getRulesEngine() {
- return mRulesEngine;
- }
-
- /**
- * Return the {@link LayoutCanvas} associated with this editor
- *
- * @return the associated {@link LayoutCanvas}
- */
- public LayoutCanvas getCanvasControl() {
- if (mCanvasViewer != null) {
- return mCanvasViewer.getCanvas();
- }
- return null;
- }
-
- /**
- * Returns the {@link UiDocumentNode} for the XML model edited by this editor
- *
- * @return the associated model
- */
- public UiDocumentNode getModel() {
- return mEditorDelegate.getUiRootNode();
- }
-
- /**
- * Callback for XML model changed. Only update/recompute the layout if the editor is visible
- */
- public void onXmlModelChanged() {
- // To optimize the rendering when the user is editing in the XML pane, we don't
- // refresh the editor if it's not the active part.
- //
- // This behavior is acceptable when the editor is the single "full screen" part
- // (as in this case active means visible.)
- // Unfortunately this breaks in 2 cases:
- // - when performing a drag'n'drop from one editor to another, the target is not
- // properly refreshed before it becomes active.
- // - when duplicating the editor window and placing both editors side by side (xml in one
- // and canvas in the other one), the canvas may not be refreshed when the XML is edited.
- //
- // TODO find a way to really query whether the pane is visible, not just active.
-
- if (mEditorDelegate.isGraphicalEditorActive()) {
- recomputeLayout();
- } else {
- // Remember we want to recompute as soon as the editor becomes active.
- mNeedsRecompute = true;
- }
- }
-
- /**
- * Recomputes the layout
- */
- public void recomputeLayout() {
- try {
- if (!ensureFileValid()) {
- return;
- }
-
- UiDocumentNode model = getModel();
- LayoutCanvas canvas = mCanvasViewer.getCanvas();
- if (!ensureModelValid(model)) {
- // Although we display an error, we still treat an empty document as a
- // successful layout result so that we can drop new elements in it.
- //
- // For that purpose, create a special LayoutScene that has no image,
- // no root view yet indicates success and then update the canvas with it.
-
- canvas.setSession(
- new StaticRenderSession(
- Result.Status.SUCCESS.createResult(),
- null /*rootViewInfo*/, null /*image*/),
- null /*explodeNodes*/, true /* layoutlib5 */);
- return;
- }
-
- LayoutLibrary layoutLib = getReadyLayoutLib(true /*displayError*/);
-
- if (layoutLib != null) {
- // if drawing in real size, (re)set the scaling factor.
- if (mActionBar.isZoomingRealSize()) {
- mActionBar.computeAndSetRealScale(false /* redraw */);
- }
-
- IProject project = mEditedFile.getProject();
- renderWithBridge(project, model, layoutLib);
-
- canvas.getPreviewManager().renderPreviews();
- }
- } finally {
- // no matter the result, we are done doing the recompute based on the latest
- // resource/code change.
- mNeedsRecompute = false;
- }
- }
-
- /**
- * Reloads the palette
- */
- public void reloadPalette() {
- if (mPalette != null) {
- IAndroidTarget renderingTarget = getRenderingTarget();
- if (renderingTarget != null) {
- mPalette.reloadPalette(renderingTarget);
- }
- }
- }
-
- /**
- * Returns the {@link LayoutLibrary} associated with this editor, if it has
- * been initialized already. May return null if it has not been initialized (or has
- * not finished initializing).
- *
- * @return The {@link LayoutLibrary}, or null
- */
- public LayoutLibrary getLayoutLibrary() {
- return getReadyLayoutLib(false /*displayError*/);
- }
-
- /**
- * Returns the scale to multiply pixels in the layout coordinate space with to obtain
- * the corresponding dip (device independent pixel)
- *
- * @return the scale to multiple layout coordinates with to obtain the dip position
- */
- public float getDipScale() {
- float dpi = mConfigChooser.getConfiguration().getDensity().getDpiValue();
- return Density.DEFAULT_DENSITY / dpi;
- }
-
- // --- private methods ---
-
- /**
- * Ensure that the file associated with this editor is valid (exists and is
- * synchronized). Any reasons why it is not are displayed in the editor's error area.
- *
- * @return True if the editor is valid, false otherwise.
- */
- private boolean ensureFileValid() {
- // check that the resource exists. If the file is opened but the project is closed
- // or deleted for some reason (changed from outside of eclipse), then this will
- // return false;
- if (mEditedFile.exists() == false) {
- displayError("Resource '%1$s' does not exist.",
- mEditedFile.getFullPath().toString());
- return false;
- }
-
- if (mEditedFile.isSynchronized(IResource.DEPTH_ZERO) == false) {
- String message = String.format("%1$s is out of sync. Please refresh.",
- mEditedFile.getName());
-
- displayError(message);
-
- // also print it in the error console.
- IProject iProject = mEditedFile.getProject();
- AdtPlugin.printErrorToConsole(iProject.getName(), message);
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns a {@link LayoutLibrary} that is ready for rendering, or null if the bridge
- * is not available or not ready yet (due to SDK loading still being in progress etc).
- * If enabled, any reasons preventing the bridge from being returned are displayed to the
- * editor's error area.
- *
- * @param displayError whether to display the loading error or not.
- *
- * @return LayoutBridge the layout bridge for rendering this editor's scene
- */
- LayoutLibrary getReadyLayoutLib(boolean displayError) {
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = getRenderingTarget();
-
- if (target != null) {
- AndroidTargetData data = currentSdk.getTargetData(target);
- if (data != null) {
- LayoutLibrary layoutLib = data.getLayoutLibrary();
-
- if (layoutLib.getStatus() == LoadStatus.LOADED) {
- return layoutLib;
- } else if (displayError) { // getBridge() == null
- // SDK is loaded but not the layout library!
-
- // check whether the bridge managed to load, or not
- if (layoutLib.getStatus() == LoadStatus.LOADING) {
- displayError("Eclipse is loading framework information and the layout library from the SDK folder.\n%1$s will refresh automatically once the process is finished.",
- mEditedFile.getName());
- } else {
- String message = layoutLib.getLoadMessage();
- displayError("Eclipse failed to load the framework information and the layout library!" +
- message != null ? "\n" + message : "");
- }
- }
- } else { // data == null
- // It can happen that the workspace refreshes while the SDK is loading its
- // data, which could trigger a redraw of the opened layout if some resources
- // changed while Eclipse is closed.
- // In this case data could be null, but this is not an error.
- // We can just silently return, as all the opened editors are automatically
- // refreshed once the SDK finishes loading.
- LoadStatus targetLoadStatus = currentSdk.checkAndLoadTargetData(target, null);
-
- // display error is asked.
- if (displayError) {
- String targetName = target.getName();
- switch (targetLoadStatus) {
- case LOADING:
- String s;
- if (currentSdk.getTarget(getProject()) == target) {
- s = String.format(
- "The project target (%1$s) is still loading.",
- targetName);
- } else {
- s = String.format(
- "The rendering target (%1$s) is still loading.",
- targetName);
- }
- s += "\nThe layout will refresh automatically once the process is finished.";
- displayError(s);
-
- break;
- case FAILED: // known failure
- case LOADED: // success but data isn't loaded?!?!
- displayError("The project target (%s) was not properly loaded.",
- targetName);
- break;
- }
- }
- }
-
- } else if (displayError) { // target == null
- displayError("The project target is not set. Right click project, choose Properties | Android.");
- }
- } else if (displayError) { // currentSdk == null
- displayError("Eclipse is loading the SDK.\n%1$s will refresh automatically once the process is finished.",
- mEditedFile.getName());
- }
-
- return null;
- }
-
- /**
- * Returns the {@link IAndroidTarget} used for the rendering.
- * <p/>
- * This first looks for the rendering target setup in the config UI, and if nothing has
- * been setup yet, returns the target of the project.
- *
- * @return an IAndroidTarget object or null if no target is setup and the project has no
- * target set.
- *
- */
- public IAndroidTarget getRenderingTarget() {
- // if the SDK is null no targets are loaded.
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk == null) {
- return null;
- }
-
- // attempt to get a target from the configuration selector.
- IAndroidTarget renderingTarget = mConfigChooser.getConfiguration().getTarget();
- if (renderingTarget != null) {
- return renderingTarget;
- }
-
- // fall back to the project target
- if (mEditedFile != null) {
- return currentSdk.getTarget(mEditedFile.getProject());
- }
-
- return null;
- }
-
- /**
- * Returns whether the current rendering target supports the given capability
- *
- * @param capability the capability to be looked up
- * @return true if the current rendering target supports the given capability
- */
- public boolean renderingSupports(Capability capability) {
- IAndroidTarget target = getRenderingTarget();
- if (target != null) {
- AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
- LayoutLibrary layoutLib = targetData.getLayoutLibrary();
- return layoutLib.supports(capability);
- }
-
- return false;
- }
-
- private boolean ensureModelValid(UiDocumentNode model) {
- // check there is actually a model (maybe the file is empty).
- if (model.getUiChildren().size() == 0) {
- if (mEditorDelegate.getEditor().isCreatingPages()) {
- displayError("Loading editor");
- return false;
- }
- displayError(
- "No XML content. Please add a root view or layout to your document.");
- return false;
- }
-
- return true;
- }
-
- /**
- * Creates a {@link RenderService} associated with this editor
- * @return the render service
- */
- @NonNull
- public RenderService createRenderService() {
- return RenderService.create(this, mCredential);
- }
-
- /**
- * Creates a {@link RenderLogger} associated with this editor
- * @param name the name of the logger
- * @return the new logger
- */
- @NonNull
- public RenderLogger createRenderLogger(String name) {
- return new RenderLogger(name, mCredential);
- }
-
- /**
- * Creates a {@link RenderService} associated with this editor
- *
- * @param configuration the configuration to use (and fallback to editor for the rest)
- * @param resolver a resource resolver to use to look up resources
- * @return the render service
- */
- @NonNull
- public RenderService createRenderService(Configuration configuration,
- ResourceResolver resolver) {
- return RenderService.create(this, configuration, resolver, mCredential);
- }
-
- private void renderWithBridge(IProject iProject, UiDocumentNode model,
- LayoutLibrary layoutLib) {
- LayoutCanvas canvas = getCanvasControl();
- Set<UiElementNode> explodeNodes = canvas.getNodesToExplode();
- RenderLogger logger = createRenderLogger(mEditedFile.getName());
- RenderingMode renderingMode = RenderingMode.NORMAL;
- // FIXME set the rendering mode using ViewRule or something.
- List<UiElementNode> children = model.getUiChildren();
- if (children.size() > 0 &&
- children.get(0).getDescriptor().getXmlLocalName().equals(SCROLL_VIEW)) {
- renderingMode = RenderingMode.V_SCROLL;
- }
-
- RenderSession session = RenderService.create(this, mCredential)
- .setModel(model)
- .setLog(logger)
- .setRenderingMode(renderingMode)
- .setIncludedWithin(mIncludedWithin)
- .setNodesToExpand(explodeNodes)
- .createRenderSession();
-
- boolean layoutlib5 = layoutLib.supports(Capability.EMBEDDED_LAYOUT);
- canvas.setSession(session, explodeNodes, layoutlib5);
-
- // update the UiElementNode with the layout info.
- if (session != null && session.getResult().isSuccess() == false) {
- // An error was generated. Print it (and any other accumulated warnings)
- String errorMessage = session.getResult().getErrorMessage();
- Throwable exception = session.getResult().getException();
- if (exception != null && errorMessage == null) {
- errorMessage = exception.toString();
- }
- if (exception != null || (errorMessage != null && errorMessage.length() > 0)) {
- logger.error(null, errorMessage, exception, null /*data*/);
- } else if (!logger.hasProblems()) {
- logger.error(null, "Unexpected error in rendering, no details given",
- null /*data*/);
- }
- // These errors will be included in the log warnings which are
- // displayed regardless of render success status below
- }
-
- // We might have detected some missing classes and swapped them by a mock view,
- // or run into fidelity warnings or missing resources, so emit all these
- // warnings
- Set<String> missingClasses = mProjectCallback.getMissingClasses();
- Set<String> brokenClasses = mProjectCallback.getUninstantiatableClasses();
- if (logger.hasProblems()) {
- displayLoggerProblems(iProject, logger);
- displayFailingClasses(missingClasses, brokenClasses, true);
- displayUserStackTrace(logger, true);
- } else if (missingClasses.size() > 0 || brokenClasses.size() > 0) {
- displayFailingClasses(missingClasses, brokenClasses, false);
- displayUserStackTrace(logger, true);
- } else if (session != null) {
- // Nope, no missing or broken classes. Clear success, congrats!
- hideError();
-
- // First time this layout is opened, run lint on the file (after a delay)
- if (!mRenderedOnce) {
- mRenderedOnce = true;
- Job job = new Job("Run Lint") {
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- getEditorDelegate().delegateRunLint();
- return Status.OK_STATUS;
- }
-
- };
- job.setSystem(true);
- job.schedule(3000); // 3 seconds
- }
-
- mConfigChooser.ensureInitialized();
- }
-
- model.refreshUi();
- }
-
- /**
- * Returns the {@link ResourceResolver} for this editor
- *
- * @return the resolver used to resolve resources for the current configuration of
- * this editor, or null
- */
- public ResourceResolver getResourceResolver() {
- if (mResourceResolver == null) {
- String theme = mConfigChooser.getThemeName();
- if (theme == null) {
- displayError("Missing theme.");
- return null;
- }
- boolean isProjectTheme = mConfigChooser.getConfiguration().isProjectTheme();
-
- Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes =
- getConfiguredProjectResources();
-
- // Get the framework resources
- Map<ResourceType, Map<String, ResourceValue>> frameworkResources =
- getConfiguredFrameworkResources();
-
- if (configuredProjectRes == null) {
- displayError("Missing project resources for current configuration.");
- return null;
- }
-
- if (frameworkResources == null) {
- displayError("Missing framework resources.");
- return null;
- }
-
- mResourceResolver = ResourceResolver.create(
- configuredProjectRes, frameworkResources,
- theme, isProjectTheme);
- }
-
- return mResourceResolver;
- }
-
- /** Returns a project callback, and optionally resets it */
- ProjectCallback getProjectCallback(boolean reset, LayoutLibrary layoutLibrary) {
- // Lazily create the project callback the first time we need it
- if (mProjectCallback == null) {
- ResourceManager resManager = ResourceManager.getInstance();
- IProject project = getProject();
- ProjectResources projectRes = resManager.getProjectResources(project);
- mProjectCallback = new ProjectCallback(layoutLibrary, projectRes, project,
- mCredential, this);
- } else if (reset) {
- // Also clears the set of missing/broken classes prior to rendering
- mProjectCallback.getMissingClasses().clear();
- mProjectCallback.getUninstantiatableClasses().clear();
- }
-
- return mProjectCallback;
- }
-
- /**
- * Returns the resource name of this layout, NOT including the @layout/ prefix
- *
- * @return the resource name of this layout, NOT including the @layout/ prefix
- */
- public String getLayoutResourceName() {
- return ResourceHelper.getLayoutName(mEditedFile);
- }
-
- /**
- * Cleans up when the rendering target is about to change
- * @param oldTarget the old rendering target.
- */
- private void preRenderingTargetChangeCleanUp(IAndroidTarget oldTarget) {
- // first clear the caches related to this file in the old target
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- AndroidTargetData data = currentSdk.getTargetData(oldTarget);
- if (data != null) {
- LayoutLibrary layoutLib = data.getLayoutLibrary();
-
- // layoutLib can never be null.
- layoutLib.clearCaches(mEditedFile.getProject());
- }
- }
-
- // Also remove the ProjectCallback as it caches custom views which must be reloaded
- // with the classloader of the new LayoutLib. We also have to clear it out
- // because it stores a reference to the layout library which could have changed.
- mProjectCallback = null;
-
- // FIXME: get rid of the current LayoutScene if any.
- }
-
- private class ReloadListener implements ILayoutReloadListener {
- /**
- * Called when the file changes triggered a redraw of the layout
- */
- @Override
- public void reloadLayout(final ChangeFlags flags, final boolean libraryChanged) {
- if (mConfigChooser.isDisposed()) {
- return;
- }
- Display display = mConfigChooser.getDisplay();
- display.asyncExec(new Runnable() {
- @Override
- public void run() {
- reloadLayoutSwt(flags, libraryChanged);
- }
- });
- }
-
- /** Reload layout. <b>Must be called on the SWT thread</b> */
- private void reloadLayoutSwt(ChangeFlags flags, boolean libraryChanged) {
- if (mConfigChooser.isDisposed()) {
- return;
- }
- assert mConfigChooser.getDisplay().getThread() == Thread.currentThread();
-
- boolean recompute = false;
- // we only care about the r class of the main project.
- if (flags.rClass && libraryChanged == false) {
- recompute = true;
- if (mEditedFile != null) {
- ResourceManager manager = ResourceManager.getInstance();
- ProjectResources projectRes = manager.getProjectResources(
- mEditedFile.getProject());
-
- if (projectRes != null) {
- projectRes.resetDynamicIds();
- }
- }
- }
-
- if (flags.localeList) {
- // the locale list *potentially* changed so we update the locale in the
- // config composite.
- // However there's no recompute, as it could not be needed
- // (for instance a new layout)
- // If a resource that's not a layout changed this will trigger a recompute anyway.
- mConfigChooser.updateLocales();
- }
-
- // if a resources was modified.
- if (flags.resources) {
- recompute = true;
-
- // TODO: differentiate between single and multi resource file changed, and whether
- // the resource change affects the cache.
-
- // force a reparse in case a value XML file changed.
- mConfiguredProjectRes = null;
- mResourceResolver = null;
-
- // clear the cache in the bridge in case a bitmap/9-patch changed.
- LayoutLibrary layoutLib = getReadyLayoutLib(true /*displayError*/);
- if (layoutLib != null) {
- layoutLib.clearCaches(mEditedFile.getProject());
- }
- }
-
- if (flags.code) {
- // only recompute if the custom view loader was used to load some code.
- if (mProjectCallback != null && mProjectCallback.isUsed()) {
- mProjectCallback = null;
- recompute = true;
- }
- }
-
- if (flags.manifest) {
- recompute |= computeSdkVersion();
- }
-
- if (recompute) {
- if (mEditorDelegate.isGraphicalEditorActive()) {
- recomputeLayout();
- } else {
- mNeedsRecompute = true;
- }
- }
- }
- }
-
- // ---- Error handling ----
-
- /**
- * Switches the sash to display the error label.
- *
- * @param errorFormat The new error to display if not null.
- * @param parameters String.format parameters for the error format.
- */
- private void displayError(String errorFormat, Object...parameters) {
- if (errorFormat != null) {
- mErrorLabel.setText(String.format(errorFormat, parameters));
- } else {
- mErrorLabel.setText("");
- }
- mSashError.setMaximizedControl(null);
- }
-
- /** Displays the canvas and hides the error label. */
- private void hideError() {
- mErrorLabel.setText("");
- mSashError.setMaximizedControl(mCanvasViewer.getControl());
- }
-
- /** Display the problem list encountered during a render */
- private void displayUserStackTrace(RenderLogger logger, boolean append) {
- List<Throwable> throwables = logger.getFirstTrace();
- if (throwables == null || throwables.isEmpty()) {
- return;
- }
-
- Throwable throwable = throwables.get(0);
-
- if (throwable instanceof RenderSecurityException) {
- addActionLink(mErrorLabel, ActionLinkStyleRange.LINK_DISABLE_SANDBOX,
- "\nTurn off custom view rendering sandbox\n");
-
- StringBuilder builder = new StringBuilder(200);
- String lastFailedPath = RenderSecurityManager.getLastFailedPath();
- if (lastFailedPath != null) {
- builder.append("Diagnostic info for ADT bug report:\n");
- builder.append("Failed path: ").append(lastFailedPath).append('\n');
- String tempDir = System.getProperty("java.io.tmpdir");
- builder.append("Normal temp dir: ").append(tempDir).append('\n');
- File normalized = new File(tempDir);
- builder.append("Normalized temp dir: ").append(normalized.getPath()).append('\n');
- try {
- builder.append("Canonical temp dir: ").append(normalized.getCanonicalPath())
- .append('\n');
- } catch (IOException e) {
- // ignore
- }
- builder.append("os.name: ").append(System.getProperty("os.name")).append('\n');
- builder.append("os.version: ").append(System.getProperty("os.version"));
- builder.append('\n');
- builder.append("java.runtime.version: ");
- builder.append(System.getProperty("java.runtime.version"));
- }
- if (throwable.getMessage().equals("Unable to create temporary file")) {
- String javaVersion = System.getProperty("java.version");
- if (javaVersion.startsWith("1.7.0_")) {
- int version = Integer
- .parseInt(javaVersion.substring(javaVersion.indexOf('_') + 1));
- if (version > 0 && version < 45) {
- builder.append('\n');
- builder.append("Tip: This may be caused by using an older version " +
- "of JDK 1.7.0; try using at least 1.7.0_45 (you are using " +
- javaVersion + ")");
- }
- }
- }
- if (builder.length() > 0) {
- addText(mErrorLabel, builder.toString());
- }
- }
-
- StackTraceElement[] frames = throwable.getStackTrace();
- int end = -1;
- boolean haveInterestingFrame = false;
- for (int i = 0; i < frames.length; i++) {
- StackTraceElement frame = frames[i];
- if (isInterestingFrame(frame)) {
- haveInterestingFrame = true;
- }
- String className = frame.getClassName();
- if (className.equals(
- "com.android.layoutlib.bridge.impl.RenderSessionImpl")) { //$NON-NLS-1$
- end = i;
- break;
- }
- }
-
- if (end == -1 || !haveInterestingFrame) {
- // Not a recognized stack trace range: just skip it
- return;
- }
-
- if (!append) {
- mErrorLabel.setText("\n"); //$NON-NLS-1$
- } else {
- addText(mErrorLabel, "\n\n"); //$NON-NLS-1$
- }
-
- addText(mErrorLabel, throwable.toString() + '\n');
- for (int i = 0; i < end; i++) {
- StackTraceElement frame = frames[i];
- String className = frame.getClassName();
- String methodName = frame.getMethodName();
- addText(mErrorLabel, " at " + className + '.' + methodName + '(');
- String fileName = frame.getFileName();
- if (fileName != null && !fileName.isEmpty()) {
- int lineNumber = frame.getLineNumber();
- String location = fileName + ':' + lineNumber;
- if (isInterestingFrame(frame)) {
- addActionLink(mErrorLabel, ActionLinkStyleRange.LINK_OPEN_LINE,
- location, className, methodName, fileName, lineNumber);
- } else {
- addText(mErrorLabel, location);
- }
- addText(mErrorLabel, ")\n"); //$NON-NLS-1$
- }
- }
- }
-
- private static boolean isInterestingFrame(StackTraceElement frame) {
- String className = frame.getClassName();
- return !(className.startsWith("android.") //$NON-NLS-1$
- || className.startsWith("com.android.") //$NON-NLS-1$
- || className.startsWith("java.") //$NON-NLS-1$
- || className.startsWith("javax.") //$NON-NLS-1$
- || className.startsWith("sun.")); //$NON-NLS-1$
- }
-
- /**
- * Switches the sash to display the error label to show a list of
- * missing classes and give options to create them.
- */
- private void displayFailingClasses(Set<String> missingClasses, Set<String> brokenClasses,
- boolean append) {
- if (missingClasses.size() == 0 && brokenClasses.size() == 0) {
- return;
- }
-
- if (!append) {
- mErrorLabel.setText(""); //$NON-NLS-1$
- } else {
- addText(mErrorLabel, "\n"); //$NON-NLS-1$
- }
-
- if (missingClasses.size() > 0) {
- addText(mErrorLabel, "The following classes could not be found:\n");
- for (String clazz : missingClasses) {
- addText(mErrorLabel, "- ");
- addText(mErrorLabel, clazz);
- addText(mErrorLabel, " (");
-
- IProject project = getProject();
- Collection<String> customViews = getCustomViewClassNames(project);
- addTypoSuggestions(clazz, customViews, false);
- addTypoSuggestions(clazz, customViews, true);
- addTypoSuggestions(clazz, getAndroidViewClassNames(project), false);
-
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_FIX_BUILD_PATH, "Fix Build Path", clazz);
- addText(mErrorLabel, ", ");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_EDIT_XML, "Edit XML", clazz);
- if (clazz.indexOf('.') != -1) {
- // Add "Create Class" link, but only for custom views
- addText(mErrorLabel, ", ");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_CREATE_CLASS, "Create Class", clazz);
- }
- addText(mErrorLabel, ")\n");
- }
- }
- if (brokenClasses.size() > 0) {
- addText(mErrorLabel, "The following classes could not be instantiated:\n");
-
- // Do we have a custom class (not an Android or add-ons class)
- boolean haveCustomClass = false;
-
- for (String clazz : brokenClasses) {
- addText(mErrorLabel, "- ");
- addText(mErrorLabel, clazz);
- addText(mErrorLabel, " (");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_OPEN_CLASS, "Open Class", clazz);
- addText(mErrorLabel, ", ");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_SHOW_LOG, "Show Error Log", clazz);
- addText(mErrorLabel, ")\n");
-
- if (!(clazz.startsWith("android.") || //$NON-NLS-1$
- clazz.startsWith("com.google."))) { //$NON-NLS-1$
- haveCustomClass = true;
- }
- }
-
- addText(mErrorLabel, "See the Error Log (Window > Show View) for more details.\n");
-
- if (haveCustomClass) {
- addBoldText(mErrorLabel, "Tip: Use View.isInEditMode() in your custom views "
- + "to skip code when shown in Eclipse");
- }
- }
-
- mSashError.setMaximizedControl(null);
- }
-
- private void addTypoSuggestions(String actual, Collection<String> views,
- boolean compareWithPackage) {
- if (views.size() == 0) {
- return;
- }
-
- // Look for typos and try to match with custom views and android views
- String actualBase = actual.substring(actual.lastIndexOf('.') + 1);
- int maxDistance = actualBase.length() >= 4 ? 2 : 1;
-
- if (views.size() > 0) {
- for (String suggested : views) {
- String suggestedBase = suggested.substring(suggested.lastIndexOf('.') + 1);
-
- String matchWith = compareWithPackage ? suggested : suggestedBase;
- if (Math.abs(actualBase.length() - matchWith.length()) > maxDistance) {
- // The string lengths differ more than the allowed edit distance;
- // no point in even attempting to compute the edit distance (requires
- // O(n*m) storage and O(n*m) speed, where n and m are the string lengths)
- continue;
- }
- if (LintUtils.editDistance(actualBase, matchWith) <= maxDistance) {
- // Suggest this class as a typo for the given class
- String labelClass = (suggestedBase.equals(actual) || actual.indexOf('.') != -1)
- ? suggested : suggestedBase;
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.LINK_CHANGE_CLASS_TO,
- String.format("Change to %1$s",
- // Only show full package name if class name
- // is the same
- labelClass),
- actual,
- viewNeedsPackage(suggested) ? suggested : suggestedBase);
- addText(mErrorLabel, ", ");
- }
- }
- }
- }
-
- private static Collection<String> getCustomViewClassNames(IProject project) {
- CustomViewFinder finder = CustomViewFinder.get(project);
- Collection<String> views = finder.getAllViews();
- if (views == null) {
- finder.refresh();
- views = finder.getAllViews();
- }
-
- return views;
- }
-
- private static Collection<String> getAndroidViewClassNames(IProject project) {
- Sdk currentSdk = Sdk.getCurrent();
- IAndroidTarget target = currentSdk.getTarget(project);
- if (target != null) {
- AndroidTargetData targetData = currentSdk.getTargetData(target);
- if (targetData != null) {
- LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors();
- return layoutDescriptors.getAllViewClassNames();
- }
- }
-
- return Collections.emptyList();
- }
-
- /** Add a normal line of text to the styled text widget. */
- private void addText(StyledText styledText, String...string) {
- for (String s : string) {
- styledText.append(s);
- }
- }
-
- /** Display the problem list encountered during a render */
- private void displayLoggerProblems(IProject project, RenderLogger logger) {
- if (logger.hasProblems()) {
- mErrorLabel.setText("");
- // A common source of problems is attempting to open a layout when there are
- // compilation errors. In this case, may not have run (or may not be up to date)
- // so resources cannot be looked up etc. Explain this situation to the user.
-
- boolean hasAaptErrors = false;
- boolean hasJavaErrors = false;
- try {
- IMarker[] markers;
- markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
- if (markers.length > 0) {
- for (IMarker marker : markers) {
- String markerType = marker.getType();
- if (markerType.equals(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER)) {
- int severity = marker.getAttribute(IMarker.SEVERITY, -1);
- if (severity == IMarker.SEVERITY_ERROR) {
- hasJavaErrors = true;
- }
- } else if (markerType.equals(AdtConstants.MARKER_AAPT_COMPILE)) {
- int severity = marker.getAttribute(IMarker.SEVERITY, -1);
- if (severity == IMarker.SEVERITY_ERROR) {
- hasAaptErrors = true;
- }
- }
- }
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
-
- if (logger.seenTagPrefix(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR)) {
- addBoldText(mErrorLabel,
- "Missing styles. Is the correct theme chosen for this layout?\n");
- addText(mErrorLabel,
- "Use the Theme combo box above the layout to choose a different layout, " +
- "or fix the theme style references.\n\n");
- }
-
- List<Throwable> trace = logger.getFirstTrace();
- if (trace != null
- && trace.toString().contains(
- "java.lang.IndexOutOfBoundsException: Index: 2, Size: 2") //$NON-NLS-1$
- && mConfigChooser.getConfiguration().getDensity() == Density.TV) {
- addBoldText(mErrorLabel,
- "It looks like you are using a render target where the layout library " +
- "does not support the tvdpi density.\n\n");
- addText(mErrorLabel, "Please try either updating to " +
- "the latest available version (using the SDK manager), or if no updated " +
- "version is available for this specific version of Android, try using " +
- "a more recent render target version.\n\n");
-
- }
-
- if (hasAaptErrors && logger.seenTagPrefix(LayoutLog.TAG_RESOURCES_PREFIX)) {
- // Text will automatically be wrapped by the error widget so no reason
- // to insert linebreaks in this error message:
- String message =
- "NOTE: This project contains resource errors, so aapt did not succeed, "
- + "which can cause rendering failures. "
- + "Fix resource problems first.\n\n";
- addBoldText(mErrorLabel, message);
- } else if (hasJavaErrors && mProjectCallback != null && mProjectCallback.isUsed()) {
- // Text will automatically be wrapped by the error widget so no reason
- // to insert linebreaks in this error message:
- String message =
- "NOTE: This project contains Java compilation errors, "
- + "which can cause rendering failures for custom views. "
- + "Fix compilation problems first.\n\n";
- addBoldText(mErrorLabel, message);
- }
-
- if (logger.seenTag(RenderLogger.TAG_MISSING_DIMENSION)) {
- List<UiElementNode> elements = UiDocumentNode.getAllElements(getModel());
- for (UiElementNode element : elements) {
- String width = element.getAttributeValue(ATTR_LAYOUT_WIDTH);
- if (width == null || width.length() == 0) {
- addSetAttributeLink(element, ATTR_LAYOUT_WIDTH);
- }
-
- String height = element.getAttributeValue(ATTR_LAYOUT_HEIGHT);
- if (height == null || height.length() == 0) {
- addSetAttributeLink(element, ATTR_LAYOUT_HEIGHT);
- }
- }
- }
-
- String problems = logger.getProblems(false /*includeFidelityWarnings*/);
- addText(mErrorLabel, problems);
-
- List<String> fidelityWarnings = logger.getFidelityWarnings();
- if (fidelityWarnings != null && fidelityWarnings.size() > 0) {
- addText(mErrorLabel,
- "The graphics preview in the layout editor may not be accurate:\n");
- for (String warning : fidelityWarnings) {
- addText(mErrorLabel, warning + ' ');
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.IGNORE_FIDELITY_WARNING,
- "(Ignore for this session)\n", warning);
- }
- }
-
- mSashError.setMaximizedControl(null);
- } else {
- mSashError.setMaximizedControl(mCanvasViewer.getControl());
- }
- }
-
- /** Appends an action link to set the given attribute on the given value */
- private void addSetAttributeLink(UiElementNode element, String attribute) {
- if (element.getXmlNode().getNodeName().equals(GRID_LAYOUT)) {
- // GridLayout does not require a layout_width or layout_height to be defined
- return;
- }
-
- String fill = VALUE_FILL_PARENT;
- // See whether we should offer match_parent instead of fill_parent
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(getProject());
- if (target.getVersion().getApiLevel() >= 8) {
- fill = VALUE_MATCH_PARENT;
- }
- }
-
- String id = element.getAttributeValue(ATTR_ID);
- if (id == null || id.length() == 0) {
- id = '<' + element.getXmlNode().getNodeName() + '>';
- } else {
- id = BaseLayoutRule.stripIdPrefix(id);
- }
-
- addText(mErrorLabel, String.format("\"%1$s\" does not set the required %2$s attribute:\n",
- id, attribute));
- addText(mErrorLabel, " (1) ");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.SET_ATTRIBUTE,
- String.format("Set to \"%1$s\"", VALUE_WRAP_CONTENT),
- element, attribute, VALUE_WRAP_CONTENT);
- addText(mErrorLabel, "\n (2) ");
- addActionLink(mErrorLabel,
- ActionLinkStyleRange.SET_ATTRIBUTE,
- String.format("Set to \"%1$s\"\n", fill),
- element, attribute, fill);
- }
-
- /** Appends the given text as a bold string in the given text widget */
- private void addBoldText(StyledText styledText, String text) {
- String s = styledText.getText();
- int start = (s == null ? 0 : s.length());
-
- styledText.append(text);
- StyleRange sr = new StyleRange();
- sr.start = start;
- sr.length = text.length();
- sr.fontStyle = SWT.BOLD;
- styledText.setStyleRange(sr);
- }
-
- /**
- * Add a URL-looking link to the styled text widget.
- * <p/>
- * A mouse-click listener is setup and it interprets the link based on the
- * action, corresponding to the value fields in {@link ActionLinkStyleRange}.
- */
- private void addActionLink(StyledText styledText, int action, String label,
- Object... data) {
- String s = styledText.getText();
- int start = (s == null ? 0 : s.length());
- styledText.append(label);
-
- StyleRange sr = new ActionLinkStyleRange(action, data);
- sr.start = start;
- sr.length = label.length();
- sr.fontStyle = SWT.NORMAL;
- sr.underlineStyle = SWT.UNDERLINE_LINK;
- sr.underline = true;
- styledText.setStyleRange(sr);
- }
-
- /**
- * Looks up the resource file corresponding to the given type
- *
- * @param type The type of resource to look up, such as {@link ResourceType#LAYOUT}
- * @param name The name of the resource (not including ".xml")
- * @param isFrameworkResource if true, the resource is a framework resource, otherwise
- * it's a project resource
- * @return the resource file defining the named resource, or null if not found
- */
- public IPath findResourceFile(ResourceType type, String name, boolean isFrameworkResource) {
- // FIXME: This code does not handle theme value resolution.
- // There is code to handle this, but it's in layoutlib; we should
- // expose that and use it here.
-
- Map<ResourceType, Map<String, ResourceValue>> map;
- map = isFrameworkResource ? mConfiguredFrameworkRes : mConfiguredProjectRes;
- if (map == null) {
- // Not yet configured
- return null;
- }
-
- Map<String, ResourceValue> layoutMap = map.get(type);
- if (layoutMap != null) {
- ResourceValue value = layoutMap.get(name);
- if (value != null) {
- String valueStr = value.getValue();
- if (valueStr.startsWith("?")) { //$NON-NLS-1$
- // FIXME: It's a reference. We should resolve this properly.
- return null;
- }
- return new Path(valueStr);
- }
- }
-
- return null;
- }
-
- /**
- * Looks up the path to the file corresponding to the given attribute value, such as
- * @layout/foo, which will return the foo.xml file in res/layout/. (The general format
- * of the resource url is {@literal @[<package_name>:]<resource_type>/<resource_name>}.
- *
- * @param url the attribute url
- * @return the path to the file defining this attribute, or null if not found
- */
- public IPath findResourceFile(String url) {
- if (!url.startsWith("@")) { //$NON-NLS-1$
- return null;
- }
- int typeEnd = url.indexOf('/', 1);
- if (typeEnd == -1) {
- return null;
- }
- int nameBegin = typeEnd + 1;
- int typeBegin = 1;
- int colon = url.lastIndexOf(':', typeEnd);
- boolean isFrameworkResource = false;
- if (colon != -1) {
- // The URL contains a package name.
- // While the url format technically allows other package names,
- // the platform apparently only supports @android for now (or if it does,
- // there are no usages in the current code base so this is not common).
- String packageName = url.substring(typeBegin, colon);
- if (ANDROID_PKG.equals(packageName)) {
- isFrameworkResource = true;
- }
-
- typeBegin = colon + 1;
- }
-
- String typeName = url.substring(typeBegin, typeEnd);
- ResourceType type = ResourceType.getEnum(typeName);
- if (type == null) {
- return null;
- }
-
- String name = url.substring(nameBegin);
- return findResourceFile(type, name, isFrameworkResource);
- }
-
- /**
- * Resolve the given @string reference into a literal String using the current project
- * configuration
- *
- * @param text the text resource reference to resolve
- * @return the resolved string, or null
- */
- public String findString(String text) {
- if (text.startsWith(STRING_PREFIX)) {
- return findString(text.substring(STRING_PREFIX.length()), false);
- } else if (text.startsWith(ANDROID_STRING_PREFIX)) {
- return findString(text.substring(ANDROID_STRING_PREFIX.length()), true);
- } else {
- return text;
- }
- }
-
- private String findString(String name, boolean isFrameworkResource) {
- Map<ResourceType, Map<String, ResourceValue>> map;
- map = isFrameworkResource ? mConfiguredFrameworkRes : mConfiguredProjectRes;
- if (map == null) {
- // Not yet configured
- return null;
- }
-
- Map<String, ResourceValue> layoutMap = map.get(ResourceType.STRING);
- if (layoutMap != null) {
- ResourceValue value = layoutMap.get(name);
- if (value != null) {
- // FIXME: This code does not handle theme value resolution.
- // There is code to handle this, but it's in layoutlib; we should
- // expose that and use it here.
- return value.getValue();
- }
- }
-
- return null;
- }
-
- /**
- * This StyleRange represents a clickable link in the render output, where various
- * actions can be taken such as creating a class, opening the project chooser to
- * adjust the build path, etc.
- */
- private class ActionLinkStyleRange extends StyleRange {
- /** Create a view class */
- private static final int LINK_CREATE_CLASS = 1;
- /** Edit the build path for the current project */
- private static final int LINK_FIX_BUILD_PATH = 2;
- /** Show the XML tab */
- private static final int LINK_EDIT_XML = 3;
- /** Open the given class */
- private static final int LINK_OPEN_CLASS = 4;
- /** Show the error log */
- private static final int LINK_SHOW_LOG = 5;
- /** Change the class reference to the given fully qualified name */
- private static final int LINK_CHANGE_CLASS_TO = 6;
- /** Ignore the given fidelity warning */
- private static final int IGNORE_FIDELITY_WARNING = 7;
- /** Set an attribute on the given XML element to a given value */
- private static final int SET_ATTRIBUTE = 8;
- /** Open the given file and line number */
- private static final int LINK_OPEN_LINE = 9;
- /** Disable sandbox */
- private static final int LINK_DISABLE_SANDBOX = 10;
-
- /** Client data: the contents depend on the specific action */
- private final Object[] mData;
- /** The action to be taken when the link is clicked */
- private final int mAction;
-
- private ActionLinkStyleRange(int action, Object... data) {
- super();
- mAction = action;
- mData = data;
- }
-
- /** Performs the click action */
- public void onClick() {
- switch (mAction) {
- case LINK_CREATE_CLASS:
- createNewClass((String) mData[0]);
- break;
- case LINK_EDIT_XML:
- mEditorDelegate.getEditor().setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
- break;
- case LINK_FIX_BUILD_PATH:
- @SuppressWarnings("restriction")
- String id = BuildPathsPropertyPage.PROP_ID;
- PreferencesUtil.createPropertyDialogOn(
- AdtPlugin.getShell(),
- getProject(), id, null, null).open();
- break;
- case LINK_OPEN_CLASS:
- AdtPlugin.openJavaClass(getProject(), (String) mData[0]);
- break;
- case LINK_OPEN_LINE:
- boolean success = AdtPlugin.openStackTraceLine(
- (String) mData[0], // class
- (String) mData[1], // method
- (String) mData[2], // file
- (Integer) mData[3]); // line
- if (!success) {
- MessageDialog.openError(mErrorLabel.getShell(), "Not Found",
- String.format("Could not find %1$s.%2$s", mData[0], mData[1]));
- }
- break;
- case LINK_SHOW_LOG:
- IWorkbench workbench = PlatformUI.getWorkbench();
- IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow();
- try {
- IWorkbenchPage page = workbenchWindow.getActivePage();
- page.showView("org.eclipse.pde.runtime.LogView"); //$NON-NLS-1$
- } catch (PartInitException e) {
- AdtPlugin.log(e, null);
- }
- break;
- case LINK_CHANGE_CLASS_TO:
- // Change class reference of mData[0] to mData[1]
- // TODO: run under undo lock
- MultiTextEdit edits = new MultiTextEdit();
- ISourceViewer textViewer =
- mEditorDelegate.getEditor().getStructuredSourceViewer();
- IDocument document = textViewer.getDocument();
- String xml = document.get();
- int index = 0;
- // Replace <old with <new and </old with </new
- String prefix = "<"; //$NON-NLS-1$
- String find = prefix + mData[0];
- String replaceWith = prefix + mData[1];
- while (true) {
- index = xml.indexOf(find, index);
- if (index == -1) {
- break;
- }
- edits.addChild(new ReplaceEdit(index, find.length(), replaceWith));
- index += find.length();
- }
- index = 0;
- prefix = "</"; //$NON-NLS-1$
- find = prefix + mData[0];
- replaceWith = prefix + mData[1];
- while (true) {
- index = xml.indexOf(find, index);
- if (index == -1) {
- break;
- }
- edits.addChild(new ReplaceEdit(index, find.length(), replaceWith));
- index += find.length();
- }
- // Handle <view class="old">
- index = 0;
- prefix = "\""; //$NON-NLS-1$
- String suffix = "\""; //$NON-NLS-1$
- find = prefix + mData[0] + suffix;
- replaceWith = prefix + mData[1] + suffix;
- while (true) {
- index = xml.indexOf(find, index);
- if (index == -1) {
- break;
- }
- edits.addChild(new ReplaceEdit(index, find.length(), replaceWith));
- index += find.length();
- }
- try {
- edits.apply(document);
- } catch (MalformedTreeException e) {
- AdtPlugin.log(e, null);
- } catch (BadLocationException e) {
- AdtPlugin.log(e, null);
- }
- break;
- case IGNORE_FIDELITY_WARNING:
- RenderLogger.ignoreFidelityWarning((String) mData[0]);
- recomputeLayout();
- break;
- case SET_ATTRIBUTE: {
- final UiElementNode element = (UiElementNode) mData[0];
- final String attribute = (String) mData[1];
- final String value = (String) mData[2];
- mEditorDelegate.getEditor().wrapUndoEditXmlModel(
- String.format("Set \"%1$s\" to \"%2$s\"", attribute, value),
- new Runnable() {
- @Override
- public void run() {
- element.setAttributeValue(attribute, ANDROID_URI, value, true);
- element.commitDirtyAttributesToXml();
- }
- });
- break;
- }
- case LINK_DISABLE_SANDBOX: {
- RenderSecurityManager.sEnabled = false;
- recomputeLayout();
-
- MessageDialog.openInformation(AdtPlugin.getShell(),
- "Disabled Rendering Sandbox",
- "The custom view rendering sandbox was disabled for this session.\n\n" +
- "You can turn it off permanently by adding\n" +
- "-D" + ENABLED_PROPERTY + "=" + VALUE_FALSE + "\n" +
- "as a new line in eclipse.ini.");
-
- break;
- }
- default:
- assert false : mAction;
- break;
- }
- }
-
- @Override
- public boolean similarTo(StyleRange style) {
- // Prevent adjacent link ranges from getting merged
- return false;
- }
- }
-
- /**
- * Returns the error label for the graphical editor (which may not be visible
- * or showing errors)
- *
- * @return the error label, never null
- */
- StyledText getErrorLabel() {
- return mErrorLabel;
- }
-
- /**
- * Monitor clicks on the error label.
- * If the click happens on a style range created by
- * {@link GraphicalEditorPart#addClassLink(StyledText, String)}, we assume it's about
- * a missing class and we then proceed to display the standard Eclipse class creator wizard.
- */
- private class ErrorLabelListener extends MouseAdapter {
-
- @Override
- public void mouseUp(MouseEvent event) {
- super.mouseUp(event);
-
- if (event.widget != mErrorLabel) {
- return;
- }
-
- int offset = mErrorLabel.getCaretOffset();
-
- StyleRange r = null;
- StyleRange[] ranges = mErrorLabel.getStyleRanges();
- if (ranges != null && ranges.length > 0) {
- for (StyleRange sr : ranges) {
- if (sr.start <= offset && sr.start + sr.length > offset) {
- r = sr;
- break;
- }
- }
- }
-
- if (r instanceof ActionLinkStyleRange) {
- ActionLinkStyleRange range = (ActionLinkStyleRange) r;
- range.onClick();
- }
-
- LayoutCanvas canvas = getCanvasControl();
- canvas.updateMenuActionState();
- }
- }
-
- private void createNewClass(String fqcn) {
-
- int pos = fqcn.lastIndexOf('.');
- String packageName = pos < 0 ? "" : fqcn.substring(0, pos); //$NON-NLS-1$
- String className = pos <= 0 || pos >= fqcn.length() ? "" : fqcn.substring(pos + 1); //$NON-NLS-1$
-
- // create the wizard page for the class creation, and configure it
- NewClassWizardPage page = new NewClassWizardPage();
-
- // set the parent class
- page.setSuperClass(SdkConstants.CLASS_VIEW, true /* canBeModified */);
-
- // get the source folders as java elements.
- IPackageFragmentRoot[] roots = getPackageFragmentRoots(
- mEditorDelegate.getEditor().getProject(),
- false /*includeContainers*/, true /*skipGenFolder*/);
-
- IPackageFragmentRoot currentRoot = null;
- IPackageFragment currentFragment = null;
- int packageMatchCount = -1;
-
- for (IPackageFragmentRoot root : roots) {
- // Get the java element for the package.
- // This method is said to always return a IPackageFragment even if the
- // underlying folder doesn't exist...
- IPackageFragment fragment = root.getPackageFragment(packageName);
- if (fragment != null && fragment.exists()) {
- // we have a perfect match! we use it.
- currentRoot = root;
- currentFragment = fragment;
- packageMatchCount = -1;
- break;
- } else {
- // we don't have a match. we look for the fragment with the best match
- // (ie the closest parent package we can find)
- try {
- IJavaElement[] children;
- children = root.getChildren();
- for (IJavaElement child : children) {
- if (child instanceof IPackageFragment) {
- fragment = (IPackageFragment)child;
- if (packageName.startsWith(fragment.getElementName())) {
- // its a match. get the number of segments
- String[] segments = fragment.getElementName().split("\\."); //$NON-NLS-1$
- if (segments.length > packageMatchCount) {
- packageMatchCount = segments.length;
- currentFragment = fragment;
- currentRoot = root;
- }
- }
- }
- }
- } catch (JavaModelException e) {
- // Couldn't get the children: we just ignore this package root.
- }
- }
- }
-
- ArrayList<IPackageFragment> createdFragments = null;
-
- if (currentRoot != null) {
- // if we have a perfect match, we set it and we're done.
- if (packageMatchCount == -1) {
- page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
- page.setPackageFragment(currentFragment, true /* canBeModified */);
- } else {
- // we have a partial match.
- // create the package. We have to start with the first segment so that we
- // know what to delete in case of a cancel.
- try {
- createdFragments = new ArrayList<IPackageFragment>();
-
- int totalCount = packageName.split("\\.").length; //$NON-NLS-1$
- int count = 0;
- int index = -1;
- // skip the matching packages
- while (count < packageMatchCount) {
- index = packageName.indexOf('.', index+1);
- count++;
- }
-
- // create the rest of the segments, except for the last one as indexOf will
- // return -1;
- while (count < totalCount - 1) {
- index = packageName.indexOf('.', index+1);
- count++;
- createdFragments.add(currentRoot.createPackageFragment(
- packageName.substring(0, index),
- true /* force*/, new NullProgressMonitor()));
- }
-
- // create the last package
- createdFragments.add(currentRoot.createPackageFragment(
- packageName, true /* force*/, new NullProgressMonitor()));
-
- // set the root and fragment in the Wizard page
- page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
- page.setPackageFragment(createdFragments.get(createdFragments.size()-1),
- true /* canBeModified */);
- } catch (JavaModelException e) {
- // If we can't create the packages, there's a problem.
- // We revert to the default package
- for (IPackageFragmentRoot root : roots) {
- // Get the java element for the package.
- // This method is said to always return a IPackageFragment even if the
- // underlying folder doesn't exist...
- IPackageFragment fragment = root.getPackageFragment(packageName);
- if (fragment != null && fragment.exists()) {
- page.setPackageFragmentRoot(root, true /* canBeModified*/);
- page.setPackageFragment(fragment, true /* canBeModified */);
- break;
- }
- }
- }
- }
- } else if (roots.length > 0) {
- // if we haven't found a valid fragment, we set the root to the first source folder.
- page.setPackageFragmentRoot(roots[0], true /* canBeModified*/);
- }
-
- // if we have a starting class name we use it
- if (className != null) {
- page.setTypeName(className, true /* canBeModified*/);
- }
-
- // create the action that will open it the wizard.
- OpenNewClassWizardAction action = new OpenNewClassWizardAction();
- action.setConfiguredWizardPage(page);
- action.run();
- IJavaElement element = action.getCreatedElement();
-
- if (element == null) {
- // lets delete the packages we created just for this.
- // we need to start with the leaf and go up
- if (createdFragments != null) {
- try {
- for (int i = createdFragments.size() - 1 ; i >= 0 ; i--) {
- createdFragments.get(i).delete(true /* force*/,
- new NullProgressMonitor());
- }
- } catch (JavaModelException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /**
- * Computes and return the {@link IPackageFragmentRoot}s corresponding to the source
- * folders of the specified project.
- *
- * @param project the project
- * @param includeContainers True to include containers
- * @param skipGenFolder True to skip the "gen" folder
- * @return an array of IPackageFragmentRoot.
- */
- private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project,
- boolean includeContainers, boolean skipGenFolder) {
- ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
- try {
- IJavaProject javaProject = JavaCore.create(project);
- IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
- for (int i = 0; i < roots.length; i++) {
- if (skipGenFolder) {
- IResource resource = roots[i].getResource();
- if (resource != null && resource.getName().equals(FD_GEN_SOURCES)) {
- continue;
- }
- }
- IClasspathEntry entry = roots[i].getRawClasspathEntry();
- if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE ||
- (includeContainers &&
- entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER)) {
- result.add(roots[i]);
- }
- }
- } catch (JavaModelException e) {
- }
-
- return result.toArray(new IPackageFragmentRoot[result.size()]);
- }
-
- /**
- * Reopens this file as included within the given file (this assumes that the given
- * file has an include tag referencing this view, and the set of views that have this
- * property can be found using the {@link IncludeFinder}.
- *
- * @param includeWithin reference to a file to include as a surrounding context,
- * or null to show the file standalone
- */
- public void showIn(Reference includeWithin) {
- mIncludedWithin = includeWithin;
-
- if (includeWithin != null) {
- IFile file = includeWithin.getFile();
-
- // Update configuration
- if (file != null) {
- mConfigChooser.resetConfigFor(file);
- }
- }
- recomputeLayout();
- }
-
- /**
- * Return all resource names of a given type, either in the project or in the
- * framework.
- *
- * @param framework if true, return all the framework resource names, otherwise return
- * all the project resource names
- * @param type the type of resource to look up
- * @return a collection of resource names, never null but possibly empty
- */
- public Collection<String> getResourceNames(boolean framework, ResourceType type) {
- Map<ResourceType, Map<String, ResourceValue>> map =
- framework ? mConfiguredFrameworkRes : mConfiguredProjectRes;
- Map<String, ResourceValue> animations = map.get(type);
- if (animations != null) {
- return animations.keySet();
- } else {
- return Collections.emptyList();
- }
- }
-
- /**
- * Return this editor's current configuration
- *
- * @return the current configuration
- */
- public FolderConfiguration getConfiguration() {
- return mConfigChooser.getConfiguration().getFullConfig();
- }
-
- /**
- * Figures out the project's minSdkVersion and targetSdkVersion and return whether the values
- * have changed.
- */
- private boolean computeSdkVersion() {
- int oldMinSdkVersion = mMinSdkVersion;
- int oldTargetSdkVersion = mTargetSdkVersion;
-
- Pair<Integer, Integer> v = ManifestInfo.computeSdkVersions(mEditedFile.getProject());
- mMinSdkVersion = v.getFirst();
- mTargetSdkVersion = v.getSecond();
-
- return oldMinSdkVersion != mMinSdkVersion || oldTargetSdkVersion != mTargetSdkVersion;
- }
-
- /**
- * Returns the associated configuration chooser
- *
- * @return the configuration chooser
- */
- @NonNull
- public ConfigurationChooser getConfigurationChooser() {
- return mConfigChooser;
- }
-
- /**
- * Returns the associated layout actions bar
- *
- * @return the layout actions bar
- */
- @NonNull
- public LayoutActionBar getLayoutActionBar() {
- return mActionBar;
- }
-
- /**
- * Returns the target SDK version
- *
- * @return the target SDK version
- */
- public int getTargetSdkVersion() {
- return mTargetSdkVersion;
- }
-
- /**
- * Returns the minimum SDK version
- *
- * @return the minimum SDK version
- */
- public int getMinSdkVersion() {
- return mMinSdkVersion;
- }
-
- /** If the flyout hover is showing, dismiss it */
- public void dismissHoverPalette() {
- mPaletteComposite.dismissHover();
- }
-
- // ---- Implements IFlyoutListener ----
-
- @Override
- public void stateChanged(int oldState, int newState) {
- // Auto zoom the surface if you open or close flyout windows such as the palette
- // or the property/outline views
- if (newState == STATE_OPEN || newState == STATE_COLLAPSED && oldState == STATE_OPEN) {
- getCanvasControl().setFitScale(true /*onlyZoomOut*/, true /*allowZoomIn*/);
- }
-
- sDockingStateVersion++;
- mDockingStateVersion = sDockingStateVersion;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java
deleted file mode 100644
index 2e7c559db..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/HoverOverlay.java
+++ /dev/null
@@ -1,187 +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.ide.eclipse.adt.internal.editors.layout.gle2.SwtDrawingStyle.HOVER;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtDrawingStyle.HOVER_SELECTION;
-
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-
-import java.util.List;
-
-/**
- * The {@link HoverOverlay} paints an optional hover on top of the layout,
- * highlighting the currently hovered view.
- */
-public class HoverOverlay extends Overlay {
- private final LayoutCanvas mCanvas;
-
- /** Hover border color. Must be disposed, it's NOT a system color. */
- private Color mHoverStrokeColor;
-
- /** Hover fill color. Must be disposed, it's NOT a system color. */
- private Color mHoverFillColor;
-
- /** Hover border select color. Must be disposed, it's NOT a system color. */
- private Color mHoverSelectStrokeColor;
-
- /** Hover fill select color. Must be disposed, it's NOT a system color. */
- private Color mHoverSelectFillColor;
-
- /** Vertical scaling & scrollbar information. */
- private CanvasTransform mVScale;
-
- /** Horizontal scaling & scrollbar information. */
- private CanvasTransform mHScale;
-
- /**
- * Current mouse hover border rectangle. Null when there's no mouse hover.
- * The rectangle coordinates do not take account of the translation, which
- * must be applied to the rectangle when drawing.
- */
- private Rectangle mHoverRect;
-
- /**
- * Constructs a new {@link HoverOverlay} linked to the given view hierarchy.
- *
- * @param canvas the associated canvas
- * @param hScale The {@link CanvasTransform} to use to transfer horizontal layout
- * coordinates to screen coordinates.
- * @param vScale The {@link CanvasTransform} to use to transfer vertical layout
- * coordinates to screen coordinates.
- */
- public HoverOverlay(LayoutCanvas canvas, CanvasTransform hScale, CanvasTransform vScale) {
- mCanvas = canvas;
- mHScale = hScale;
- mVScale = vScale;
- }
-
- @Override
- public void create(Device device) {
- if (SwtDrawingStyle.HOVER.getStrokeColor() != null) {
- mHoverStrokeColor = new Color(device, SwtDrawingStyle.HOVER.getStrokeColor());
- }
- if (SwtDrawingStyle.HOVER.getFillColor() != null) {
- mHoverFillColor = new Color(device, SwtDrawingStyle.HOVER.getFillColor());
- }
-
- if (SwtDrawingStyle.HOVER_SELECTION.getStrokeColor() != null) {
- mHoverSelectStrokeColor = new Color(device,
- SwtDrawingStyle.HOVER_SELECTION.getStrokeColor());
- }
- if (SwtDrawingStyle.HOVER_SELECTION.getFillColor() != null) {
- mHoverSelectFillColor = new Color(device,
- SwtDrawingStyle.HOVER_SELECTION.getFillColor());
- }
- }
-
- @Override
- public void dispose() {
- if (mHoverStrokeColor != null) {
- mHoverStrokeColor.dispose();
- mHoverStrokeColor = null;
- }
-
- if (mHoverFillColor != null) {
- mHoverFillColor.dispose();
- mHoverFillColor = null;
- }
-
- if (mHoverSelectStrokeColor != null) {
- mHoverSelectStrokeColor.dispose();
- mHoverSelectStrokeColor = null;
- }
-
- if (mHoverSelectFillColor != null) {
- mHoverSelectFillColor.dispose();
- mHoverSelectFillColor = null;
- }
- }
-
- /**
- * Sets the hover rectangle. The coordinates of the rectangle are in layout
- * coordinates. The recipient is will own this rectangle.
- * <p/>
- * TODO: Consider switching input arguments to two {@link LayoutPoint}s so
- * we don't have ambiguity about the coordinate system of these input
- * parameters.
- * <p/>
- *
- * @param x The top left x coordinate, in layout coordinates, of the hover.
- * @param y The top left y coordinate, in layout coordinates, of the hover.
- * @param w The width of the hover (in layout coordinates).
- * @param h The height of the hover (in layout coordinates).
- */
- public void setHover(int x, int y, int w, int h) {
- mHoverRect = new Rectangle(x, y, w, h);
- }
-
- /**
- * Removes the hover for the next paint.
- */
- public void clearHover() {
- mHoverRect = null;
- }
-
- @Override
- public void paint(GC gc) {
- if (mHoverRect != null) {
- // Translate the hover rectangle (in canvas coordinates) to control
- // coordinates
- int x = mHScale.translate(mHoverRect.x);
- int y = mVScale.translate(mHoverRect.y);
- int w = mHScale.scale(mHoverRect.width);
- int h = mVScale.scale(mHoverRect.height);
-
-
- boolean hoverIsSelected = false;
- List<SelectionItem> selections = mCanvas.getSelectionManager().getSelections();
- for (SelectionItem item : selections) {
- if (mHoverRect.equals(item.getViewInfo().getSelectionRect())) {
- hoverIsSelected = true;
- break;
- }
- }
-
- Color stroke = hoverIsSelected ? mHoverSelectStrokeColor : mHoverStrokeColor;
- Color fill = hoverIsSelected ? mHoverSelectFillColor : mHoverFillColor;
-
- if (stroke != null) {
- int oldAlpha = gc.getAlpha();
- gc.setForeground(stroke);
- gc.setLineStyle(hoverIsSelected ?
- HOVER_SELECTION.getLineStyle() : HOVER.getLineStyle());
- gc.setAlpha(hoverIsSelected ?
- HOVER_SELECTION.getStrokeAlpha() : HOVER.getStrokeAlpha());
- gc.drawRectangle(x, y, w, h);
- gc.setAlpha(oldAlpha);
- }
-
- if (fill != null) {
- int oldAlpha = gc.getAlpha();
- gc.setAlpha(hoverIsSelected ?
- HOVER_SELECTION.getFillAlpha() : HOVER.getFillAlpha());
- gc.setBackground(fill);
- gc.fillRectangle(x, y, w, h);
- gc.setAlpha(oldAlpha);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java
deleted file mode 100644
index 4447eebd2..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageControl.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Canvas;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * An ImageControl which simply renders an image, with optional margins and tooltips. This
- * is useful since a {@link CLabel}, even without text, will hide the image when there is
- * not enough room to fully fit it.
- * <p>
- * The image is always rendered left and top aligned.
- */
-public class ImageControl extends Canvas implements MouseTrackListener {
- private Image mImage;
- private int mLeftMargin;
- private int mTopMargin;
- private int mRightMargin;
- private int mBottomMargin;
- private boolean mDisposeImage = true;
- private boolean mMouseIn;
- private Color mHoverColor;
- private float mScale = 1.0f;
-
- /**
- * Creates an ImageControl rendering the given image, which will be disposed when this
- * control is disposed (unless the {@link #setDisposeImage} method is called to turn
- * off auto dispose).
- *
- * @param parent the parent to add the image control to
- * @param style the SWT style to use
- * @param image the image to be rendered, which must not be null and should be unique
- * for this image control since it will be disposed by this control when
- * the control is disposed (unless the {@link #setDisposeImage} method is
- * called to turn off auto dispose)
- */
- public ImageControl(@NonNull Composite parent, int style, @Nullable Image image) {
- super(parent, style | SWT.NO_FOCUS | SWT.DOUBLE_BUFFERED);
- mImage = image;
-
- addPaintListener(new PaintListener() {
- @Override
- public void paintControl(PaintEvent event) {
- onPaint(event);
- }
- });
- }
-
- @Nullable
- public Image getImage() {
- return mImage;
- }
-
- public void setImage(@Nullable Image image) {
- if (mDisposeImage && mImage != null) {
- mImage.dispose();
- }
- mImage = image;
- redraw();
- }
-
- public void fitToWidth(int width) {
- if (mImage == null) {
- return;
- }
- Rectangle imageRect = mImage.getBounds();
- int imageWidth = imageRect.width;
- if (imageWidth <= width) {
- mScale = 1.0f;
- return;
- }
-
- mScale = width / (float) imageWidth;
- redraw();
- }
-
- public void setScale(float scale) {
- mScale = scale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- public void setHoverColor(@Nullable Color hoverColor) {
- if (mHoverColor != null) {
- removeMouseTrackListener(this);
- }
- mHoverColor = hoverColor;
- if (hoverColor != null) {
- addMouseTrackListener(this);
- }
- }
-
- @Nullable
- public Color getHoverColor() {
- return mHoverColor;
- }
-
- @Override
- public void dispose() {
- super.dispose();
-
- if (mDisposeImage && mImage != null && !mImage.isDisposed()) {
- mImage.dispose();
- }
- mImage = null;
- }
-
- public void setDisposeImage(boolean disposeImage) {
- mDisposeImage = disposeImage;
- }
-
- public boolean getDisposeImage() {
- return mDisposeImage;
- }
-
- @Override
- public Point computeSize(int wHint, int hHint, boolean changed) {
- checkWidget();
- Point e = new Point(0, 0);
- if (mImage != null) {
- Rectangle r = mImage.getBounds();
- if (mScale != 1.0f) {
- e.x += mScale * r.width;
- e.y += mScale * r.height;
- } else {
- e.x += r.width;
- e.y += r.height;
- }
- }
- if (wHint == SWT.DEFAULT) {
- e.x += mLeftMargin + mRightMargin;
- } else {
- e.x = wHint;
- }
- if (hHint == SWT.DEFAULT) {
- e.y += mTopMargin + mBottomMargin;
- } else {
- e.y = hHint;
- }
-
- return e;
- }
-
- private void onPaint(PaintEvent event) {
- Rectangle rect = getClientArea();
- if (mImage == null || rect.width == 0 || rect.height == 0) {
- return;
- }
-
- GC gc = event.gc;
- Rectangle imageRect = mImage.getBounds();
- int imageHeight = imageRect.height;
- int imageWidth = imageRect.width;
- int destWidth = imageWidth;
- int destHeight = imageHeight;
-
- int oldGcAlias = gc.getAntialias();
- int oldGcInterpolation = gc.getInterpolation();
- if (mScale != 1.0f) {
- destWidth = (int) (mScale * destWidth);
- destHeight = (int) (mScale * destHeight);
- gc.setAntialias(SWT.ON);
- gc.setInterpolation(SWT.HIGH);
- }
-
- gc.drawImage(mImage, 0, 0, imageWidth, imageHeight, rect.x + mLeftMargin, rect.y
- + mTopMargin, destWidth, destHeight);
-
- gc.setAntialias(oldGcAlias);
- gc.setInterpolation(oldGcInterpolation);
-
- if (mHoverColor != null && mMouseIn) {
- gc.setAlpha(60);
- gc.setBackground(mHoverColor);
- gc.setLineWidth(1);
- gc.fillRectangle(0, 0, destWidth, destHeight);
- }
- }
-
- public void setMargins(int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
- checkWidget();
- mLeftMargin = Math.max(0, leftMargin);
- mTopMargin = Math.max(0, topMargin);
- mRightMargin = Math.max(0, rightMargin);
- mBottomMargin = Math.max(0, bottomMargin);
- redraw();
- }
-
- // ---- Implements MouseTrackListener ----
-
- @Override
- public void mouseEnter(MouseEvent e) {
- mMouseIn = true;
- if (mHoverColor != null) {
- redraw();
- }
- }
-
- @Override
- public void mouseExit(MouseEvent e) {
- mMouseIn = false;
- if (mHoverColor != null) {
- redraw();
- }
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
deleted file mode 100644
index a1363ecb1..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
+++ /dev/null
@@ -1,447 +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.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE;
-
-import com.android.SdkConstants;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.rendering.api.IImageFactory;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.SWTException;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.PaletteData;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.awt.image.WritableRaster;
-import java.lang.ref.SoftReference;
-
-/**
- * The {@link ImageOverlay} class renders an image as an overlay.
- */
-public class ImageOverlay extends Overlay implements IImageFactory {
- /**
- * Whether the image should be pre-scaled (scaled to the zoom level) once
- * instead of dynamically during each paint; this is necessary on some
- * platforms (see issue #19447)
- */
- private static final boolean PRESCALE =
- // Currently this is necessary on Linux because the "Cairo" library
- // seems to be a bottleneck
- SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX
- && !(Boolean.getBoolean("adt.noprescale")); //$NON-NLS-1$
-
- /** Current background image. Null when there's no image. */
- private Image mImage;
-
- /** A pre-scaled version of the image */
- private Image mPreScaledImage;
-
- /** Whether the rendered image should have a drop shadow */
- private boolean mShowDropShadow;
-
- /** Current background AWT image. This is created by {@link #getImage()}, which is called
- * by the LayoutLib. */
- private SoftReference<BufferedImage> mAwtImage = new SoftReference<BufferedImage>(null);
-
- /**
- * Strong reference to the image in the above soft reference, to prevent
- * garbage collection when {@link PRESCALE} is set, until the scaled image
- * is created (lazily as part of the next paint call, where this strong
- * reference is nulled out and the above soft reference becomes eligible to
- * be reclaimed when memory is low.)
- */
- @SuppressWarnings("unused") // Used by the garbage collector to keep mAwtImage non-soft
- private BufferedImage mAwtImageStrongRef;
-
- /** The associated {@link LayoutCanvas}. */
- private LayoutCanvas mCanvas;
-
- /** Vertical scaling & scrollbar information. */
- private CanvasTransform mVScale;
-
- /** Horizontal scaling & scrollbar information. */
- private CanvasTransform mHScale;
-
- /**
- * Constructs an {@link ImageOverlay} tied to the given canvas.
- *
- * @param canvas The {@link LayoutCanvas} to paint the overlay over.
- * @param hScale The horizontal scale information.
- * @param vScale The vertical scale information.
- */
- public ImageOverlay(LayoutCanvas canvas, CanvasTransform hScale, CanvasTransform vScale) {
- mCanvas = canvas;
- mHScale = hScale;
- mVScale = vScale;
- }
-
- @Override
- public void create(Device device) {
- super.create(device);
- }
-
- @Override
- public void dispose() {
- if (mImage != null) {
- mImage.dispose();
- mImage = null;
- }
- if (mPreScaledImage != null) {
- mPreScaledImage.dispose();
- mPreScaledImage = null;
- }
- }
-
- /**
- * Sets the image to be drawn as an overlay from the passed in AWT
- * {@link BufferedImage} (which will be converted to an SWT image).
- * <p/>
- * The image <b>can</b> be null, which is the case when we are dealing with
- * an empty document.
- *
- * @param awtImage The AWT image to be rendered as an SWT image.
- * @param isAlphaChannelImage whether the alpha channel of the image is relevant
- * @return The corresponding SWT image, or null.
- */
- public synchronized Image setImage(BufferedImage awtImage, boolean isAlphaChannelImage) {
- mShowDropShadow = !isAlphaChannelImage;
-
- BufferedImage oldAwtImage = mAwtImage.get();
- if (awtImage != oldAwtImage || awtImage == null) {
- mAwtImage.clear();
- mAwtImageStrongRef = null;
-
- if (mImage != null) {
- mImage.dispose();
- }
-
- if (awtImage == null) {
- mImage = null;
- } else {
- mImage = SwtUtils.convertToSwt(mCanvas.getDisplay(), awtImage,
- isAlphaChannelImage, -1);
- }
- } else {
- assert awtImage instanceof SwtReadyBufferedImage;
-
- if (isAlphaChannelImage) {
- if (mImage != null) {
- mImage.dispose();
- }
-
- mImage = SwtUtils.convertToSwt(mCanvas.getDisplay(), awtImage, true, -1);
- } else {
- Image prev = mImage;
- mImage = ((SwtReadyBufferedImage)awtImage).getSwtImage();
- if (prev != mImage && prev != null) {
- prev.dispose();
- }
- }
- }
-
- if (mPreScaledImage != null) {
- // Force refresh on next paint
- mPreScaledImage.dispose();
- mPreScaledImage = null;
- }
-
- return mImage;
- }
-
- /**
- * Returns the currently painted image, or null if none has been set
- *
- * @return the currently painted image or null
- */
- public Image getImage() {
- return mImage;
- }
-
- /**
- * Returns the currently rendered image, or null if none has been set
- *
- * @return the currently rendered image or null
- */
- @Nullable
- BufferedImage getAwtImage() {
- BufferedImage awtImage = mAwtImage.get();
- if (awtImage == null && mImage != null) {
- awtImage = SwtUtils.convertToAwt(mImage);
- }
-
- return awtImage;
- }
-
- /**
- * Returns whether this image overlay should be painted with a drop shadow.
- * This is usually the case, but not for transparent themes like the dialog
- * theme (Theme.*Dialog), which already provides its own shadow.
- *
- * @return true if the image overlay should be shown with a drop shadow.
- */
- public boolean getShowDropShadow() {
- return mShowDropShadow;
- }
-
- @Override
- public synchronized void paint(GC gc) {
- if (mImage != null) {
- boolean valid = mCanvas.getViewHierarchy().isValid();
- mCanvas.ensureZoomed();
- if (!valid) {
- gc_setAlpha(gc, 128); // half-transparent
- }
-
- CanvasTransform hi = mHScale;
- CanvasTransform vi = mVScale;
-
- // On some platforms, dynamic image scaling is very slow (see issue #19447) so
- // compute a pre-scaled version of the image once and render that instead.
- // This is done lazily in paint rather than when the image changes because
- // the image must be rescaled each time the zoom level changes, which varies
- // independently from when the image changes.
- BufferedImage awtImage = mAwtImage.get();
- if (PRESCALE && awtImage != null) {
- int imageWidth = (mPreScaledImage == null) ? 0
- : mPreScaledImage.getImageData().width
- - (mShowDropShadow ? SHADOW_SIZE : 0);
- if (mPreScaledImage == null || imageWidth != hi.getScaledImgSize()) {
- double xScale = hi.getScaledImgSize() / (double) awtImage.getWidth();
- double yScale = vi.getScaledImgSize() / (double) awtImage.getHeight();
- BufferedImage scaledAwtImage;
-
- // NOTE: == comparison on floating point numbers is okay
- // here because we normalize the scaling factor
- // to an exact 1.0 in the zooming code when the value gets
- // near 1.0 to make painting more efficient in the presence
- // of rounding errors.
- if (xScale == 1.0 && yScale == 1.0) {
- // Scaling to 100% is easy!
- scaledAwtImage = awtImage;
-
- if (mShowDropShadow) {
- // Just need to draw drop shadows
- scaledAwtImage = ImageUtils.createRectangularDropShadow(awtImage);
- }
- } else {
- if (mShowDropShadow) {
- scaledAwtImage = ImageUtils.scale(awtImage, xScale, yScale,
- SHADOW_SIZE, SHADOW_SIZE);
- ImageUtils.drawRectangleShadow(scaledAwtImage, 0, 0,
- scaledAwtImage.getWidth() - SHADOW_SIZE,
- scaledAwtImage.getHeight() - SHADOW_SIZE);
- } else {
- scaledAwtImage = ImageUtils.scale(awtImage, xScale, yScale);
- }
- }
-
- if (mPreScaledImage != null && !mPreScaledImage.isDisposed()) {
- mPreScaledImage.dispose();
- }
- mPreScaledImage = SwtUtils.convertToSwt(mCanvas.getDisplay(), scaledAwtImage,
- true /*transferAlpha*/, -1);
- // We can't just clear the mAwtImageStrongRef here, because if the
- // zooming factor changes, we may need to use it again
- }
-
- if (mPreScaledImage != null) {
- gc.drawImage(mPreScaledImage, hi.translate(0), vi.translate(0));
- }
- return;
- }
-
- // we only anti-alias when reducing the image size.
- int oldAlias = -2;
- if (hi.getScale() < 1.0) {
- oldAlias = gc_setAntialias(gc, SWT.ON);
- }
-
- int srcX = 0;
- int srcY = 0;
- int srcWidth = hi.getImgSize();
- int srcHeight = vi.getImgSize();
- int destX = hi.translate(0);
- int destY = vi.translate(0);
- int destWidth = hi.getScaledImgSize();
- int destHeight = vi.getScaledImgSize();
-
- gc.drawImage(mImage,
- srcX, srcY, srcWidth, srcHeight,
- destX, destY, destWidth, destHeight);
-
- if (mShowDropShadow) {
- SwtUtils.drawRectangleShadow(gc, destX, destY, destWidth, destHeight);
- }
-
- if (oldAlias != -2) {
- gc_setAntialias(gc, oldAlias);
- }
-
- if (!valid) {
- gc_setAlpha(gc, 255); // opaque
- }
- }
- }
-
- /**
- * Sets the alpha for the given GC.
- * <p/>
- * Alpha may not work on all platforms and may fail with an exception, which
- * is hidden here (false is returned in that case).
- *
- * @param gc the GC to change
- * @param alpha the new alpha, 0 for transparent, 255 for opaque.
- * @return True if the operation worked, false if it failed with an
- * exception.
- * @see GC#setAlpha(int)
- */
- private boolean gc_setAlpha(GC gc, int alpha) {
- try {
- gc.setAlpha(alpha);
- return true;
- } catch (SWTException e) {
- return false;
- }
- }
-
- /**
- * Sets the non-text antialias flag for the given GC.
- * <p/>
- * Antialias may not work on all platforms and may fail with an exception,
- * which is hidden here (-2 is returned in that case).
- *
- * @param gc the GC to change
- * @param alias One of {@link SWT#DEFAULT}, {@link SWT#ON}, {@link SWT#OFF}.
- * @return The previous aliasing mode if the operation worked, or -2 if it
- * failed with an exception.
- * @see GC#setAntialias(int)
- */
- private int gc_setAntialias(GC gc, int alias) {
- try {
- int old = gc.getAntialias();
- gc.setAntialias(alias);
- return old;
- } catch (SWTException e) {
- return -2;
- }
- }
-
- /**
- * Custom {@link BufferedImage} class able to convert itself into an SWT {@link Image}
- * efficiently.
- *
- * The BufferedImage also contains an instance of {@link ImageData} that's kept around
- * and used to create new SWT {@link Image} objects in {@link #getSwtImage()}.
- *
- */
- private static final class SwtReadyBufferedImage extends BufferedImage {
-
- private final ImageData mImageData;
- private final Device mDevice;
-
- /**
- * Creates the image with a given model, raster and SWT {@link ImageData}
- * @param model the color model
- * @param raster the image raster
- * @param imageData the SWT image data.
- * @param device the {@link Device} in which the SWT image will be painted.
- */
- private SwtReadyBufferedImage(int width, int height, ImageData imageData, Device device) {
- super(width, height, BufferedImage.TYPE_INT_ARGB);
- mImageData = imageData;
- mDevice = device;
- }
-
- /**
- * Returns a new {@link Image} object initialized with the content of the BufferedImage.
- * @return the image object.
- */
- private Image getSwtImage() {
- // transfer the content of the bufferedImage into the image data.
- WritableRaster raster = getRaster();
- int[] imageDataBuffer = ((DataBufferInt) raster.getDataBuffer()).getData();
-
- mImageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
-
- return new Image(mDevice, mImageData);
- }
-
- /**
- * Creates a new {@link SwtReadyBufferedImage}.
- * @param w the width of the image
- * @param h the height of the image
- * @param device the device in which the SWT image will be painted
- * @return a new {@link SwtReadyBufferedImage} object
- */
- private static SwtReadyBufferedImage createImage(int w, int h, Device device) {
- // NOTE: We can't make this image bigger to accommodate the drop shadow directly
- // (such that we could paint one into the image after a layoutlib render)
- // since this image is in the full resolution of the device, and gets scaled
- // to fit in the layout editor. This would have the net effect of causing
- // the drop shadow to get zoomed/scaled along with the scene, making a tiny
- // drop shadow for tablet layouts, a huge drop shadow for tiny QVGA screens, etc.
-
- ImageData imageData = new ImageData(w, h, 32,
- new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
-
- SwtReadyBufferedImage swtReadyImage = new SwtReadyBufferedImage(w, h,
- imageData, device);
-
- return swtReadyImage;
- }
- }
-
- /**
- * Implementation of {@link IImageFactory#getImage(int, int)}.
- */
- @Override
- public BufferedImage getImage(int w, int h) {
- BufferedImage awtImage = mAwtImage.get();
- if (awtImage == null ||
- awtImage.getWidth() != w ||
- awtImage.getHeight() != h) {
- mAwtImage.clear();
- awtImage = SwtReadyBufferedImage.createImage(w, h, getDevice());
- mAwtImage = new SoftReference<BufferedImage>(awtImage);
- if (PRESCALE) {
- mAwtImageStrongRef = awtImage;
- }
- }
-
- return awtImage;
- }
-
- /**
- * Returns the bounds of the current image, or null
- *
- * @return the bounds of the current image, or null
- */
- public Rect getImageBounds() {
- if (mImage == null) {
- return null;
- }
-
- return new Rect(0, 0, mImage.getImageData().width, mImage.getImageData().height);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java
deleted file mode 100644
index b5bc9aa72..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java
+++ /dev/null
@@ -1,979 +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.DOT_9PNG;
-import static com.android.SdkConstants.DOT_BMP;
-import static com.android.SdkConstants.DOT_GIF;
-import static com.android.SdkConstants.DOT_JPG;
-import static com.android.SdkConstants.DOT_PNG;
-import static com.android.utils.SdkUtils.endsWithIgnoreCase;
-import static java.awt.RenderingHints.KEY_ANTIALIASING;
-import static java.awt.RenderingHints.KEY_INTERPOLATION;
-import static java.awt.RenderingHints.KEY_RENDERING;
-import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;
-import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR;
-import static java.awt.RenderingHints.VALUE_RENDER_QUALITY;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.Rect;
-import com.android.ide.eclipse.adt.AdtPlugin;
-
-import org.eclipse.swt.graphics.RGB;
-import org.eclipse.swt.graphics.Rectangle;
-
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.imageio.ImageIO;
-
-/**
- * Utilities related to image processing.
- */
-public class ImageUtils {
- /**
- * Returns true if the given image has no dark pixels
- *
- * @param image the image to be checked for dark pixels
- * @return true if no dark pixels were found
- */
- public static boolean containsDarkPixels(BufferedImage image) {
- for (int y = 0, height = image.getHeight(); y < height; y++) {
- for (int x = 0, width = image.getWidth(); x < width; x++) {
- int pixel = image.getRGB(x, y);
- if ((pixel & 0xFF000000) != 0) {
- int r = (pixel & 0xFF0000) >> 16;
- int g = (pixel & 0x00FF00) >> 8;
- int b = (pixel & 0x0000FF);
-
- // One perceived luminance formula is (0.299*red + 0.587*green + 0.114*blue)
- // In order to keep this fast since we don't need a very accurate
- // measure, I'll just estimate this with integer math:
- long brightness = (299L*r + 587*g + 114*b) / 1000;
- if (brightness < 128) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Returns the perceived brightness of the given RGB integer on a scale from 0 to 255
- *
- * @param rgb the RGB triplet, 8 bits each
- * @return the perceived brightness, with 0 maximally dark and 255 maximally bright
- */
- public static int getBrightness(int rgb) {
- if ((rgb & 0xFFFFFF) != 0) {
- int r = (rgb & 0xFF0000) >> 16;
- int g = (rgb & 0x00FF00) >> 8;
- int b = (rgb & 0x0000FF);
- // See the containsDarkPixels implementation for details
- return (int) ((299L*r + 587*g + 114*b) / 1000);
- }
-
- return 0;
- }
-
- /**
- * Converts an alpha-red-green-blue integer color into an {@link RGB} color.
- * <p>
- * <b>NOTE</b> - this will drop the alpha value since {@link RGB} objects do not
- * contain transparency information.
- *
- * @param rgb the RGB integer to convert to a color description
- * @return the color description corresponding to the integer
- */
- public static RGB intToRgb(int rgb) {
- return new RGB((rgb & 0xFF0000) >>> 16, (rgb & 0xFF00) >>> 8, rgb & 0xFF);
- }
-
- /**
- * Converts an {@link RGB} color into a alpha-red-green-blue integer
- *
- * @param rgb the RGB color descriptor to convert
- * @param alpha the amount of alpha to add into the color integer (since the
- * {@link RGB} objects do not contain an alpha channel)
- * @return an integer corresponding to the {@link RGB} color
- */
- public static int rgbToInt(RGB rgb, int alpha) {
- return alpha << 24 | (rgb.red << 16) | (rgb.green << 8) | rgb.blue;
- }
-
- /**
- * Crops blank pixels from the edges of the image and returns the cropped result. We
- * crop off pixels that are blank (meaning they have an alpha value = 0). Note that
- * this is not the same as pixels that aren't opaque (an alpha value other than 255).
- *
- * @param image the image to be cropped
- * @param initialCrop If not null, specifies a rectangle which contains an initial
- * crop to continue. This can be used to crop an image where you already
- * know about margins in the image
- * @return a cropped version of the source image, or null if the whole image was blank
- * and cropping completely removed everything
- */
- @Nullable
- public static BufferedImage cropBlank(
- @NonNull BufferedImage image,
- @Nullable Rect initialCrop) {
- return cropBlank(image, initialCrop, image.getType());
- }
-
- /**
- * Crops blank pixels from the edges of the image and returns the cropped result. We
- * crop off pixels that are blank (meaning they have an alpha value = 0). Note that
- * this is not the same as pixels that aren't opaque (an alpha value other than 255).
- *
- * @param image the image to be cropped
- * @param initialCrop If not null, specifies a rectangle which contains an initial
- * crop to continue. This can be used to crop an image where you already
- * know about margins in the image
- * @param imageType the type of {@link BufferedImage} to create
- * @return a cropped version of the source image, or null if the whole image was blank
- * and cropping completely removed everything
- */
- public static BufferedImage cropBlank(BufferedImage image, Rect initialCrop, int imageType) {
- CropFilter filter = new CropFilter() {
- @Override
- public boolean crop(BufferedImage bufferedImage, int x, int y) {
- int rgb = bufferedImage.getRGB(x, y);
- return (rgb & 0xFF000000) == 0x00000000;
- // TODO: Do a threshold of 80 instead of just 0? Might give better
- // visual results -- e.g. check <= 0x80000000
- }
- };
- return crop(image, filter, initialCrop, imageType);
- }
-
- /**
- * Crops pixels of a given color from the edges of the image and returns the cropped
- * result.
- *
- * @param image the image to be cropped
- * @param blankArgb the color considered to be blank, as a 32 pixel integer with 8
- * bits of alpha, red, green and blue
- * @param initialCrop If not null, specifies a rectangle which contains an initial
- * crop to continue. This can be used to crop an image where you already
- * know about margins in the image
- * @return a cropped version of the source image, or null if the whole image was blank
- * and cropping completely removed everything
- */
- @Nullable
- public static BufferedImage cropColor(
- @NonNull BufferedImage image,
- final int blankArgb,
- @Nullable Rect initialCrop) {
- return cropColor(image, blankArgb, initialCrop, image.getType());
- }
-
- /**
- * Crops pixels of a given color from the edges of the image and returns the cropped
- * result.
- *
- * @param image the image to be cropped
- * @param blankArgb the color considered to be blank, as a 32 pixel integer with 8
- * bits of alpha, red, green and blue
- * @param initialCrop If not null, specifies a rectangle which contains an initial
- * crop to continue. This can be used to crop an image where you already
- * know about margins in the image
- * @param imageType the type of {@link BufferedImage} to create
- * @return a cropped version of the source image, or null if the whole image was blank
- * and cropping completely removed everything
- */
- public static BufferedImage cropColor(BufferedImage image,
- final int blankArgb, Rect initialCrop, int imageType) {
- CropFilter filter = new CropFilter() {
- @Override
- public boolean crop(BufferedImage bufferedImage, int x, int y) {
- return blankArgb == bufferedImage.getRGB(x, y);
- }
- };
- return crop(image, filter, initialCrop, imageType);
- }
-
- /**
- * Interface implemented by cropping functions that determine whether
- * a pixel should be cropped or not.
- */
- private static interface CropFilter {
- /**
- * Returns true if the pixel is should be cropped.
- *
- * @param image the image containing the pixel in question
- * @param x the x position of the pixel
- * @param y the y position of the pixel
- * @return true if the pixel should be cropped (for example, is blank)
- */
- boolean crop(BufferedImage image, int x, int y);
- }
-
- private static BufferedImage crop(BufferedImage image, CropFilter filter, Rect initialCrop,
- int imageType) {
- if (image == null) {
- return null;
- }
-
- // First, determine the dimensions of the real image within the image
- int x1, y1, x2, y2;
- if (initialCrop != null) {
- x1 = initialCrop.x;
- y1 = initialCrop.y;
- x2 = initialCrop.x + initialCrop.w;
- y2 = initialCrop.y + initialCrop.h;
- } else {
- x1 = 0;
- y1 = 0;
- x2 = image.getWidth();
- y2 = image.getHeight();
- }
-
- // Nothing left to crop
- if (x1 == x2 || y1 == y2) {
- return null;
- }
-
- // This algorithm is a bit dumb -- it just scans along the edges looking for
- // a pixel that shouldn't be cropped. I could maybe try to make it smarter by
- // for example doing a binary search to quickly eliminate large empty areas to
- // the right and bottom -- but this is slightly tricky with components like the
- // AnalogClock where I could accidentally end up finding a blank horizontal or
- // vertical line somewhere in the middle of the rendering of the clock, so for now
- // we do the dumb thing -- not a big deal since we tend to crop reasonably
- // small images.
-
- // First determine top edge
- topEdge: for (; y1 < y2; y1++) {
- for (int x = x1; x < x2; x++) {
- if (!filter.crop(image, x, y1)) {
- break topEdge;
- }
- }
- }
-
- if (y1 == image.getHeight()) {
- // The image is blank
- return null;
- }
-
- // Next determine left edge
- leftEdge: for (; x1 < x2; x1++) {
- for (int y = y1; y < y2; y++) {
- if (!filter.crop(image, x1, y)) {
- break leftEdge;
- }
- }
- }
-
- // Next determine right edge
- rightEdge: for (; x2 > x1; x2--) {
- for (int y = y1; y < y2; y++) {
- if (!filter.crop(image, x2 - 1, y)) {
- break rightEdge;
- }
- }
- }
-
- // Finally determine bottom edge
- bottomEdge: for (; y2 > y1; y2--) {
- for (int x = x1; x < x2; x++) {
- if (!filter.crop(image, x, y2 - 1)) {
- break bottomEdge;
- }
- }
- }
-
- // No need to crop?
- if (x1 == 0 && y1 == 0 && x2 == image.getWidth() && y2 == image.getHeight()) {
- return image;
- }
-
- if (x1 == x2 || y1 == y2) {
- // Nothing left after crop -- blank image
- return null;
- }
-
- int width = x2 - x1;
- int height = y2 - y1;
-
- // Now extract the sub-image
- if (imageType == -1) {
- imageType = image.getType();
- }
- if (imageType == BufferedImage.TYPE_CUSTOM) {
- imageType = BufferedImage.TYPE_INT_ARGB;
- }
- BufferedImage cropped = new BufferedImage(width, height, imageType);
- Graphics g = cropped.getGraphics();
- g.drawImage(image, 0, 0, width, height, x1, y1, x2, y2, null);
-
- g.dispose();
-
- return cropped;
- }
-
- /**
- * Creates a drop shadow of a given image and returns a new image which shows the
- * input image on top of its drop shadow.
- * <p>
- * <b>NOTE: If the shape is rectangular and opaque, consider using
- * {@link #drawRectangleShadow(Graphics, int, int, int, int)} instead.</b>
- *
- * @param source the source image to be shadowed
- * @param shadowSize the size of the shadow in pixels
- * @param shadowOpacity the opacity of the shadow, with 0=transparent and 1=opaque
- * @param shadowRgb the RGB int to use for the shadow color
- * @return a new image with the source image on top of its shadow
- */
- public static BufferedImage createDropShadow(BufferedImage source, int shadowSize,
- float shadowOpacity, int shadowRgb) {
-
- // This code is based on
- // http://www.jroller.com/gfx/entry/non_rectangular_shadow
-
- BufferedImage image = new BufferedImage(source.getWidth() + shadowSize * 2,
- source.getHeight() + shadowSize * 2,
- BufferedImage.TYPE_INT_ARGB);
-
- Graphics2D g2 = image.createGraphics();
- g2.drawImage(source, null, shadowSize, shadowSize);
-
- int dstWidth = image.getWidth();
- int dstHeight = image.getHeight();
-
- int left = (shadowSize - 1) >> 1;
- int right = shadowSize - left;
- int xStart = left;
- int xStop = dstWidth - right;
- int yStart = left;
- int yStop = dstHeight - right;
-
- shadowRgb = shadowRgb & 0x00FFFFFF;
-
- int[] aHistory = new int[shadowSize];
- int historyIdx = 0;
-
- int aSum;
-
- int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
- int lastPixelOffset = right * dstWidth;
- float sumDivider = shadowOpacity / shadowSize;
-
- // horizontal pass
- for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
- aSum = 0;
- historyIdx = 0;
- for (int x = 0; x < shadowSize; x++, bufferOffset++) {
- int a = dataBuffer[bufferOffset] >>> 24;
- aHistory[x] = a;
- aSum += a;
- }
-
- bufferOffset -= right;
-
- for (int x = xStart; x < xStop; x++, bufferOffset++) {
- int a = (int) (aSum * sumDivider);
- dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
- // subtract the oldest pixel from the sum
- aSum -= aHistory[historyIdx];
-
- // get the latest pixel
- a = dataBuffer[bufferOffset + right] >>> 24;
- aHistory[historyIdx] = a;
- aSum += a;
-
- if (++historyIdx >= shadowSize) {
- historyIdx -= shadowSize;
- }
- }
- }
- // vertical pass
- for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
- aSum = 0;
- historyIdx = 0;
- for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
- int a = dataBuffer[bufferOffset] >>> 24;
- aHistory[y] = a;
- aSum += a;
- }
-
- bufferOffset -= lastPixelOffset;
-
- for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
- int a = (int) (aSum * sumDivider);
- dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
- // subtract the oldest pixel from the sum
- aSum -= aHistory[historyIdx];
-
- // get the latest pixel
- a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
- aHistory[historyIdx] = a;
- aSum += a;
-
- if (++historyIdx >= shadowSize) {
- historyIdx -= shadowSize;
- }
- }
- }
-
- g2.drawImage(source, null, 0, 0);
- g2.dispose();
-
- return image;
- }
-
- /**
- * Draws a rectangular drop shadow (of size {@link #SHADOW_SIZE} by
- * {@link #SHADOW_SIZE} around the given source and returns a new image with
- * both combined
- *
- * @param source the source image
- * @return the source image with a drop shadow on the bottom and right
- */
- public static BufferedImage createRectangularDropShadow(BufferedImage source) {
- int type = source.getType();
- if (type == BufferedImage.TYPE_CUSTOM) {
- type = BufferedImage.TYPE_INT_ARGB;
- }
-
- int width = source.getWidth();
- int height = source.getHeight();
- BufferedImage image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE, type);
- Graphics g = image.getGraphics();
- g.drawImage(source, 0, 0, width, height, null);
- ImageUtils.drawRectangleShadow(image, 0, 0, width, height);
- g.dispose();
-
- return image;
- }
-
- /**
- * Draws a drop shadow for the given rectangle into the given context. It
- * will not draw anything if the rectangle is smaller than a minimum
- * determined by the assets used to draw the shadow graphics.
- * The size of the shadow is {@link #SHADOW_SIZE}.
- *
- * @param image the image to draw the shadow into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static final void drawRectangleShadow(BufferedImage image,
- int x, int y, int width, int height) {
- Graphics gc = image.getGraphics();
- try {
- drawRectangleShadow(gc, x, y, width, height);
- } finally {
- gc.dispose();
- }
- }
-
- /**
- * Draws a small drop shadow for the given rectangle into the given context. It
- * will not draw anything if the rectangle is smaller than a minimum
- * determined by the assets used to draw the shadow graphics.
- * The size of the shadow is {@link #SMALL_SHADOW_SIZE}.
- *
- * @param image the image to draw the shadow into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static final void drawSmallRectangleShadow(BufferedImage image,
- int x, int y, int width, int height) {
- Graphics gc = image.getGraphics();
- try {
- drawSmallRectangleShadow(gc, x, y, width, height);
- } finally {
- gc.dispose();
- }
- }
-
- /**
- * The width and height of the drop shadow painted by
- * {@link #drawRectangleShadow(Graphics, int, int, int, int)}
- */
- public static final int SHADOW_SIZE = 20; // DO NOT EDIT. This corresponds to bitmap graphics
-
- /**
- * The width and height of the drop shadow painted by
- * {@link #drawSmallRectangleShadow(Graphics, int, int, int, int)}
- */
- public static final int SMALL_SHADOW_SIZE = 10; // DO NOT EDIT. Corresponds to bitmap graphics
-
- /**
- * Draws a drop shadow for the given rectangle into the given context. It
- * will not draw anything if the rectangle is smaller than a minimum
- * determined by the assets used to draw the shadow graphics.
- * <p>
- * This corresponds to
- * {@link SwtUtils#drawRectangleShadow(org.eclipse.swt.graphics.GC, int, int, int, int)},
- * but applied to an AWT graphics object instead, such that no image
- * conversion has to be performed.
- * <p>
- * Make sure to keep changes in the visual appearance here in sync with the
- * AWT version in
- * {@link SwtUtils#drawRectangleShadow(org.eclipse.swt.graphics.GC, int, int, int, int)}.
- *
- * @param gc the graphics context to draw into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static final void drawRectangleShadow(Graphics gc,
- int x, int y, int width, int height) {
- if (sShadowBottomLeft == null) {
- // Shadow graphics. This was generated by creating a drop shadow in
- // Gimp, using the parameters x offset=10, y offset=10, blur radius=10,
- // color=black, and opacity=51. These values attempt to make a shadow
- // that is legible both for dark and light themes, on top of the
- // canvas background (rgb(150,150,150). Darker shadows would tend to
- // blend into the foreground for a dark holo screen, and lighter shadows
- // would be hard to spot on the canvas background. If you make adjustments,
- // make sure to check the shadow with both dark and light themes.
- //
- // After making the graphics, I cut out the top right, bottom left
- // and bottom right corners as 20x20 images, and these are reproduced by
- // painting them in the corresponding places in the target graphics context.
- // I then grabbed a single horizontal gradient line from the middle of the
- // right edge,and a single vertical gradient line from the bottom. These
- // are then painted scaled/stretched in the target to fill the gaps between
- // the three corner images.
- //
- // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
- sShadowBottomLeft = readImage("shadow-bl.png"); //$NON-NLS-1$
- sShadowBottom = readImage("shadow-b.png"); //$NON-NLS-1$
- sShadowBottomRight = readImage("shadow-br.png"); //$NON-NLS-1$
- sShadowRight = readImage("shadow-r.png"); //$NON-NLS-1$
- sShadowTopRight = readImage("shadow-tr.png"); //$NON-NLS-1$
- assert sShadowBottomLeft != null;
- assert sShadowBottomRight.getWidth() == SHADOW_SIZE;
- assert sShadowBottomRight.getHeight() == SHADOW_SIZE;
- }
-
- int blWidth = sShadowBottomLeft.getWidth();
- int trHeight = sShadowTopRight.getHeight();
- if (width < blWidth) {
- return;
- }
- if (height < trHeight) {
- return;
- }
-
- gc.drawImage(sShadowBottomLeft, x, y + height, null);
- gc.drawImage(sShadowBottomRight, x + width, y + height, null);
- gc.drawImage(sShadowTopRight, x + width, y, null);
- gc.drawImage(sShadowBottom,
- x + sShadowBottomLeft.getWidth(), y + height,
- x + width, y + height + sShadowBottom.getHeight(),
- 0, 0, sShadowBottom.getWidth(), sShadowBottom.getHeight(),
- null);
- gc.drawImage(sShadowRight,
- x + width, y + sShadowTopRight.getHeight(),
- x + width + sShadowRight.getWidth(), y + height,
- 0, 0, sShadowRight.getWidth(), sShadowRight.getHeight(),
- null);
- }
-
- /**
- * Draws a small drop shadow for the given rectangle into the given context. It
- * will not draw anything if the rectangle is smaller than a minimum
- * determined by the assets used to draw the shadow graphics.
- * <p>
- *
- * @param gc the graphics context to draw into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static final void drawSmallRectangleShadow(Graphics gc,
- int x, int y, int width, int height) {
- if (sShadow2BottomLeft == null) {
- // Shadow graphics. This was generated by creating a drop shadow in
- // Gimp, using the parameters x offset=5, y offset=%, blur radius=5,
- // color=black, and opacity=51. These values attempt to make a shadow
- // that is legible both for dark and light themes, on top of the
- // canvas background (rgb(150,150,150). Darker shadows would tend to
- // blend into the foreground for a dark holo screen, and lighter shadows
- // would be hard to spot on the canvas background. If you make adjustments,
- // make sure to check the shadow with both dark and light themes.
- //
- // After making the graphics, I cut out the top right, bottom left
- // and bottom right corners as 20x20 images, and these are reproduced by
- // painting them in the corresponding places in the target graphics context.
- // I then grabbed a single horizontal gradient line from the middle of the
- // right edge,and a single vertical gradient line from the bottom. These
- // are then painted scaled/stretched in the target to fill the gaps between
- // the three corner images.
- //
- // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
- sShadow2BottomLeft = readImage("shadow2-bl.png"); //$NON-NLS-1$
- sShadow2Bottom = readImage("shadow2-b.png"); //$NON-NLS-1$
- sShadow2BottomRight = readImage("shadow2-br.png"); //$NON-NLS-1$
- sShadow2Right = readImage("shadow2-r.png"); //$NON-NLS-1$
- sShadow2TopRight = readImage("shadow2-tr.png"); //$NON-NLS-1$
- assert sShadow2BottomLeft != null;
- assert sShadow2TopRight != null;
- assert sShadow2BottomRight.getWidth() == SMALL_SHADOW_SIZE;
- assert sShadow2BottomRight.getHeight() == SMALL_SHADOW_SIZE;
- }
-
- int blWidth = sShadow2BottomLeft.getWidth();
- int trHeight = sShadow2TopRight.getHeight();
- if (width < blWidth) {
- return;
- }
- if (height < trHeight) {
- return;
- }
-
- gc.drawImage(sShadow2BottomLeft, x, y + height, null);
- gc.drawImage(sShadow2BottomRight, x + width, y + height, null);
- gc.drawImage(sShadow2TopRight, x + width, y, null);
- gc.drawImage(sShadow2Bottom,
- x + sShadow2BottomLeft.getWidth(), y + height,
- x + width, y + height + sShadow2Bottom.getHeight(),
- 0, 0, sShadow2Bottom.getWidth(), sShadow2Bottom.getHeight(),
- null);
- gc.drawImage(sShadow2Right,
- x + width, y + sShadow2TopRight.getHeight(),
- x + width + sShadow2Right.getWidth(), y + height,
- 0, 0, sShadow2Right.getWidth(), sShadow2Right.getHeight(),
- null);
- }
-
- /**
- * Reads the given image from the plugin folder
- *
- * @param name the name of the image (including file extension)
- * @return the corresponding image, or null if something goes wrong
- */
- @Nullable
- public static BufferedImage readImage(@NonNull String name) {
- InputStream stream = ImageUtils.class.getResourceAsStream("/icons/" + name); //$NON-NLS-1$
- if (stream != null) {
- try {
- return ImageIO.read(stream);
- } catch (IOException e) {
- AdtPlugin.log(e, "Could not read %1$s", name);
- } finally {
- try {
- stream.close();
- } catch (IOException e) {
- // Dumb API
- }
- }
- }
-
- return null;
- }
-
- // Normal drop shadow
- private static BufferedImage sShadowBottomLeft;
- private static BufferedImage sShadowBottom;
- private static BufferedImage sShadowBottomRight;
- private static BufferedImage sShadowRight;
- private static BufferedImage sShadowTopRight;
-
- // Small drop shadow
- private static BufferedImage sShadow2BottomLeft;
- private static BufferedImage sShadow2Bottom;
- private static BufferedImage sShadow2BottomRight;
- private static BufferedImage sShadow2Right;
- private static BufferedImage sShadow2TopRight;
-
- /**
- * Returns a bounding rectangle for the given list of rectangles. If the list is
- * empty, the bounding rectangle is null.
- *
- * @param items the list of rectangles to compute a bounding rectangle for (may not be
- * null)
- * @return a bounding rectangle of the passed in rectangles, or null if the list is
- * empty
- */
- public static Rectangle getBoundingRectangle(List<Rectangle> items) {
- Iterator<Rectangle> iterator = items.iterator();
- if (!iterator.hasNext()) {
- return null;
- }
-
- Rectangle bounds = iterator.next();
- Rectangle union = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
- while (iterator.hasNext()) {
- union.add(iterator.next());
- }
-
- return union;
- }
-
- /**
- * Returns a new image which contains of the sub image given by the rectangle (x1,y1)
- * to (x2,y2)
- *
- * @param source the source image
- * @param x1 top left X coordinate
- * @param y1 top left Y coordinate
- * @param x2 bottom right X coordinate
- * @param y2 bottom right Y coordinate
- * @return a new image containing the pixels in the given range
- */
- public static BufferedImage subImage(BufferedImage source, int x1, int y1, int x2, int y2) {
- int width = x2 - x1;
- int height = y2 - y1;
- int imageType = source.getType();
- if (imageType == BufferedImage.TYPE_CUSTOM) {
- imageType = BufferedImage.TYPE_INT_ARGB;
- }
- BufferedImage sub = new BufferedImage(width, height, imageType);
- Graphics g = sub.getGraphics();
- g.drawImage(source, 0, 0, width, height, x1, y1, x2, y2, null);
- g.dispose();
-
- return sub;
- }
-
- /**
- * Returns the color value represented by the given string value
- * @param value the color value
- * @return the color as an int
- * @throw NumberFormatException if the conversion failed.
- */
- public static int getColor(String value) {
- // Copied from ResourceHelper in layoutlib
- if (value != null) {
- if (value.startsWith("#") == false) { //$NON-NLS-1$
- throw new NumberFormatException(
- String.format("Color value '%s' must start with #", value));
- }
-
- value = value.substring(1);
-
- // make sure it's not longer than 32bit
- if (value.length() > 8) {
- throw new NumberFormatException(String.format(
- "Color value '%s' is too long. Format is either" +
- "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
- value));
- }
-
- if (value.length() == 3) { // RGB format
- char[] color = new char[8];
- color[0] = color[1] = 'F';
- color[2] = color[3] = value.charAt(0);
- color[4] = color[5] = value.charAt(1);
- color[6] = color[7] = value.charAt(2);
- value = new String(color);
- } else if (value.length() == 4) { // ARGB format
- char[] color = new char[8];
- color[0] = color[1] = value.charAt(0);
- color[2] = color[3] = value.charAt(1);
- color[4] = color[5] = value.charAt(2);
- color[6] = color[7] = value.charAt(3);
- value = new String(color);
- } else if (value.length() == 6) {
- value = "FF" + value; //$NON-NLS-1$
- }
-
- // this is a RRGGBB or AARRGGBB value
-
- // Integer.parseInt will fail to parse strings like "ff191919", so we use
- // a Long, but cast the result back into an int, since we know that we're only
- // dealing with 32 bit values.
- return (int)Long.parseLong(value, 16);
- }
-
- throw new NumberFormatException();
- }
-
- /**
- * Resize the given image
- *
- * @param source the image to be scaled
- * @param xScale x scale
- * @param yScale y scale
- * @return the scaled image
- */
- public static BufferedImage scale(BufferedImage source, double xScale, double yScale) {
- return scale(source, xScale, yScale, 0, 0);
- }
-
- /**
- * Resize the given image
- *
- * @param source the image to be scaled
- * @param xScale x scale
- * @param yScale y scale
- * @param rightMargin extra margin to add on the right
- * @param bottomMargin extra margin to add on the bottom
- * @return the scaled image
- */
- public static BufferedImage scale(BufferedImage source, double xScale, double yScale,
- int rightMargin, int bottomMargin) {
- int sourceWidth = source.getWidth();
- int sourceHeight = source.getHeight();
- int destWidth = Math.max(1, (int) (xScale * sourceWidth));
- int destHeight = Math.max(1, (int) (yScale * sourceHeight));
- int imageType = source.getType();
- if (imageType == BufferedImage.TYPE_CUSTOM) {
- imageType = BufferedImage.TYPE_INT_ARGB;
- }
- if (xScale > 0.5 && yScale > 0.5) {
- BufferedImage scaled =
- new BufferedImage(destWidth + rightMargin, destHeight + bottomMargin, imageType);
- Graphics2D g2 = scaled.createGraphics();
- g2.setComposite(AlphaComposite.Src);
- g2.setColor(new Color(0, true));
- g2.fillRect(0, 0, destWidth + rightMargin, destHeight + bottomMargin);
- g2.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR);
- g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
- g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
- g2.drawImage(source, 0, 0, destWidth, destHeight, 0, 0, sourceWidth, sourceHeight,
- null);
- g2.dispose();
- return scaled;
- } else {
- // When creating a thumbnail, using the above code doesn't work very well;
- // you get some visible artifacts, especially for text. Instead use the
- // technique of repeatedly scaling the image into half; this will cause
- // proper averaging of neighboring pixels, and will typically (for the kinds
- // of screen sizes used by this utility method in the layout editor) take
- // about 3-4 iterations to get the result since we are logarithmically reducing
- // the size. Besides, each successive pass in operating on much fewer pixels
- // (a reduction of 4 in each pass).
- //
- // However, we may not be resizing to a size that can be reached exactly by
- // successively diving in half. Therefore, once we're within a factor of 2 of
- // the final size, we can do a resize to the exact target size.
- // However, we can get even better results if we perform this final resize
- // up front. Let's say we're going from width 1000 to a destination width of 85.
- // The first approach would cause a resize from 1000 to 500 to 250 to 125, and
- // then a resize from 125 to 85. That last resize can distort/blur a lot.
- // Instead, we can start with the destination width, 85, and double it
- // successfully until we're close to the initial size: 85, then 170,
- // then 340, and finally 680. (The next one, 1360, is larger than 1000).
- // So, now we *start* the thumbnail operation by resizing from width 1000 to
- // width 680, which will preserve a lot of visual details such as text.
- // Then we can successively resize the image in half, 680 to 340 to 170 to 85.
- // We end up with the expected final size, but we've been doing an exact
- // divide-in-half resizing operation at the end so there is less distortion.
-
-
- int iterations = 0; // Number of halving operations to perform after the initial resize
- int nearestWidth = destWidth; // Width closest to source width that = 2^x, x is integer
- int nearestHeight = destHeight;
- while (nearestWidth < sourceWidth / 2) {
- nearestWidth *= 2;
- nearestHeight *= 2;
- iterations++;
- }
-
- // If we're supposed to add in margins, we need to do it in the initial resizing
- // operation if we don't have any subsequent resizing operations.
- if (iterations == 0) {
- nearestWidth += rightMargin;
- nearestHeight += bottomMargin;
- }
-
- BufferedImage scaled = new BufferedImage(nearestWidth, nearestHeight, imageType);
- Graphics2D g2 = scaled.createGraphics();
- g2.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR);
- g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
- g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
- g2.drawImage(source, 0, 0, nearestWidth, nearestHeight,
- 0, 0, sourceWidth, sourceHeight, null);
- g2.dispose();
-
- sourceWidth = nearestWidth;
- sourceHeight = nearestHeight;
- source = scaled;
-
- for (int iteration = iterations - 1; iteration >= 0; iteration--) {
- int halfWidth = sourceWidth / 2;
- int halfHeight = sourceHeight / 2;
- if (iteration == 0) { // Last iteration: Add margins in final image
- scaled = new BufferedImage(halfWidth + rightMargin, halfHeight + bottomMargin,
- imageType);
- } else {
- scaled = new BufferedImage(halfWidth, halfHeight, imageType);
- }
- g2 = scaled.createGraphics();
- g2.setRenderingHint(KEY_INTERPOLATION,VALUE_INTERPOLATION_BILINEAR);
- g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
- g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
- g2.drawImage(source, 0, 0,
- halfWidth, halfHeight, 0, 0,
- sourceWidth, sourceHeight,
- null);
- g2.dispose();
-
- sourceWidth = halfWidth;
- sourceHeight = halfHeight;
- source = scaled;
- iterations--;
- }
- return scaled;
- }
- }
-
- /**
- * Returns true if the given file path points to an image file recognized by
- * Android. See http://developer.android.com/guide/appendix/media-formats.html
- * for details.
- *
- * @param path the filename to be tested
- * @return true if the file represents an image file
- */
- public static boolean hasImageExtension(String path) {
- return endsWithIgnoreCase(path, DOT_PNG)
- || endsWithIgnoreCase(path, DOT_9PNG)
- || endsWithIgnoreCase(path, DOT_GIF)
- || endsWithIgnoreCase(path, DOT_JPG)
- || endsWithIgnoreCase(path, DOT_BMP);
- }
-
- /**
- * Creates a new image of the given size filled with the given color
- *
- * @param width the width of the image
- * @param height the height of the image
- * @param color the color of the image
- * @return a new image of the given size filled with the given color
- */
- public static BufferedImage createColoredImage(int width, int height, RGB color) {
- BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = image.getGraphics();
- g.setColor(new Color(color.red, color.green, color.blue));
- g.fillRect(0, 0, image.getWidth(), image.getHeight());
- g.dispose();
- return image;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java
deleted file mode 100644
index 7bab914e5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinder.java
+++ /dev/null
@@ -1,1111 +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.ATTR_LAYOUT;
-import static com.android.SdkConstants.EXT_XML;
-import static com.android.SdkConstants.FD_RESOURCES;
-import static com.android.SdkConstants.FD_RES_LAYOUT;
-import static com.android.SdkConstants.TOOLS_URI;
-import static com.android.SdkConstants.VIEW_FRAGMENT;
-import static com.android.SdkConstants.VIEW_INCLUDE;
-import static com.android.ide.eclipse.adt.AdtConstants.WS_LAYOUTS;
-import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
-import static com.android.resources.ResourceType.LAYOUT;
-import static org.eclipse.core.resources.IResourceDelta.ADDED;
-import static org.eclipse.core.resources.IResourceDelta.CHANGED;
-import static org.eclipse.core.resources.IResourceDelta.CONTENT;
-import static org.eclipse.core.resources.IResourceDelta.REMOVED;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
-import com.android.ide.common.resources.ResourceFile;
-import com.android.ide.common.resources.ResourceFolder;
-import com.android.ide.common.resources.ResourceItem;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager.IResourceListener;
-import com.android.ide.eclipse.adt.io.IFileWrapper;
-import com.android.io.IAbstractFile;
-import com.android.resources.ResourceType;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The include finder finds other XML files that are including a given XML file, and does
- * so efficiently (caching results across IDE sessions etc).
- */
-@SuppressWarnings("restriction") // XML model
-public class IncludeFinder {
- /** Qualified name for the per-project persistent property include-map */
- private final static QualifiedName CONFIG_INCLUDES = new QualifiedName(AdtPlugin.PLUGIN_ID,
- "includes");//$NON-NLS-1$
-
- /**
- * Qualified name for the per-project non-persistent property storing the
- * {@link IncludeFinder} for this project
- */
- private final static QualifiedName INCLUDE_FINDER = new QualifiedName(AdtPlugin.PLUGIN_ID,
- "includefinder"); //$NON-NLS-1$
-
- /** Project that the include finder locates includes for */
- private final IProject mProject;
-
- /** Map from a layout resource name to a set of layouts included by the given resource */
- private Map<String, List<String>> mIncludes = null;
-
- /**
- * Reverse map of {@link #mIncludes}; points to other layouts that are including a
- * given layouts
- */
- private Map<String, List<String>> mIncludedBy = null;
-
- /** Flag set during a refresh; ignore updates when this is true */
- private static boolean sRefreshing;
-
- /** Global (cross-project) resource listener */
- private static ResourceListener sListener;
-
- /**
- * Constructs an {@link IncludeFinder} for the given project. Don't use this method;
- * use the {@link #get} factory method instead.
- *
- * @param project project to create an {@link IncludeFinder} for
- */
- private IncludeFinder(IProject project) {
- mProject = project;
- }
-
- /**
- * Returns the {@link IncludeFinder} for the given project
- *
- * @param project the project the finder is associated with
- * @return an {@link IncludeFinder} for the given project, never null
- */
- @NonNull
- public static IncludeFinder get(IProject project) {
- IncludeFinder finder = null;
- try {
- finder = (IncludeFinder) project.getSessionProperty(INCLUDE_FINDER);
- } catch (CoreException e) {
- // Not a problem; we will just create a new one
- }
-
- if (finder == null) {
- finder = new IncludeFinder(project);
- try {
- project.setSessionProperty(INCLUDE_FINDER, finder);
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't store IncludeFinder");
- }
- }
-
- return finder;
- }
-
- /**
- * Returns a list of resource names that are included by the given resource
- *
- * @param includer the resource name to return included layouts for
- * @return the layouts included by the given resource
- */
- private List<String> getIncludesFrom(String includer) {
- ensureInitialized();
-
- return mIncludes.get(includer);
- }
-
- /**
- * Gets the list of all other layouts that are including the given layout.
- *
- * @param included the file that is included
- * @return the files that are including the given file, or null or empty
- */
- @Nullable
- public List<Reference> getIncludedBy(IResource included) {
- ensureInitialized();
- String mapKey = getMapKey(included);
- List<String> result = mIncludedBy.get(mapKey);
- if (result == null) {
- String name = getResourceName(included);
- if (!name.equals(mapKey)) {
- result = mIncludedBy.get(name);
- }
- }
-
- if (result != null && result.size() > 0) {
- List<Reference> references = new ArrayList<Reference>(result.size());
- for (String s : result) {
- references.add(new Reference(mProject, s));
- }
- return references;
- } else {
- return null;
- }
- }
-
- /**
- * Returns true if the given resource is included from some other layout in the
- * project
- *
- * @param included the resource to check
- * @return true if the file is included by some other layout
- */
- public boolean isIncluded(IResource included) {
- ensureInitialized();
- String mapKey = getMapKey(included);
- List<String> result = mIncludedBy.get(mapKey);
- if (result == null) {
- String name = getResourceName(included);
- if (!name.equals(mapKey)) {
- result = mIncludedBy.get(name);
- }
- }
-
- return result != null && result.size() > 0;
- }
-
- @VisibleForTesting
- /* package */ List<String> getIncludedBy(String included) {
- ensureInitialized();
- return mIncludedBy.get(included);
- }
-
- /** Initialize the inclusion data structures, if not already done */
- private void ensureInitialized() {
- if (mIncludes == null) {
- // Initialize
- if (!readSettings()) {
- // Couldn't read settings: probably the first time this code is running
- // so there is no known data about includes.
-
- // Yes, these should be multimaps! If we start using Guava replace
- // these with multimaps.
- mIncludes = new HashMap<String, List<String>>();
- mIncludedBy = new HashMap<String, List<String>>();
-
- scanProject();
- saveSettings();
- }
- }
- }
-
- // ----- Persistence -----
-
- /**
- * Create a String serialization of the includes map. The map attempts to be compact;
- * it strips out the @layout/ prefix, and eliminates the values for empty string
- * values. The map can be restored by calling {@link #decodeMap}. The encoded String
- * will have sorted keys.
- *
- * @param map the map to be serialized
- * @return a serialization (never null) of the given map
- */
- @VisibleForTesting
- public static String encodeMap(Map<String, List<String>> map) {
- StringBuilder sb = new StringBuilder();
-
- if (map != null) {
- // Process the keys in sorted order rather than just
- // iterating over the entry set to ensure stable output
- List<String> keys = new ArrayList<String>(map.keySet());
- Collections.sort(keys);
- for (String key : keys) {
- List<String> values = map.get(key);
-
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append(key);
- if (values.size() > 0) {
- sb.append('=').append('>');
- sb.append('{');
- boolean first = true;
- for (String value : values) {
- if (first) {
- first = false;
- } else {
- sb.append(',');
- }
- sb.append(value);
- }
- sb.append('}');
- }
- }
- }
-
- return sb.toString();
- }
-
- /**
- * Decodes the encoding (produced by {@link #encodeMap}) back into the original map,
- * modulo any key sorting differences.
- *
- * @param encoded an encoding of a map created by {@link #encodeMap}
- * @return a map corresponding to the encoded values, never null
- */
- @VisibleForTesting
- public static Map<String, List<String>> decodeMap(String encoded) {
- HashMap<String, List<String>> map = new HashMap<String, List<String>>();
-
- if (encoded.length() > 0) {
- int i = 0;
- int end = encoded.length();
-
- while (i < end) {
-
- // Find key range
- int keyBegin = i;
- int keyEnd = i;
- while (i < end) {
- char c = encoded.charAt(i);
- if (c == ',') {
- break;
- } else if (c == '=') {
- i += 2; // Skip =>
- break;
- }
- i++;
- keyEnd = i;
- }
-
- List<String> values = new ArrayList<String>();
- // Find values
- if (i < end && encoded.charAt(i) == '{') {
- i++;
- while (i < end) {
- int valueBegin = i;
- int valueEnd = i;
- char c = 0;
- while (i < end) {
- c = encoded.charAt(i);
- if (c == ',' || c == '}') {
- valueEnd = i;
- break;
- }
- i++;
- }
- if (valueEnd > valueBegin) {
- values.add(encoded.substring(valueBegin, valueEnd));
- }
-
- if (c == '}') {
- if (i < end-1 && encoded.charAt(i+1) == ',') {
- i++;
- }
- break;
- }
- assert c == ',';
- i++;
- }
- }
-
- String key = encoded.substring(keyBegin, keyEnd);
- map.put(key, values);
- i++;
- }
- }
-
- return map;
- }
-
- /**
- * Stores the settings in the persistent project storage.
- */
- private void saveSettings() {
- // Serialize the mIncludes map into a compact String. The mIncludedBy map can be
- // inferred from it.
- String encoded = encodeMap(mIncludes);
-
- try {
- if (encoded.length() >= 2048) {
- // The maximum length of a setting key is 2KB, according to the javadoc
- // for the project class. It's unlikely that we'll
- // hit this -- even with an average layout root name of 20 characters
- // we can still store over a hundred names. But JUST IN CASE we run
- // into this, we'll clear out the key in this name which means that the
- // information will need to be recomputed in the next IDE session.
- mProject.setPersistentProperty(CONFIG_INCLUDES, null);
- } else {
- String existing = mProject.getPersistentProperty(CONFIG_INCLUDES);
- if (!encoded.equals(existing)) {
- mProject.setPersistentProperty(CONFIG_INCLUDES, encoded);
- }
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't store include settings");
- }
- }
-
- /**
- * Reads previously stored settings from the persistent project storage
- *
- * @return true iff settings were restored from the project
- */
- private boolean readSettings() {
- try {
- String encoded = mProject.getPersistentProperty(CONFIG_INCLUDES);
- if (encoded != null) {
- mIncludes = decodeMap(encoded);
-
- // Set up a reverse map, pointing from included files to the files that
- // included them
- mIncludedBy = new HashMap<String, List<String>>(2 * mIncludes.size());
- for (Map.Entry<String, List<String>> entry : mIncludes.entrySet()) {
- // File containing the <include>
- String includer = entry.getKey();
- // Files being <include>'ed by the above file
- List<String> included = entry.getValue();
- setIncludedBy(includer, included);
- }
-
- return true;
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't read include settings");
- }
-
- return false;
- }
-
- // ----- File scanning -----
-
- /**
- * Scan the whole project for XML layout resources that are performing includes.
- */
- private void scanProject() {
- ProjectResources resources = ResourceManager.getInstance().getProjectResources(mProject);
- if (resources != null) {
- Collection<ResourceItem> layouts = resources.getResourceItemsOfType(LAYOUT);
- for (ResourceItem layout : layouts) {
- List<ResourceFile> sources = layout.getSourceFileList();
- for (ResourceFile source : sources) {
- updateFileIncludes(source, false);
- }
- }
-
- return;
- }
- }
-
- /**
- * Scans the given {@link ResourceFile} and if it is a layout resource, updates the
- * includes in it.
- *
- * @param resourceFile the {@link ResourceFile} to be scanned for includes (doesn't
- * have to be only layout XML files; this method will filter the type)
- * @param singleUpdate true if this is a single file being updated, false otherwise
- * (e.g. during initial project scanning)
- * @return true if we updated the includes for the resource file
- */
- private boolean updateFileIncludes(ResourceFile resourceFile, boolean singleUpdate) {
- Collection<ResourceType> resourceTypes = resourceFile.getResourceTypes();
- for (ResourceType type : resourceTypes) {
- if (type == ResourceType.LAYOUT) {
- ensureInitialized();
-
- List<String> includes = Collections.emptyList();
- if (resourceFile.getFile() instanceof IFileWrapper) {
- IFile file = ((IFileWrapper) resourceFile.getFile()).getIFile();
-
- // See if we have an existing XML model for this file; if so, we can
- // just look directly at the parse tree
- boolean hadXmlModel = false;
- IStructuredModel model = null;
- try {
- IModelManager modelManager = StructuredModelManager.getModelManager();
- model = modelManager.getExistingModelForRead(file);
- if (model instanceof IDOMModel) {
- IDOMModel domModel = (IDOMModel) model;
- Document document = domModel.getDocument();
- includes = findIncludesInDocument(document);
- hadXmlModel = true;
- }
- } finally {
- if (model != null) {
- model.releaseFromRead();
- }
- }
-
- // If no XML model we have to read the XML contents and (possibly) parse it.
- // The actual file may not exist anymore (e.g. when deleting a layout file
- // or when the workspace is out of sync.)
- if (!hadXmlModel) {
- String xml = AdtPlugin.readFile(file);
- if (xml != null) {
- includes = findIncludes(xml);
- }
- }
- } else {
- String xml = AdtPlugin.readFile(resourceFile);
- if (xml != null) {
- includes = findIncludes(xml);
- }
- }
-
- String key = getMapKey(resourceFile);
- if (includes.equals(getIncludesFrom(key))) {
- // Common case -- so avoid doing settings flush etc
- return false;
- }
-
- boolean detectCycles = singleUpdate;
- setIncluded(key, includes, detectCycles);
-
- if (singleUpdate) {
- saveSettings();
- }
-
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Finds the list of includes in the given XML content. It attempts quickly return
- * empty if the file does not include any include tags; it does this by only parsing
- * if it detects the string &lt;include in the file.
- */
- @VisibleForTesting
- @NonNull
- static List<String> findIncludes(@NonNull String xml) {
- int index = xml.indexOf(ATTR_LAYOUT);
- if (index != -1) {
- return findIncludesInXml(xml);
- }
-
- return Collections.emptyList();
- }
-
- /**
- * Parses the given XML content and extracts all the included URLs and returns them
- *
- * @param xml layout XML content to be parsed for includes
- * @return a list of included urls, or null
- */
- @VisibleForTesting
- @NonNull
- static List<String> findIncludesInXml(@NonNull String xml) {
- Document document = DomUtilities.parseDocument(xml, false /*logParserErrors*/);
- if (document != null) {
- return findIncludesInDocument(document);
- }
-
- return Collections.emptyList();
- }
-
- /** Searches the given DOM document and returns the list of includes, if any */
- @NonNull
- private static List<String> findIncludesInDocument(@NonNull Document document) {
- List<String> includes = findIncludesInDocument(document, null);
- if (includes == null) {
- includes = Collections.emptyList();
- }
- return includes;
- }
-
- @Nullable
- private static List<String> findIncludesInDocument(@NonNull Node node,
- @Nullable List<String> urls) {
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- String tag = node.getNodeName();
- boolean isInclude = tag.equals(VIEW_INCLUDE);
- boolean isFragment = tag.equals(VIEW_FRAGMENT);
- if (isInclude || isFragment) {
- Element element = (Element) node;
- String url;
- if (isInclude) {
- url = element.getAttribute(ATTR_LAYOUT);
- } else {
- url = element.getAttributeNS(TOOLS_URI, ATTR_LAYOUT);
- }
- if (url.length() > 0) {
- String resourceName = urlToLocalResource(url);
- if (resourceName != null) {
- if (urls == null) {
- urls = new ArrayList<String>();
- }
- urls.add(resourceName);
- }
- }
-
- }
- }
-
- NodeList children = node.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- urls = findIncludesInDocument(children.item(i), urls);
- }
-
- return urls;
- }
-
-
- /**
- * Returns the layout URL to a local resource name (provided the URL is a local
- * resource, not something in @android etc.) Returns null otherwise.
- */
- private static String urlToLocalResource(String url) {
- if (!url.startsWith("@")) { //$NON-NLS-1$
- return null;
- }
- int typeEnd = url.indexOf('/', 1);
- if (typeEnd == -1) {
- return null;
- }
- int nameBegin = typeEnd + 1;
- int typeBegin = 1;
- int colon = url.lastIndexOf(':', typeEnd);
- if (colon != -1) {
- String packageName = url.substring(typeBegin, colon);
- if ("android".equals(packageName)) { //$NON-NLS-1$
- // Don't want to point to non-local resources
- return null;
- }
-
- typeBegin = colon + 1;
- assert "layout".equals(url.substring(typeBegin, typeEnd)); //$NON-NLS-1$
- }
-
- return url.substring(nameBegin);
- }
-
- /**
- * Record the list of included layouts from the given layout
- *
- * @param includer the layout including other layouts
- * @param included the layouts that were included by the including layout
- * @param detectCycles if true, check for cycles and report them as project errors
- */
- @VisibleForTesting
- /* package */ void setIncluded(String includer, List<String> included, boolean detectCycles) {
- // Remove previously linked inverse mappings
- List<String> oldIncludes = mIncludes.get(includer);
- if (oldIncludes != null && oldIncludes.size() > 0) {
- for (String includee : oldIncludes) {
- List<String> includers = mIncludedBy.get(includee);
- if (includers != null) {
- includers.remove(includer);
- }
- }
- }
-
- mIncludes.put(includer, included);
- // Reverse mapping: for included items, point back to including file
- setIncludedBy(includer, included);
-
- if (detectCycles) {
- detectCycles(includer);
- }
- }
-
- /** Record the list of included layouts from the given layout */
- private void setIncludedBy(String includer, List<String> included) {
- for (String target : included) {
- List<String> list = mIncludedBy.get(target);
- if (list == null) {
- list = new ArrayList<String>(2); // We don't expect many includes
- mIncludedBy.put(target, list);
- }
- if (!list.contains(includer)) {
- list.add(includer);
- }
- }
- }
-
- /** Start listening on project resources */
- public static void start() {
- assert sListener == null;
- sListener = new ResourceListener();
- ResourceManager.getInstance().addListener(sListener);
- }
-
- /** Stop listening on project resources */
- public static void stop() {
- assert sListener != null;
- ResourceManager.getInstance().addListener(sListener);
- }
-
- private static String getMapKey(ResourceFile resourceFile) {
- IAbstractFile file = resourceFile.getFile();
- String name = file.getName();
- String folderName = file.getParentFolder().getName();
- return getMapKey(folderName, name);
- }
-
- private static String getMapKey(IResource resourceFile) {
- String folderName = resourceFile.getParent().getName();
- String name = resourceFile.getName();
- return getMapKey(folderName, name);
- }
-
- private static String getResourceName(IResource resourceFile) {
- String name = resourceFile.getName();
- int baseEnd = name.length() - EXT_XML.length() - 1; // -1: the dot
- if (baseEnd > 0) {
- name = name.substring(0, baseEnd);
- }
-
- return name;
- }
-
- private static String getMapKey(String folderName, String name) {
- int baseEnd = name.length() - EXT_XML.length() - 1; // -1: the dot
- if (baseEnd > 0) {
- name = name.substring(0, baseEnd);
- }
-
- // Create a map key for the given resource file
- // This will map
- // /res/layout/foo.xml => "foo"
- // /res/layout-land/foo.xml => "-land/foo"
-
- if (FD_RES_LAYOUT.equals(folderName)) {
- // Normal case -- keep just the basename
- return name;
- } else {
- // Store the relative path from res/ on down, so
- // /res/layout-land/foo.xml becomes "layout-land/foo"
- //if (folderName.startsWith(FD_LAYOUT)) {
- // folderName = folderName.substring(FD_LAYOUT.length());
- //}
-
- return folderName + WS_SEP + name;
- }
- }
-
- /** Listener of resource file saves, used to update layout inclusion data structures */
- private static class ResourceListener implements IResourceListener {
- @Override
- public void fileChanged(IProject project, ResourceFile file, int eventType) {
- if (sRefreshing) {
- return;
- }
-
- if ((eventType & (CHANGED | ADDED | REMOVED | CONTENT)) == 0) {
- return;
- }
-
- IncludeFinder finder = get(project);
- if (finder != null) {
- if (finder.updateFileIncludes(file, true)) {
- finder.saveSettings();
- }
- }
- }
-
- @Override
- public void folderChanged(IProject project, ResourceFolder folder, int eventType) {
- // We only care about layout resource files
- }
- }
-
- // ----- Cycle detection -----
-
- private void detectCycles(String from) {
- // Perform DFS on the include graph and look for a cycle; if we find one, produce
- // a chain of includes on the way back to show to the user
- if (mIncludes.size() > 0) {
- Set<String> visiting = new HashSet<String>(mIncludes.size());
- String chain = dfs(from, visiting);
- if (chain != null) {
- addError(from, chain);
- } else {
- // Is there an existing error for us to clean up?
- removeErrors(from);
- }
- }
- }
-
- /** Format to chain include cycles in: a=>b=>c=>d etc */
- private final String CHAIN_FORMAT = "%1$s=>%2$s"; //$NON-NLS-1$
-
- private String dfs(String from, Set<String> visiting) {
- visiting.add(from);
-
- List<String> includes = mIncludes.get(from);
- if (includes != null && includes.size() > 0) {
- for (String include : includes) {
- if (visiting.contains(include)) {
- return String.format(CHAIN_FORMAT, from, include);
- }
- String chain = dfs(include, visiting);
- if (chain != null) {
- return String.format(CHAIN_FORMAT, from, chain);
- }
- }
- }
-
- visiting.remove(from);
-
- return null;
- }
-
- private void removeErrors(String from) {
- final IResource resource = findResource(from);
- if (resource != null) {
- try {
- final String markerId = IMarker.PROBLEM;
-
- IMarker[] markers = resource.findMarkers(markerId, true, IResource.DEPTH_ZERO);
-
- for (final IMarker marker : markers) {
- String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
- if (tmpMsg == null || tmpMsg.startsWith(MESSAGE)) {
- // Remove
- runLater(new Runnable() {
- @Override
- public void run() {
- try {
- sRefreshing = true;
- marker.delete();
- } catch (CoreException e) {
- AdtPlugin.log(e, "Can't delete problem marker");
- } finally {
- sRefreshing = false;
- }
- }
- });
- }
- }
- } catch (CoreException e) {
- // if we couldn't get the markers, then we just mark the file again
- // (since markerAlreadyExists is initialized to false, we do nothing)
- }
- }
- }
-
- /** Error message for cycles */
- private static final String MESSAGE = "Found cyclical <include> chain";
-
- private void addError(String from, String chain) {
- final IResource resource = findResource(from);
- if (resource != null) {
- final String markerId = IMarker.PROBLEM;
- final String message = String.format("%1$s: %2$s", MESSAGE, chain);
- final int lineNumber = 1;
- final int severity = IMarker.SEVERITY_ERROR;
-
- // check if there's a similar marker already, since aapt is launched twice
- boolean markerAlreadyExists = false;
- try {
- IMarker[] markers = resource.findMarkers(markerId, true, IResource.DEPTH_ZERO);
-
- for (IMarker marker : markers) {
- int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
- if (tmpLine != lineNumber) {
- break;
- }
-
- int tmpSeverity = marker.getAttribute(IMarker.SEVERITY, -1);
- if (tmpSeverity != severity) {
- break;
- }
-
- String tmpMsg = marker.getAttribute(IMarker.MESSAGE, null);
- if (tmpMsg == null || tmpMsg.equals(message) == false) {
- break;
- }
-
- // if we're here, all the marker attributes are equals, we found it
- // and exit
- markerAlreadyExists = true;
- break;
- }
-
- } catch (CoreException e) {
- // if we couldn't get the markers, then we just mark the file again
- // (since markerAlreadyExists is initialized to false, we do nothing)
- }
-
- if (!markerAlreadyExists) {
- runLater(new Runnable() {
- @Override
- public void run() {
- try {
- sRefreshing = true;
-
- // Adding a resource will force a refresh on the file;
- // ignore these updates
- BaseProjectHelper.markResource(resource, markerId, message, lineNumber,
- severity);
- } finally {
- sRefreshing = false;
- }
- }
- });
- }
- }
- }
-
- // FIXME: Find more standard Eclipse way to do this.
- // We need to run marker registration/deletion "later", because when the include
- // scanning is running it's in the middle of resource notification, so the IDE
- // throws an exception
- private static void runLater(Runnable runnable) {
- Display display = Display.findDisplay(Thread.currentThread());
- if (display != null) {
- display.asyncExec(runnable);
- } else {
- AdtPlugin.log(IStatus.WARNING, "Could not find display");
- }
- }
-
- /**
- * Finds the project resource for the given layout path
- *
- * @param from the resource name
- * @return the {@link IResource}, or null if not found
- */
- private IResource findResource(String from) {
- final IResource resource = mProject.findMember(WS_LAYOUTS + WS_SEP + from + '.' + EXT_XML);
- return resource;
- }
-
- /**
- * Creates a blank, project-less {@link IncludeFinder} <b>for use by unit tests
- * only</b>
- */
- @VisibleForTesting
- /* package */ static IncludeFinder create() {
- IncludeFinder finder = new IncludeFinder(null);
- finder.mIncludes = new HashMap<String, List<String>>();
- finder.mIncludedBy = new HashMap<String, List<String>>();
- return finder;
- }
-
- /** A reference to a particular file in the project */
- public static class Reference {
- /** The unique id referencing the file, such as (for res/layout-land/main.xml)
- * "layout-land/main") */
- private final String mId;
-
- /** The project containing the file */
- private final IProject mProject;
-
- /** The resource name of the file, such as (for res/layout/main.xml) "main" */
- private String mName;
-
- /** Creates a new include reference */
- private Reference(IProject project, String id) {
- super();
- mProject = project;
- mId = id;
- }
-
- /**
- * Returns the id identifying the given file within the project
- *
- * @return the id identifying the given file within the project
- */
- public String getId() {
- return mId;
- }
-
- /**
- * Returns the {@link IFile} in the project for the given file. May return null if
- * there is an error in locating the file or if the file no longer exists.
- *
- * @return the project file, or null
- */
- public IFile getFile() {
- String reference = mId;
- if (!reference.contains(WS_SEP)) {
- reference = FD_RES_LAYOUT + WS_SEP + reference;
- }
-
- String projectPath = FD_RESOURCES + WS_SEP + reference + '.' + EXT_XML;
- IResource member = mProject.findMember(projectPath);
- if (member instanceof IFile) {
- return (IFile) member;
- }
-
- return null;
- }
-
- /**
- * Returns a description of this reference, suitable to be shown to the user
- *
- * @return a display name for the reference
- */
- public String getDisplayName() {
- // The ID is deliberately kept in a pretty user-readable format but we could
- // consider prepending layout/ on ids that don't have it (to make the display
- // more uniform) or ripping out all layout[-constraint] prefixes out and
- // instead prepending @ etc.
- return mId;
- }
-
- /**
- * Returns the name of the reference, suitable for resource lookup. For example,
- * for "res/layout/main.xml", as well as for "res/layout-land/main.xml", this
- * would be "main".
- *
- * @return the resource name of the reference
- */
- public String getName() {
- if (mName == null) {
- mName = mId;
- int index = mName.lastIndexOf(WS_SEP);
- if (index != -1) {
- mName = mName.substring(index + 1);
- }
- }
-
- return mName;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mId == null) ? 0 : mId.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Reference other = (Reference) obj;
- if (mId == null) {
- if (other.mId != null)
- return false;
- } else if (!mId.equals(other.mId))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "Reference [getId()=" + getId() //$NON-NLS-1$
- + ", getDisplayName()=" + getDisplayName() //$NON-NLS-1$
- + ", getName()=" + getName() //$NON-NLS-1$
- + ", getFile()=" + getFile() + "]"; //$NON-NLS-1$
- }
-
- /**
- * Creates a reference to the given file
- *
- * @param file the file to create a reference for
- * @return a reference to the given file
- */
- public static Reference create(IFile file) {
- return new Reference(file.getProject(), getMapKey(file));
- }
-
- /**
- * Returns the resource name of this layout, such as {@code @layout/foo}.
- *
- * @return the resource name
- */
- public String getResourceName() {
- return '@' + FD_RES_LAYOUT + '/' + getName();
- }
- }
-
- /**
- * Returns a collection of layouts (expressed as resource names, such as
- * {@code @layout/foo} which would be invalid includes in the given layout
- * (because it would introduce a cycle)
- *
- * @param layout the layout file to check for cyclic dependencies from
- * @return a collection of layout resources which cannot be included from
- * the given layout, never null
- */
- public Collection<String> getInvalidIncludes(IFile layout) {
- IProject project = layout.getProject();
- Reference self = Reference.create(layout);
-
- // Add anyone who transitively can reach this file via includes.
- LinkedList<Reference> queue = new LinkedList<Reference>();
- List<Reference> invalid = new ArrayList<Reference>();
- queue.add(self);
- invalid.add(self);
- Set<String> seen = new HashSet<String>();
- seen.add(self.getId());
- while (!queue.isEmpty()) {
- Reference reference = queue.removeFirst();
- String refId = reference.getId();
-
- // Look up both configuration specific includes as well as includes in the
- // base versions
- List<String> included = getIncludedBy(refId);
- if (refId.indexOf('/') != -1) {
- List<String> baseIncluded = getIncludedBy(reference.getName());
- if (included == null) {
- included = baseIncluded;
- } else if (baseIncluded != null) {
- included = new ArrayList<String>(included);
- included.addAll(baseIncluded);
- }
- }
-
- if (included != null && included.size() > 0) {
- for (String id : included) {
- if (!seen.contains(id)) {
- seen.add(id);
- Reference ref = new Reference(project, id);
- invalid.add(ref);
- queue.addLast(ref);
- }
- }
- }
- }
-
- List<String> result = new ArrayList<String>();
- for (Reference reference : invalid) {
- result.add(reference.getResourceName());
- }
-
- return result;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlay.java
deleted file mode 100644
index 81c03edd5..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlay.java
+++ /dev/null
@@ -1,150 +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 com.android.annotations.VisibleForTesting;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Rectangle;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * The {@link IncludeOverlay} class renders masks to -partially- hide everything outside
- * an included file's own content. This overlay is in use when you are editing an included
- * file shown within a different file's context (e.g. "Show In > other").
- */
-public class IncludeOverlay extends Overlay {
- /** Mask transparency - 0 is transparent, 255 is opaque */
- private static final int MASK_TRANSPARENCY = 160;
-
- /** The associated {@link LayoutCanvas}. */
- private LayoutCanvas mCanvas;
-
- /**
- * Constructs an {@link IncludeOverlay} tied to the given canvas.
- *
- * @param canvas The {@link LayoutCanvas} to paint the overlay over.
- */
- public IncludeOverlay(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- @Override
- public void paint(GC gc) {
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- List<Rectangle> includedBounds = viewHierarchy.getIncludedBounds();
- if (includedBounds == null || includedBounds.size() == 0) {
- // We don't support multiple included children yet. When that works,
- // this code should use a BSP tree to figure out which regions to paint
- // to leave holes in the mask.
- return;
- }
-
- Image image = mCanvas.getImageOverlay().getImage();
- if (image == null) {
- return;
- }
-
- int oldAlpha = gc.getAlpha();
- gc.setAlpha(MASK_TRANSPARENCY);
- Color bg = gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
- gc.setBackground(bg);
-
- CanvasViewInfo root = viewHierarchy.getRoot();
- Rectangle whole = root.getAbsRect();
- whole = new Rectangle(whole.x, whole.y, whole.width + 1, whole.height + 1);
- Collection<Rectangle> masks = subtractRectangles(whole, includedBounds);
-
- for (Rectangle mask : masks) {
- ControlPoint topLeft = LayoutPoint.create(mCanvas, mask.x, mask.y).toControl();
- ControlPoint bottomRight = LayoutPoint.create(mCanvas, mask.x + mask.width,
- mask.y + mask.height).toControl();
- int x1 = topLeft.x;
- int y1 = topLeft.y;
- int x2 = bottomRight.x;
- int y2 = bottomRight.y;
-
- gc.fillRectangle(x1, y1, x2 - x1, y2 - y1);
- }
-
- gc.setAlpha(oldAlpha);
- }
-
- /**
- * Given a Rectangle, remove holes from it (specified as a collection of Rectangles) such
- * that the result is a list of rectangles that cover everything that is not a hole.
- *
- * @param rectangle the rectangle to subtract from
- * @param holes the holes to subtract from the rectangle
- * @return a list of sub rectangles that remain after subtracting out the given list of holes
- */
- @VisibleForTesting
- static Collection<Rectangle> subtractRectangles(
- Rectangle rectangle, Collection<Rectangle> holes) {
- List<Rectangle> result = new ArrayList<Rectangle>();
- result.add(rectangle);
-
- for (Rectangle hole : holes) {
- List<Rectangle> tempResult = new ArrayList<Rectangle>();
- for (Rectangle r : result) {
- if (hole.intersects(r)) {
- // Clip the hole to fit the rectangle bounds
- Rectangle h = hole.intersection(r);
-
- // Split the rectangle
-
- // Above (includes the NW and NE corners)
- if (h.y > r.y) {
- tempResult.add(new Rectangle(r.x, r.y, r.width, h.y - r.y));
- }
-
- // Left (not including corners)
- if (h.x > r.x) {
- tempResult.add(new Rectangle(r.x, h.y, h.x - r.x, h.height));
- }
-
- int hx2 = h.x + h.width;
- int hy2 = h.y + h.height;
- int rx2 = r.x + r.width;
- int ry2 = r.y + r.height;
-
- // Below (includes the SW and SE corners)
- if (hy2 < ry2) {
- tempResult.add(new Rectangle(r.x, hy2, r.width, ry2 - hy2));
- }
-
- // Right (not including corners)
- if (hx2 < rx2) {
- tempResult.add(new Rectangle(hx2, h.y, rx2 - hx2, h.height));
- }
- } else {
- tempResult.add(r);
- }
- }
-
- result = tempResult;
- }
-
- return result;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
deleted file mode 100644
index 1b1bd23c4..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutActionBar.java
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * Copyright (C) 2011 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_ID;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
-import com.android.ide.common.api.RuleAction.Separator;
-import com.android.ide.common.api.RuleAction.Toggle;
-import com.android.ide.common.layout.BaseViewRule;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.sdklib.devices.Device;
-import com.android.sdklib.devices.Screen;
-import com.android.sdkuilib.internal.widgets.ResolutionChooserDialog;
-import com.google.common.base.Strings;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.MenuItem;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.eclipse.ui.ISharedImages;
-import org.eclipse.ui.PlatformUI;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Toolbar shown at the top of the layout editor, which adds a number of context-sensitive
- * layout actions (as well as zooming controls on the right).
- */
-public class LayoutActionBar extends Composite {
- private GraphicalEditorPart mEditor;
- private ToolBar mLayoutToolBar;
- private ToolBar mLintToolBar;
- private ToolBar mZoomToolBar;
- private ToolItem mZoomRealSizeButton;
- private ToolItem mZoomOutButton;
- private ToolItem mZoomResetButton;
- private ToolItem mZoomInButton;
- private ToolItem mZoomFitButton;
- private ToolItem mLintButton;
- private List<RuleAction> mPrevActions;
-
- /**
- * Creates a new {@link LayoutActionBar} and adds it to the given parent.
- *
- * @param parent the parent composite to add the actions bar to
- * @param style the SWT style to apply
- * @param editor the associated layout editor
- */
- public LayoutActionBar(Composite parent, int style, GraphicalEditorPart editor) {
- super(parent, style | SWT.NO_FOCUS);
- mEditor = editor;
-
- GridLayout layout = new GridLayout(3, false);
- setLayout(layout);
-
- mLayoutToolBar = new ToolBar(this, /*SWT.WRAP |*/ SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL);
- mLayoutToolBar.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
- mZoomToolBar = createZoomControls();
- mZoomToolBar.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
- mLintToolBar = createLintControls();
-
- GridData lintData = new GridData(SWT.END, SWT.BEGINNING, false, false);
- lintData.exclude = true;
- mLintToolBar.setLayoutData(lintData);
- }
-
- @Override
- public void dispose() {
- super.dispose();
- mPrevActions = null;
- }
-
- /** Updates the layout contents based on the current selection */
- void updateSelection() {
- NodeProxy parent = null;
- LayoutCanvas canvas = mEditor.getCanvasControl();
- SelectionManager selectionManager = canvas.getSelectionManager();
- List<SelectionItem> selections = selectionManager.getSelections();
- if (selections.size() > 0) {
- // TODO: better handle multi-selection -- maybe we should disable it or
- // something.
- // What if you select children with different parents? Of different types?
- // etc.
- NodeProxy node = selections.get(0).getNode();
- if (node != null && node.getParent() != null) {
- parent = (NodeProxy) node.getParent();
- }
- }
-
- if (parent == null) {
- // Show the background's properties
- CanvasViewInfo root = canvas.getViewHierarchy().getRoot();
- if (root == null) {
- return;
- }
- parent = canvas.getNodeFactory().create(root);
- selections = Collections.emptyList();
- }
-
- RulesEngine engine = mEditor.getRulesEngine();
- List<NodeProxy> selectedNodes = new ArrayList<NodeProxy>();
- for (SelectionItem item : selections) {
- selectedNodes.add(item.getNode());
- }
- List<RuleAction> actions = new ArrayList<RuleAction>();
- engine.callAddLayoutActions(actions, parent, selectedNodes);
-
- // Place actions in the correct order (the actions may come from different
- // rules and should be merged properly via sorting keys)
- Collections.sort(actions);
-
- // Add in actions for the child as well, if there is exactly one.
- // These are not merged into the parent list of actions; they are appended
- // at the end.
- int index = -1;
- String label = null;
- if (selectedNodes.size() == 1) {
- List<RuleAction> itemActions = new ArrayList<RuleAction>();
- NodeProxy selectedNode = selectedNodes.get(0);
- engine.callAddLayoutActions(itemActions, selectedNode, null);
- if (itemActions.size() > 0) {
- Collections.sort(itemActions);
-
- if (!(itemActions.get(0) instanceof RuleAction.Separator)) {
- actions.add(RuleAction.createSeparator(0));
- }
- label = selectedNode.getStringAttr(ANDROID_URI, ATTR_ID);
- if (label != null) {
- label = BaseViewRule.stripIdPrefix(label);
- index = actions.size();
- }
- actions.addAll(itemActions);
- }
- }
-
- if (!updateActions(actions)) {
- updateToolbar(actions, index, label);
- }
- mPrevActions = actions;
- }
-
- /** Update the toolbar widgets */
- private void updateToolbar(final List<RuleAction> actions, final int labelIndex,
- final String label) {
- if (mLayoutToolBar == null || mLayoutToolBar.isDisposed()) {
- return;
- }
- for (ToolItem c : mLayoutToolBar.getItems()) {
- c.dispose();
- }
- mLayoutToolBar.pack();
- addActions(actions, labelIndex, label);
- mLayoutToolBar.pack();
- mLayoutToolBar.layout();
- }
-
- /**
- * Attempts to update the existing toolbar actions, if the action list is
- * similar to the current list. Returns false if this cannot be done and the
- * contents must be replaced.
- */
- private boolean updateActions(@NonNull List<RuleAction> actions) {
- List<RuleAction> before = mPrevActions;
- List<RuleAction> after = actions;
-
- if (before == null) {
- return false;
- }
-
- if (!before.equals(after) || after.size() > mLayoutToolBar.getItemCount()) {
- return false;
- }
-
- int actionIndex = 0;
- for (int i = 0, max = mLayoutToolBar.getItemCount(); i < max; i++) {
- ToolItem item = mLayoutToolBar.getItem(i);
- int style = item.getStyle();
- Object data = item.getData();
- if (data != null) {
- // One action can result in multiple toolbar items (e.g. a choice action
- // can result in multiple radio buttons), so we've have to replace all of
- // them with the corresponding new action
- RuleAction prevAction = before.get(actionIndex);
- while (prevAction != data) {
- actionIndex++;
- if (actionIndex == before.size()) {
- return false;
- }
- prevAction = before.get(actionIndex);
- if (prevAction == data) {
- break;
- } else if (!(prevAction instanceof RuleAction.Separator)) {
- return false;
- }
- }
- RuleAction newAction = after.get(actionIndex);
- assert newAction.equals(prevAction); // Maybe I can do this lazily instead?
-
- // Update action binding to the new action
- item.setData(newAction);
-
- // Sync button states: the checked state is not considered part of
- // RuleAction equality
- if ((style & SWT.CHECK) != 0) {
- assert newAction instanceof Toggle;
- Toggle toggle = (Toggle) newAction;
- item.setSelection(toggle.isChecked());
- } else if ((style & SWT.RADIO) != 0) {
- assert newAction instanceof Choices;
- Choices choices = (Choices) newAction;
- String current = choices.getCurrent();
- String id = (String) item.getData(ATTR_ID);
- boolean selected = Strings.nullToEmpty(current).equals(id);
- item.setSelection(selected);
- }
- } else {
- // Must be a separator, or a label (which we insert for nested widgets)
- assert (style & SWT.SEPARATOR) != 0 || !item.getText().isEmpty() : item;
- }
- }
-
- return true;
- }
-
- private void addActions(List<RuleAction> actions, int labelIndex, String label) {
- if (actions.size() > 0) {
- // Flag used to indicate that if there are any actions -after- this, it
- // should be separated from this current action (we don't unconditionally
- // add a separator at the end of these groups in case there are no more
- // actions at the end so that we don't have a trailing separator)
- boolean needSeparator = false;
-
- int index = 0;
- for (RuleAction action : actions) {
- if (index == labelIndex) {
- final ToolItem button = new ToolItem(mLayoutToolBar, SWT.PUSH);
- button.setText(label);
- needSeparator = false;
- }
- index++;
-
- if (action instanceof Separator) {
- addSeparator(mLayoutToolBar);
- needSeparator = false;
- continue;
- } else if (needSeparator) {
- addSeparator(mLayoutToolBar);
- needSeparator = false;
- }
-
- if (action instanceof RuleAction.Choices) {
- RuleAction.Choices choices = (Choices) action;
- if (!choices.isRadio()) {
- addDropdown(choices);
- } else {
- addSeparator(mLayoutToolBar);
- addRadio(choices);
- needSeparator = true;
- }
- } else if (action instanceof RuleAction.Toggle) {
- addToggle((Toggle) action);
- } else {
- addPlainAction(action);
- }
- }
- }
- }
-
- /** Add a separator to the toolbar, unless there already is one there at the end already */
- private static void addSeparator(ToolBar toolBar) {
- int n = toolBar.getItemCount();
- if (n > 0 && (toolBar.getItem(n - 1).getStyle() & SWT.SEPARATOR) == 0) {
- ToolItem separator = new ToolItem(toolBar, SWT.SEPARATOR);
- separator.setWidth(15);
- }
- }
-
- private void addToggle(Toggle toggle) {
- final ToolItem button = new ToolItem(mLayoutToolBar, SWT.CHECK);
-
- URL iconUrl = toggle.getIconUrl();
- String title = toggle.getTitle();
- if (iconUrl != null) {
- button.setImage(IconFactory.getInstance().getIcon(iconUrl));
- button.setToolTipText(title);
- } else {
- button.setText(title);
- }
- button.setData(toggle);
-
- button.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- Toggle toggle = (Toggle) button.getData();
- toggle.getCallback().action(toggle, getSelectedNodes(),
- toggle.getId(), button.getSelection());
- updateSelection();
- }
- });
- if (toggle.isChecked()) {
- button.setSelection(true);
- }
- }
-
- private List<INode> getSelectedNodes() {
- List<SelectionItem> selections =
- mEditor.getCanvasControl().getSelectionManager().getSelections();
- List<INode> nodes = new ArrayList<INode>(selections.size());
- for (SelectionItem item : selections) {
- nodes.add(item.getNode());
- }
-
- return nodes;
- }
-
-
- private void addPlainAction(RuleAction menuAction) {
- final ToolItem button = new ToolItem(mLayoutToolBar, SWT.PUSH);
-
- URL iconUrl = menuAction.getIconUrl();
- String title = menuAction.getTitle();
- if (iconUrl != null) {
- button.setImage(IconFactory.getInstance().getIcon(iconUrl));
- button.setToolTipText(title);
- } else {
- button.setText(title);
- }
- button.setData(menuAction);
-
- button.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- RuleAction menuAction = (RuleAction) button.getData();
- menuAction.getCallback().action(menuAction, getSelectedNodes(), menuAction.getId(),
- false);
- updateSelection();
- }
- });
- }
-
- private void addRadio(RuleAction.Choices choices) {
- List<URL> icons = choices.getIconUrls();
- List<String> titles = choices.getTitles();
- List<String> ids = choices.getIds();
- String current = choices.getCurrent() != null ? choices.getCurrent() : ""; //$NON-NLS-1$
-
- assert icons != null;
- assert icons.size() == titles.size();
-
- for (int i = 0; i < icons.size(); i++) {
- URL iconUrl = icons.get(i);
- String title = titles.get(i);
- final String id = ids.get(i);
- final ToolItem item = new ToolItem(mLayoutToolBar, SWT.RADIO);
- item.setToolTipText(title);
- item.setImage(IconFactory.getInstance().getIcon(iconUrl));
- item.setData(choices);
- item.setData(ATTR_ID, id);
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- if (item.getSelection()) {
- RuleAction.Choices choices = (Choices) item.getData();
- choices.getCallback().action(choices, getSelectedNodes(), id, null);
- updateSelection();
- }
- }
- });
- boolean selected = current.equals(id);
- if (selected) {
- item.setSelection(true);
- }
- }
- }
-
- private void addDropdown(RuleAction.Choices choices) {
- final ToolItem combo = new ToolItem(mLayoutToolBar, SWT.DROP_DOWN);
- URL iconUrl = choices.getIconUrl();
- if (iconUrl != null) {
- combo.setImage(IconFactory.getInstance().getIcon(iconUrl));
- combo.setToolTipText(choices.getTitle());
- } else {
- combo.setText(choices.getTitle());
- }
- combo.setData(choices);
-
- Listener menuListener = new Listener() {
- @Override
- public void handleEvent(Event event) {
- Menu menu = new Menu(mLayoutToolBar.getShell(), SWT.POP_UP);
- RuleAction.Choices choices = (Choices) combo.getData();
- List<URL> icons = choices.getIconUrls();
- List<String> titles = choices.getTitles();
- List<String> ids = choices.getIds();
- String current = choices.getCurrent() != null ? choices.getCurrent() : ""; //$NON-NLS-1$
-
- for (int i = 0; i < titles.size(); i++) {
- String title = titles.get(i);
- final String id = ids.get(i);
- URL itemIconUrl = icons != null && icons.size() > 0 ? icons.get(i) : null;
- MenuItem item = new MenuItem(menu, SWT.CHECK);
- item.setText(title);
- if (itemIconUrl != null) {
- Image itemIcon = IconFactory.getInstance().getIcon(itemIconUrl);
- item.setImage(itemIcon);
- }
-
- boolean selected = id.equals(current);
- if (selected) {
- item.setSelection(true);
- }
-
- item.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- RuleAction.Choices choices = (Choices) combo.getData();
- choices.getCallback().action(choices, getSelectedNodes(), id, null);
- updateSelection();
- }
- });
- }
-
- Rectangle bounds = combo.getBounds();
- Point location = new Point(bounds.x, bounds.y + bounds.height);
- location = combo.getParent().toDisplay(location);
- menu.setLocation(location.x, location.y);
- menu.setVisible(true);
- }
- };
- combo.addListener(SWT.Selection, menuListener);
- }
-
- // ---- Zoom Controls ----
-
- @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused
- private ToolBar createZoomControls() {
- ToolBar toolBar = new ToolBar(this, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL);
-
- IconFactory iconFactory = IconFactory.getInstance();
- mZoomRealSizeButton = new ToolItem(toolBar, SWT.CHECK);
- mZoomRealSizeButton.setToolTipText("Emulate Real Size");
- mZoomRealSizeButton.setImage(iconFactory.getIcon("zoomreal")); //$NON-NLS-1$);
- mZoomRealSizeButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- boolean newState = mZoomRealSizeButton.getSelection();
- if (rescaleToReal(newState)) {
- mZoomOutButton.setEnabled(!newState);
- mZoomResetButton.setEnabled(!newState);
- mZoomInButton.setEnabled(!newState);
- mZoomFitButton.setEnabled(!newState);
- } else {
- mZoomRealSizeButton.setSelection(!newState);
- }
- }
- });
-
- mZoomFitButton = new ToolItem(toolBar, SWT.PUSH);
- mZoomFitButton.setToolTipText("Zoom to Fit (0)");
- mZoomFitButton.setImage(iconFactory.getIcon("zoomfit")); //$NON-NLS-1$);
- mZoomFitButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- rescaleToFit(true);
- }
- });
-
- mZoomResetButton = new ToolItem(toolBar, SWT.PUSH);
- mZoomResetButton.setToolTipText("Reset Zoom to 100% (1)");
- mZoomResetButton.setImage(iconFactory.getIcon("zoom100")); //$NON-NLS-1$);
- mZoomResetButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- resetScale();
- }
- });
-
- // Group zoom in/out separately
- new ToolItem(toolBar, SWT.SEPARATOR);
-
- mZoomOutButton = new ToolItem(toolBar, SWT.PUSH);
- mZoomOutButton.setToolTipText("Zoom Out (-)");
- mZoomOutButton.setImage(iconFactory.getIcon("zoomminus")); //$NON-NLS-1$);
- mZoomOutButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- rescale(-1);
- }
- });
-
- mZoomInButton = new ToolItem(toolBar, SWT.PUSH);
- mZoomInButton.setToolTipText("Zoom In (+)");
- mZoomInButton.setImage(iconFactory.getIcon("zoomplus")); //$NON-NLS-1$);
- mZoomInButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- rescale(+1);
- }
- });
-
- return toolBar;
- }
-
- @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused
- private ToolBar createLintControls() {
- ToolBar toolBar = new ToolBar(this, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL);
-
- // Separate from adjacent toolbar
- new ToolItem(toolBar, SWT.SEPARATOR);
-
- ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
- mLintButton = new ToolItem(toolBar, SWT.PUSH);
- mLintButton.setToolTipText("Show Lint Warnings for this Layout");
- mLintButton.setImage(sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK));
- mLintButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- CommonXmlEditor editor = mEditor.getEditorDelegate().getEditor();
- IFile file = editor.getInputFile();
- if (file != null) {
- EclipseLintClient.showErrors(getShell(), file, editor);
- }
- }
- });
-
- return toolBar;
- }
-
- /**
- * Updates the lint indicator state in the given layout editor
- */
- public void updateErrorIndicator() {
- updateErrorIndicator(mEditor.getEditedFile());
- }
-
- /**
- * Updates the lint indicator state for the given file
- *
- * @param file the file to show the indicator status for
- */
- public void updateErrorIndicator(IFile file) {
- IMarker[] markers = EclipseLintClient.getMarkers(file);
- updateErrorIndicator(markers.length);
- }
-
- /**
- * Sets whether the action bar should show the "lint warnings" button
- *
- * @param hasLintWarnings whether there are lint errors to be shown
- */
- private void updateErrorIndicator(final int markerCount) {
- Display display = getDisplay();
- if (display.getThread() != Thread.currentThread()) {
- display.asyncExec(new Runnable() {
- @Override
- public void run() {
- if (!isDisposed()) {
- updateErrorIndicator(markerCount);
- }
- }
- });
- return;
- }
-
- GridData layoutData = (GridData) mLintToolBar.getLayoutData();
- Integer existing = (Integer) mLintToolBar.getData();
- Integer current = Integer.valueOf(markerCount);
- if (!current.equals(existing)) {
- mLintToolBar.setData(current);
- boolean layout = false;
- boolean hasLintWarnings = markerCount > 0 && AdtPrefs.getPrefs().isLintOnSave();
- if (layoutData.exclude == hasLintWarnings) {
- layoutData.exclude = !hasLintWarnings;
- mLintToolBar.setVisible(hasLintWarnings);
- layout = true;
- }
- if (markerCount > 0) {
- String iconName = "";
- switch (markerCount) {
- case 1: iconName = "lint1"; break; //$NON-NLS-1$
- case 2: iconName = "lint2"; break; //$NON-NLS-1$
- case 3: iconName = "lint3"; break; //$NON-NLS-1$
- case 4: iconName = "lint4"; break; //$NON-NLS-1$
- case 5: iconName = "lint5"; break; //$NON-NLS-1$
- case 6: iconName = "lint6"; break; //$NON-NLS-1$
- case 7: iconName = "lint7"; break; //$NON-NLS-1$
- case 8: iconName = "lint8"; break; //$NON-NLS-1$
- case 9: iconName = "lint9"; break; //$NON-NLS-1$
- default: iconName = "lint9p"; break;//$NON-NLS-1$
- }
- mLintButton.setImage(IconFactory.getInstance().getIcon(iconName));
- }
- if (layout) {
- layout();
- }
- redraw();
- }
- }
-
- /**
- * Returns true if zooming in/out/to-fit/etc is allowed (which is not the case while
- * emulating real size)
- *
- * @return true if zooming is allowed
- */
- boolean isZoomingAllowed() {
- return mZoomInButton.isEnabled();
- }
-
- boolean isZoomingRealSize() {
- return mZoomRealSizeButton.getSelection();
- }
-
- /**
- * Rescales canvas.
- * @param direction +1 for zoom in, -1 for zoom out
- */
- void rescale(int direction) {
- LayoutCanvas canvas = mEditor.getCanvasControl();
- double s = canvas.getScale();
-
- if (direction > 0) {
- s = s * 1.2;
- } else {
- s = s / 1.2;
- }
-
- // Some operations are faster if the zoom is EXACTLY 1.0 rather than ALMOST 1.0.
- // (This is because there is a fast-path when image copying and the scale is 1.0;
- // in that case it does not have to do any scaling).
- //
- // If you zoom out 10 times and then back in 10 times, small rounding errors mean
- // that you end up with a scale=1.0000000000000004. In the cases, when you get close
- // to 1.0, just make the zoom an exact 1.0.
- if (Math.abs(s-1.0) < 0.0001) {
- s = 1.0;
- }
-
- canvas.setScale(s, true /*redraw*/);
- }
-
- /**
- * Reset the canvas scale to 100%
- */
- void resetScale() {
- mEditor.getCanvasControl().setScale(1, true /*redraw*/);
- }
-
- /**
- * Reset the canvas scale to best fit (so content is as large as possible without scrollbars)
- */
- void rescaleToFit(boolean onlyZoomOut) {
- mEditor.getCanvasControl().setFitScale(onlyZoomOut, true /*allowZoomIn*/);
- }
-
- boolean rescaleToReal(boolean real) {
- if (real) {
- return computeAndSetRealScale(true /*redraw*/);
- } else {
- // reset the scale to 100%
- mEditor.getCanvasControl().setScale(1, true /*redraw*/);
- return true;
- }
- }
-
- boolean computeAndSetRealScale(boolean redraw) {
- // compute average dpi of X and Y
- ConfigurationChooser chooser = mEditor.getConfigurationChooser();
- Configuration config = chooser.getConfiguration();
- Device device = config.getDevice();
- Screen screen = device.getDefaultHardware().getScreen();
- double dpi = (screen.getXdpi() + screen.getYdpi()) / 2.;
-
- // get the monitor dpi
- float monitor = AdtPrefs.getPrefs().getMonitorDensity();
- if (monitor == 0.f) {
- ResolutionChooserDialog dialog = new ResolutionChooserDialog(chooser.getShell());
- if (dialog.open() == Window.OK) {
- monitor = dialog.getDensity();
- AdtPrefs.getPrefs().setMonitorDensity(monitor);
- } else {
- return false;
- }
- }
-
- mEditor.getCanvasControl().setScale(monitor / dpi, redraw);
- return true;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
deleted file mode 100644
index 814b82cec..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
+++ /dev/null
@@ -1,1720 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IDragElement.IDragAttribute;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.eclipse.adt.AdtPlugin;
-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.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-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.lint.LintEditAction;
-import com.android.resources.Density;
-
-import org.eclipse.core.filesystem.EFS;
-import org.eclipse.core.filesystem.IFileStore;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IWorkspaceRoot;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
-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.IMenuManager;
-import org.eclipse.jface.action.IStatusLineManager;
-import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DragSource;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.MenuDetectEvent;
-import org.eclipse.swt.events.MenuDetectListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Canvas;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IActionBars;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IEditorSite;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.actions.ActionFactory;
-import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
-import org.eclipse.ui.actions.ContributionItemFactory;
-import org.eclipse.ui.ide.IDE;
-import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
-import org.eclipse.ui.texteditor.ITextEditor;
-import org.w3c.dom.Node;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Displays the image rendered by the {@link GraphicalEditorPart} and handles
- * the interaction with the widgets.
- * <p/>
- * {@link LayoutCanvas} implements the "Canvas" control. The editor part
- * actually uses the {@link LayoutCanvasViewer}, which is a JFace viewer wrapper
- * around this control.
- * <p/>
- * The LayoutCanvas contains the painting logic for the canvas. Selection,
- * clipboard, view management etc. is handled in separate helper classes.
- *
- * @since GLE2
- */
-@SuppressWarnings("restriction") // For WorkBench "Show In" support
-public class LayoutCanvas extends Canvas {
- private final static QualifiedName NAME_ZOOM =
- new QualifiedName(AdtPlugin.PLUGIN_ID, "zoom");//$NON-NLS-1$
-
- private static final boolean DEBUG = false;
-
- static final String PREFIX_CANVAS_ACTION = "canvas_action_"; //$NON-NLS-1$
-
- /** The layout editor that uses this layout canvas. */
- private final LayoutEditorDelegate mEditorDelegate;
-
- /** The Rules Engine, associated with the current project. */
- private RulesEngine mRulesEngine;
-
- /** GC wrapper given to the IViewRule methods. The GC itself is only defined in the
- * context of {@link #onPaint(PaintEvent)}; otherwise it is null. */
- private GCWrapper mGCWrapper;
-
- /** Default font used on the canvas. Do not dispose, it's a system font. */
- private Font mFont;
-
- /** Current hover view info. Null when no mouse hover. */
- private CanvasViewInfo mHoverViewInfo;
-
- /** When true, always display the outline of all views. */
- private boolean mShowOutline;
-
- /** When true, display the outline of all empty parent views. */
- private boolean mShowInvisible;
-
- /** Drop target associated with this composite. */
- private DropTarget mDropTarget;
-
- /** Factory that can create {@link INode} proxies. */
- private final @NonNull NodeFactory mNodeFactory = new NodeFactory(this);
-
- /** Vertical scaling & scrollbar information. */
- private final CanvasTransform mVScale;
-
- /** Horizontal scaling & scrollbar information. */
- private final CanvasTransform mHScale;
-
- /** Drag source associated with this canvas. */
- private DragSource mDragSource;
-
- /**
- * The current Outline Page, to set its model.
- * It isn't possible to call OutlinePage2.dispose() in this.dispose().
- * this.dispose() is called from GraphicalEditorPart.dispose(),
- * when page's widget is already disposed.
- * Added the DisposeListener to OutlinePage2 in order to correctly dispose this page.
- **/
- private OutlinePage mOutlinePage;
-
- /** Delete action for the Edit or context menu. */
- private Action mDeleteAction;
-
- /** Select-All action for the Edit or context menu. */
- private Action mSelectAllAction;
-
- /** Paste action for the Edit or context menu. */
- private Action mPasteAction;
-
- /** Cut action for the Edit or context menu. */
- private Action mCutAction;
-
- /** Copy action for the Edit or context menu. */
- private Action mCopyAction;
-
- /** Undo action: delegates to the text editor */
- private IAction mUndoAction;
-
- /** Redo action: delegates to the text editor */
- private IAction mRedoAction;
-
- /** Root of the context menu. */
- private MenuManager mMenuManager;
-
- /** The view hierarchy associated with this canvas. */
- private final ViewHierarchy mViewHierarchy = new ViewHierarchy(this);
-
- /** The selection in the canvas. */
- private final SelectionManager mSelectionManager = new SelectionManager(this);
-
- /** The overlay which paints the optional outline. */
- private OutlineOverlay mOutlineOverlay;
-
- /** The overlay which paints outlines around empty children */
- private EmptyViewsOverlay mEmptyOverlay;
-
- /** The overlay which paints the mouse hover. */
- private HoverOverlay mHoverOverlay;
-
- /** The overlay which paints the lint warnings */
- private LintOverlay mLintOverlay;
-
- /** The overlay which paints the selection. */
- private SelectionOverlay mSelectionOverlay;
-
- /** The overlay which paints the rendered layout image. */
- private ImageOverlay mImageOverlay;
-
- /** The overlay which paints masks hiding everything but included content. */
- private IncludeOverlay mIncludeOverlay;
-
- /** Configuration previews shown next to the layout */
- private final RenderPreviewManager mPreviewManager;
-
- /**
- * Gesture Manager responsible for identifying mouse, keyboard and drag and
- * drop events.
- */
- private final GestureManager mGestureManager = new GestureManager(this);
-
- /**
- * When set, performs a zoom-to-fit when the next rendering image arrives.
- */
- private boolean mZoomFitNextImage;
-
- /**
- * Native clipboard support.
- */
- private ClipboardSupport mClipboardSupport;
-
- /** Tooltip manager for lint warnings */
- private LintTooltipManager mLintTooltipManager;
-
- private Color mBackgroundColor;
-
- /**
- * Creates a new {@link LayoutCanvas} widget
- *
- * @param editorDelegate the associated editor delegate
- * @param rulesEngine the rules engine
- * @param parent parent SWT widget
- * @param style the SWT style
- */
- public LayoutCanvas(LayoutEditorDelegate editorDelegate,
- RulesEngine rulesEngine,
- Composite parent,
- int style) {
- super(parent, style | SWT.DOUBLE_BUFFERED | SWT.V_SCROLL | SWT.H_SCROLL);
- mEditorDelegate = editorDelegate;
- mRulesEngine = rulesEngine;
-
- mBackgroundColor = new Color(parent.getDisplay(), 150, 150, 150);
- setBackground(mBackgroundColor);
-
- mClipboardSupport = new ClipboardSupport(this, parent);
- mHScale = new CanvasTransform(this, getHorizontalBar());
- mVScale = new CanvasTransform(this, getVerticalBar());
- mPreviewManager = new RenderPreviewManager(this);
-
- // Unit test suite passes a null here; TODO: Replace with mocking
- IFile file = editorDelegate != null ? editorDelegate.getEditor().getInputFile() : null;
- if (file != null) {
- String zoom = AdtPlugin.getFileProperty(file, NAME_ZOOM);
- if (zoom != null) {
- try {
- double initialScale = Double.parseDouble(zoom);
- if (initialScale > 0.1) {
- mHScale.setScale(initialScale);
- mVScale.setScale(initialScale);
- }
- } catch (NumberFormatException nfe) {
- // Ignore - use zoom=100%
- }
- } else {
- mZoomFitNextImage = true;
- }
- }
-
- mGCWrapper = new GCWrapper(mHScale, mVScale);
-
- Display display = getDisplay();
- mFont = display.getSystemFont();
-
- // --- Set up graphic overlays
- // mOutlineOverlay and mEmptyOverlay are initialized lazily
- mHoverOverlay = new HoverOverlay(this, mHScale, mVScale);
- mHoverOverlay.create(display);
- mSelectionOverlay = new SelectionOverlay(this);
- mSelectionOverlay.create(display);
- mImageOverlay = new ImageOverlay(this, mHScale, mVScale);
- mIncludeOverlay = new IncludeOverlay(this);
- mImageOverlay.create(display);
- mLintOverlay = new LintOverlay(this);
- mLintOverlay.create(display);
-
- // --- Set up listeners
- addPaintListener(new PaintListener() {
- @Override
- public void paintControl(PaintEvent e) {
- onPaint(e);
- }
- });
-
- addControlListener(new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- super.controlResized(e);
-
- // Check editor state:
- LayoutWindowCoordinator coordinator = null;
- IEditorSite editorSite = getEditorDelegate().getEditor().getEditorSite();
- IWorkbenchWindow window = editorSite.getWorkbenchWindow();
- if (window != null) {
- coordinator = LayoutWindowCoordinator.get(window, false);
- if (coordinator != null) {
- coordinator.syncMaximizedState(editorSite.getPage());
- }
- }
-
- updateScrollBars();
-
- // Update the zoom level in the canvas when you toggle the zoom
- if (coordinator != null) {
- mZoomCheck.run();
- } else {
- // During startup, delay updates which can trigger further layout
- getDisplay().asyncExec(mZoomCheck);
-
- }
- }
- });
-
- // --- setup drag'n'drop ---
- // DND Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
-
- mDropTarget = createDropTarget(this);
- mDragSource = createDragSource(this);
- mGestureManager.registerListeners(mDragSource, mDropTarget);
-
- if (mEditorDelegate == null) {
- // TODO: In another CL we should use EasyMock/objgen to provide an editor.
- return; // Unit test
- }
-
- // --- setup context menu ---
- setupGlobalActionHandlers();
- createContextMenu();
-
- // --- setup outline ---
- // Get the outline associated with this editor, if any and of the right type.
- if (editorDelegate != null) {
- mOutlinePage = editorDelegate.getGraphicalOutline();
- }
-
- mLintTooltipManager = new LintTooltipManager(this);
- mLintTooltipManager.register();
- }
-
- void updateScrollBars() {
- Rectangle clientArea = getClientArea();
- Image image = mImageOverlay.getImage();
- if (image != null) {
- ImageData imageData = image.getImageData();
- int clientWidth = clientArea.width;
- int clientHeight = clientArea.height;
-
- int imageWidth = imageData.width;
- int imageHeight = imageData.height;
-
- int fullWidth = imageWidth;
- int fullHeight = imageHeight;
-
- if (mPreviewManager.hasPreviews()) {
- fullHeight = Math.max(fullHeight,
- (int) (mPreviewManager.getHeight() / mHScale.getScale()));
- }
-
- if (clientWidth == 0) {
- clientWidth = imageWidth;
- Shell shell = getShell();
- if (shell != null) {
- org.eclipse.swt.graphics.Point size = shell.getSize();
- if (size.x > 0) {
- clientWidth = size.x * 70 / 100;
- }
- }
- }
- if (clientHeight == 0) {
- clientHeight = imageHeight;
- Shell shell = getShell();
- if (shell != null) {
- org.eclipse.swt.graphics.Point size = shell.getSize();
- if (size.y > 0) {
- clientWidth = size.y * 80 / 100;
- }
- }
- }
-
- mHScale.setSize(imageWidth, fullWidth, clientWidth);
- mVScale.setSize(imageHeight, fullHeight, clientHeight);
- }
- }
-
- private Runnable mZoomCheck = new Runnable() {
- private Boolean mWasZoomed;
-
- @Override
- public void run() {
- if (isDisposed()) {
- return;
- }
-
- IEditorSite editorSite = getEditorDelegate().getEditor().getEditorSite();
- IWorkbenchWindow window = editorSite.getWorkbenchWindow();
- if (window != null) {
- LayoutWindowCoordinator coordinator = LayoutWindowCoordinator.get(window, false);
- if (coordinator != null) {
- Boolean zoomed = coordinator.isEditorMaximized();
- if (mWasZoomed != zoomed) {
- if (mWasZoomed != null) {
- LayoutActionBar actionBar = getGraphicalEditor().getLayoutActionBar();
- if (actionBar.isZoomingAllowed()) {
- setFitScale(true /*onlyZoomOut*/, true /*allowZoomIn*/);
- }
- }
- mWasZoomed = zoomed;
- }
- }
- }
- }
- };
-
- void handleKeyPressed(KeyEvent e) {
- // Set up backspace as an alias for the delete action within the canvas.
- // On most Macs there is no delete key - though there IS a key labeled
- // "Delete" and it sends a backspace key code! In short, for Macs we should
- // treat backspace as delete, and it's harmless (and probably useful) to
- // handle backspace for other platforms as well.
- if (e.keyCode == SWT.BS) {
- mDeleteAction.run();
- } else if (e.keyCode == SWT.ESC) {
- mSelectionManager.selectParent();
- } else if (e.keyCode == DynamicContextMenu.DEFAULT_ACTION_KEY) {
- mSelectionManager.performDefaultAction();
- } else if (e.keyCode == 'r') {
- // Keep key bindings in sync with {@link DynamicContextMenu#createPlainAction}
- // TODO: Find a way to look up the Eclipse key bindings and attempt
- // to use the current keymap's rename action.
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- // Command+Option+R
- if ((e.stateMask & (SWT.MOD1 | SWT.MOD3)) == (SWT.MOD1 | SWT.MOD3)) {
- mSelectionManager.performRename();
- }
- } else {
- // Alt+Shift+R
- if ((e.stateMask & (SWT.MOD2 | SWT.MOD3)) == (SWT.MOD2 | SWT.MOD3)) {
- mSelectionManager.performRename();
- }
- }
- } else {
- // Zooming actions
- char c = e.character;
- LayoutActionBar actionBar = getGraphicalEditor().getLayoutActionBar();
- if (c == '1' && actionBar.isZoomingAllowed()) {
- setScale(1, true);
- } else if (c == '0' && actionBar.isZoomingAllowed()) {
- setFitScale(true, true /*allowZoomIn*/);
- } else if (e.keyCode == '0' && (e.stateMask & SWT.MOD2) != 0
- && actionBar.isZoomingAllowed()) {
- setFitScale(false, true /*allowZoomIn*/);
- } else if ((c == '+' || c == '=') && actionBar.isZoomingAllowed()) {
- if ((e.stateMask & SWT.MOD1) != 0) {
- mPreviewManager.zoomIn();
- } else {
- actionBar.rescale(1);
- }
- } else if (c == '-' && actionBar.isZoomingAllowed()) {
- if ((e.stateMask & SWT.MOD1) != 0) {
- mPreviewManager.zoomOut();
- } else {
- actionBar.rescale(-1);
- }
- }
- }
- }
-
- @Override
- public void dispose() {
- super.dispose();
-
- mGestureManager.unregisterListeners(mDragSource, mDropTarget);
-
- if (mLintTooltipManager != null) {
- mLintTooltipManager.unregister();
- mLintTooltipManager = null;
- }
-
- if (mDropTarget != null) {
- mDropTarget.dispose();
- mDropTarget = null;
- }
-
- if (mRulesEngine != null) {
- mRulesEngine.dispose();
- mRulesEngine = null;
- }
-
- if (mDragSource != null) {
- mDragSource.dispose();
- mDragSource = null;
- }
-
- if (mClipboardSupport != null) {
- mClipboardSupport.dispose();
- mClipboardSupport = null;
- }
-
- if (mGCWrapper != null) {
- mGCWrapper.dispose();
- mGCWrapper = null;
- }
-
- if (mOutlineOverlay != null) {
- mOutlineOverlay.dispose();
- mOutlineOverlay = null;
- }
-
- if (mEmptyOverlay != null) {
- mEmptyOverlay.dispose();
- mEmptyOverlay = null;
- }
-
- if (mHoverOverlay != null) {
- mHoverOverlay.dispose();
- mHoverOverlay = null;
- }
-
- if (mSelectionOverlay != null) {
- mSelectionOverlay.dispose();
- mSelectionOverlay = null;
- }
-
- if (mImageOverlay != null) {
- mImageOverlay.dispose();
- mImageOverlay = null;
- }
-
- if (mIncludeOverlay != null) {
- mIncludeOverlay.dispose();
- mIncludeOverlay = null;
- }
-
- if (mLintOverlay != null) {
- mLintOverlay.dispose();
- mLintOverlay = null;
- }
-
- if (mBackgroundColor != null) {
- mBackgroundColor.dispose();
- mBackgroundColor = null;
- }
-
- mPreviewManager.disposePreviews();
- mViewHierarchy.dispose();
- }
-
- /**
- * Returns the configuration preview manager for this canvas
- *
- * @return the configuration preview manager for this canvas
- */
- @NonNull
- public RenderPreviewManager getPreviewManager() {
- return mPreviewManager;
- }
-
- /** Returns the Rules Engine, associated with the current project. */
- RulesEngine getRulesEngine() {
- return mRulesEngine;
- }
-
- /** Sets the Rules Engine, associated with the current project. */
- void setRulesEngine(RulesEngine rulesEngine) {
- mRulesEngine = rulesEngine;
- }
-
- /**
- * Returns the factory to use to convert from {@link CanvasViewInfo} or from
- * {@link UiViewElementNode} to {@link INode} proxies.
- *
- * @return the node factory
- */
- @NonNull
- public NodeFactory getNodeFactory() {
- return mNodeFactory;
- }
-
- /**
- * Returns the GCWrapper used to paint view rules.
- *
- * @return The GCWrapper used to paint view rules
- */
- GCWrapper getGcWrapper() {
- return mGCWrapper;
- }
-
- /**
- * Returns the {@link LayoutEditorDelegate} associated with this canvas.
- *
- * @return the delegate
- */
- public LayoutEditorDelegate getEditorDelegate() {
- return mEditorDelegate;
- }
-
- /**
- * Returns the current {@link ImageOverlay} painting the rendered result
- *
- * @return the image overlay responsible for painting the rendered result, never null
- */
- ImageOverlay getImageOverlay() {
- return mImageOverlay;
- }
-
- /**
- * Returns the current {@link SelectionOverlay} painting the selection highlights
- *
- * @return the selection overlay responsible for painting the selection highlights,
- * never null
- */
- SelectionOverlay getSelectionOverlay() {
- return mSelectionOverlay;
- }
-
- /**
- * Returns the {@link GestureManager} associated with this canvas.
- *
- * @return the {@link GestureManager} associated with this canvas, never null.
- */
- GestureManager getGestureManager() {
- return mGestureManager;
- }
-
- /**
- * Returns the current {@link HoverOverlay} painting the mouse hover.
- *
- * @return the hover overlay responsible for painting the mouse hover,
- * never null
- */
- HoverOverlay getHoverOverlay() {
- return mHoverOverlay;
- }
-
- /**
- * Returns the horizontal {@link CanvasTransform} transform object, which can map
- * a layout point into a control point.
- *
- * @return A {@link CanvasTransform} for mapping between layout and control
- * coordinates in the horizontal dimension.
- */
- CanvasTransform getHorizontalTransform() {
- return mHScale;
- }
-
- /**
- * Returns the vertical {@link CanvasTransform} transform object, which can map a
- * layout point into a control point.
- *
- * @return A {@link CanvasTransform} for mapping between layout and control
- * coordinates in the vertical dimension.
- */
- CanvasTransform getVerticalTransform() {
- return mVScale;
- }
-
- /**
- * Returns the {@link OutlinePage} associated with this canvas
- *
- * @return the {@link OutlinePage} associated with this canvas
- */
- public OutlinePage getOutlinePage() {
- return mOutlinePage;
- }
-
- /**
- * Returns the {@link SelectionManager} associated with this canvas.
- *
- * @return The {@link SelectionManager} holding the selection for this
- * canvas. Never null.
- */
- public SelectionManager getSelectionManager() {
- return mSelectionManager;
- }
-
- /**
- * Returns the {@link ViewHierarchy} object associated with this canvas,
- * holding the most recent rendered view of the scene, if valid.
- *
- * @return The {@link ViewHierarchy} object associated with this canvas.
- * Never null.
- */
- public ViewHierarchy getViewHierarchy() {
- return mViewHierarchy;
- }
-
- /**
- * Returns the {@link ClipboardSupport} object associated with this canvas.
- *
- * @return The {@link ClipboardSupport} object for this canvas. Null only after dispose.
- */
- public ClipboardSupport getClipboardSupport() {
- return mClipboardSupport;
- }
-
- /** Returns the Select All action bound to this canvas */
- Action getSelectAllAction() {
- return mSelectAllAction;
- }
-
- /** Returns the associated {@link GraphicalEditorPart} */
- GraphicalEditorPart getGraphicalEditor() {
- return mEditorDelegate.getGraphicalEditor();
- }
-
- /**
- * Sets the result of the layout rendering. The result object indicates if the layout
- * rendering succeeded. If it did, it contains a bitmap and the objects rectangles.
- *
- * Implementation detail: the bridge's computeLayout() method already returns a newly
- * allocated ILayourResult. That means we can keep this result and hold on to it
- * when it is valid.
- *
- * @param session The new scene, either valid or not.
- * @param explodedNodes The set of individual nodes the layout computer was asked to
- * explode. Note that these are independent of the explode-all mode where
- * all views are exploded; this is used only for the mode (
- * {@link #showInvisibleViews(boolean)}) where individual invisible nodes
- * are padded during certain interactions.
- */
- void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
- boolean layoutlib5) {
- // disable any hover
- clearHover();
-
- mViewHierarchy.setSession(session, explodedNodes, layoutlib5);
- if (mViewHierarchy.isValid() && session != null) {
- Image image = mImageOverlay.setImage(session.getImage(),
- session.isAlphaChannelImage());
-
- mOutlinePage.setModel(mViewHierarchy.getRoot());
- getGraphicalEditor().setModel(mViewHierarchy.getRoot());
-
- if (image != null) {
- updateScrollBars();
- if (mZoomFitNextImage) {
- // Must be run asynchronously because getClientArea() returns 0 bounds
- // when the editor is being initialized
- getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- if (!isDisposed()) {
- ensureZoomed();
- }
- }
- });
- }
-
- // Ensure that if we have a a preview mode enabled, it's shown
- syncPreviewMode();
- }
- }
-
- redraw();
- }
-
- void ensureZoomed() {
- if (mZoomFitNextImage && getClientArea().height > 0) {
- mZoomFitNextImage = false;
- LayoutActionBar actionBar = getGraphicalEditor().getLayoutActionBar();
- if (actionBar.isZoomingAllowed()) {
- setFitScale(true, true /*allowZoomIn*/);
- }
- }
- }
-
- void setShowOutline(boolean newState) {
- mShowOutline = newState;
- redraw();
- }
-
- /**
- * Returns the zoom scale factor of the canvas (the amount the full
- * resolution render of the device is zoomed before being shown on the
- * canvas)
- *
- * @return the image scale
- */
- public double getScale() {
- return mHScale.getScale();
- }
-
- void setScale(double scale, boolean redraw) {
- if (scale <= 0.0) {
- scale = 1.0;
- }
-
- if (scale == getScale()) {
- return;
- }
-
- mHScale.setScale(scale);
- mVScale.setScale(scale);
- if (redraw) {
- redraw();
- }
-
- // Clear the zoom setting if it is almost identical to 1.0
- String zoomValue = (Math.abs(scale - 1.0) < 0.0001) ? null : Double.toString(scale);
- IFile file = mEditorDelegate.getEditor().getInputFile();
- if (file != null) {
- AdtPlugin.setFileProperty(file, NAME_ZOOM, zoomValue);
- }
- }
-
- /**
- * Scales the canvas to best fit
- *
- * @param onlyZoomOut if true, then the zooming factor will never be larger than 1,
- * which means that this function will zoom out if necessary to show the
- * rendered image, but it will never zoom in.
- * TODO: Rename this, it sounds like it conflicts with allowZoomIn,
- * even though one is referring to the zoom level and one is referring
- * to the overall act of scaling above/below 1.
- * @param allowZoomIn if false, then if the computed zoom factor is smaller than
- * the current zoom factor, it will be ignored.
- */
- public void setFitScale(boolean onlyZoomOut, boolean allowZoomIn) {
- ImageOverlay imageOverlay = getImageOverlay();
- if (imageOverlay == null) {
- return;
- }
- Image image = imageOverlay.getImage();
- if (image != null) {
- Rectangle canvasSize = getClientArea();
- int canvasWidth = canvasSize.width;
- int canvasHeight = canvasSize.height;
-
- boolean hasPreviews = mPreviewManager.hasPreviews();
- if (hasPreviews) {
- canvasWidth = 2 * canvasWidth / 3;
- } else {
- canvasWidth -= 4;
- canvasHeight -= 4;
- }
-
- ImageData imageData = image.getImageData();
- int sceneWidth = imageData.width;
- int sceneHeight = imageData.height;
- if (sceneWidth == 0.0 || sceneHeight == 0.0) {
- return;
- }
-
- if (imageOverlay.getShowDropShadow()) {
- sceneWidth += 2 * ImageUtils.SHADOW_SIZE;
- sceneHeight += 2 * ImageUtils.SHADOW_SIZE;
- }
-
- // Reduce the margins if necessary
- int hDelta = canvasWidth - sceneWidth;
- int hMargin = 0;
- if (hDelta > 2 * CanvasTransform.DEFAULT_MARGIN) {
- hMargin = CanvasTransform.DEFAULT_MARGIN;
- } else if (hDelta > 0) {
- hMargin = hDelta / 2;
- }
-
- int vDelta = canvasHeight - sceneHeight;
- int vMargin = 0;
- if (vDelta > 2 * CanvasTransform.DEFAULT_MARGIN) {
- vMargin = CanvasTransform.DEFAULT_MARGIN;
- } else if (vDelta > 0) {
- vMargin = vDelta / 2;
- }
-
- double hScale = (canvasWidth - 2 * hMargin) / (double) sceneWidth;
- double vScale = (canvasHeight - 2 * vMargin) / (double) sceneHeight;
-
- double scale = Math.min(hScale, vScale);
-
- if (onlyZoomOut) {
- scale = Math.min(1.0, scale);
- }
-
- if (!allowZoomIn && scale > getScale()) {
- return;
- }
-
- setScale(scale, true);
- }
- }
-
- /**
- * Transforms a point, expressed in layout coordinates, into "client" coordinates
- * relative to the control (and not relative to the display).
- *
- * @param canvasX X in the canvas coordinates
- * @param canvasY Y in the canvas coordinates
- * @return A new {@link Point} in control client coordinates (not display coordinates)
- */
- Point layoutToControlPoint(int canvasX, int canvasY) {
- int x = mHScale.translate(canvasX);
- int y = mVScale.translate(canvasY);
- return new Point(x, y);
- }
-
- /**
- * Returns the action for the context menu corresponding to the given action id.
- * <p/>
- * For global actions such as copy or paste, the action id must be composed of
- * the {@link #PREFIX_CANVAS_ACTION} followed by one of {@link ActionFactory}'s
- * action ids.
- * <p/>
- * Returns null if there's no action for the given id.
- */
- IAction getAction(String actionId) {
- String prefix = PREFIX_CANVAS_ACTION;
- if (mMenuManager == null ||
- actionId == null ||
- !actionId.startsWith(prefix)) {
- return null;
- }
-
- actionId = actionId.substring(prefix.length());
-
- for (IContributionItem contrib : mMenuManager.getItems()) {
- if (contrib instanceof ActionContributionItem &&
- actionId.equals(contrib.getId())) {
- return ((ActionContributionItem) contrib).getAction();
- }
- }
-
- return null;
- }
-
- //---------------
-
- /**
- * Paints the canvas in response to paint events.
- */
- private void onPaint(PaintEvent e) {
- GC gc = e.gc;
- gc.setFont(mFont);
- mGCWrapper.setGC(gc);
- try {
- if (!mImageOverlay.isHiding()) {
- mImageOverlay.paint(gc);
- }
-
- mPreviewManager.paint(gc);
-
- if (mShowOutline) {
- if (mOutlineOverlay == null) {
- mOutlineOverlay = new OutlineOverlay(mViewHierarchy, mHScale, mVScale);
- mOutlineOverlay.create(getDisplay());
- }
- if (!mOutlineOverlay.isHiding()) {
- mOutlineOverlay.paint(gc);
- }
- }
-
- if (mShowInvisible) {
- if (mEmptyOverlay == null) {
- mEmptyOverlay = new EmptyViewsOverlay(mViewHierarchy, mHScale, mVScale);
- mEmptyOverlay.create(getDisplay());
- }
- if (!mEmptyOverlay.isHiding()) {
- mEmptyOverlay.paint(gc);
- }
- }
-
- if (!mHoverOverlay.isHiding()) {
- mHoverOverlay.paint(gc);
- }
-
- if (!mLintOverlay.isHiding()) {
- mLintOverlay.paint(gc);
- }
-
- if (!mIncludeOverlay.isHiding()) {
- mIncludeOverlay.paint(gc);
- }
-
- if (!mSelectionOverlay.isHiding()) {
- mSelectionOverlay.paint(mSelectionManager, mGCWrapper, gc, mRulesEngine);
- }
- mGestureManager.paint(gc);
-
- } finally {
- mGCWrapper.setGC(null);
- }
- }
-
- /**
- * Shows or hides invisible parent views, which are views which have empty bounds and
- * no children. The nodes which will be shown are provided by
- * {@link #getNodesToExplode()}.
- *
- * @param show When true, any invisible parent nodes are padded and highlighted
- * ("exploded"), and when false any formerly exploded nodes are hidden.
- */
- void showInvisibleViews(boolean show) {
- if (mShowInvisible == show) {
- return;
- }
- mShowInvisible = show;
-
- // Optimization: Avoid doing work when we don't have invisible parents (on show)
- // or formerly exploded nodes (on hide).
- if (show && !mViewHierarchy.hasInvisibleParents()) {
- return;
- } else if (!show && !mViewHierarchy.hasExplodedParents()) {
- return;
- }
-
- mEditorDelegate.recomputeLayout();
- }
-
- /**
- * Returns a set of nodes that should be exploded (forced non-zero padding during render),
- * or null if no nodes should be exploded. (Note that this is independent of the
- * explode-all mode, where all nodes are padded -- that facility does not use this
- * mechanism, which is only intended to be used to expose invisible parent nodes.
- *
- * @return The set of invisible parents, or null if no views should be expanded.
- */
- public Set<UiElementNode> getNodesToExplode() {
- if (mShowInvisible) {
- return mViewHierarchy.getInvisibleNodes();
- }
-
- // IF we have selection, and IF we have invisible nodes in the view,
- // see if any of the selected items are among the invisible nodes, and if so
- // add them to a lazily constructed set which we pass back for rendering.
- Set<UiElementNode> result = null;
- List<SelectionItem> selections = mSelectionManager.getSelections();
- if (selections.size() > 0) {
- List<CanvasViewInfo> invisibleParents = mViewHierarchy.getInvisibleViews();
- if (invisibleParents.size() > 0) {
- for (SelectionItem item : selections) {
- CanvasViewInfo viewInfo = item.getViewInfo();
- // O(n^2) here, but both the selection size and especially the
- // invisibleParents size are expected to be small
- if (invisibleParents.contains(viewInfo)) {
- UiViewElementNode node = viewInfo.getUiViewNode();
- if (node != null) {
- if (result == null) {
- result = new HashSet<UiElementNode>();
- }
- result.add(node);
- }
- }
- }
- }
- }
-
- return result;
- }
-
- /**
- * Clears the hover.
- */
- void clearHover() {
- mHoverOverlay.clearHover();
- }
-
- /**
- * Hover on top of a known child.
- */
- void hover(MouseEvent e) {
- // Check if a button is pressed; no hovers during drags
- if ((e.stateMask & SWT.BUTTON_MASK) != 0) {
- clearHover();
- return;
- }
-
- LayoutPoint p = ControlPoint.create(this, e).toLayout();
- CanvasViewInfo vi = mViewHierarchy.findViewInfoAt(p);
-
- // We don't hover on the root since it's not a widget per see and it is always there.
- // We also skip spacers...
- if (vi != null && (vi.isRoot() || vi.isHidden())) {
- vi = null;
- }
-
- boolean needsUpdate = vi != mHoverViewInfo;
- mHoverViewInfo = vi;
-
- if (vi == null) {
- clearHover();
- } else {
- Rectangle r = vi.getSelectionRect();
- mHoverOverlay.setHover(r.x, r.y, r.width, r.height);
- }
-
- if (needsUpdate) {
- redraw();
- }
- }
-
- /**
- * Shows the given {@link CanvasViewInfo}, which can mean exposing its XML or if it's
- * an included element, its corresponding file.
- *
- * @param vi the {@link CanvasViewInfo} to be shown
- */
- public void show(CanvasViewInfo vi) {
- String url = vi.getIncludeUrl();
- if (url != null) {
- showInclude(url);
- } else {
- showXml(vi);
- }
- }
-
- /**
- * Shows the layout file referenced by the given url in the same project.
- *
- * @param url The layout attribute url of the form @layout/foo
- */
- private void showInclude(String url) {
- GraphicalEditorPart graphicalEditor = getGraphicalEditor();
- IPath filePath = graphicalEditor.findResourceFile(url);
- if (filePath == null) {
- // Should not be possible - if the URL had been bad, then we wouldn't
- // have been able to render the scene and you wouldn't have been able
- // to click on it
- return;
- }
-
- // Save the including file, if necessary: without it, the "Show Included In"
- // facility which is invoked automatically will not work properly if the <include>
- // tag is not in the saved version of the file, since the outer file is read from
- // disk rather than from memory.
- IEditorSite editorSite = graphicalEditor.getEditorSite();
- IWorkbenchPage page = editorSite.getPage();
- page.saveEditor(mEditorDelegate.getEditor(), false);
-
- IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot();
- IFile xmlFile = null;
- IPath workspacePath = workspace.getLocation();
- if (workspacePath.isPrefixOf(filePath)) {
- IPath relativePath = filePath.makeRelativeTo(workspacePath);
- xmlFile = (IFile) workspace.findMember(relativePath);
- } else if (filePath.isAbsolute()) {
- xmlFile = workspace.getFileForLocation(filePath);
- }
- if (xmlFile != null) {
- IFile leavingFile = graphicalEditor.getEditedFile();
- Reference next = Reference.create(graphicalEditor.getEditedFile());
-
- try {
- IEditorPart openAlready = EditorUtility.isOpenInEditor(xmlFile);
-
- // Show the included file as included within this click source?
- if (openAlready != null) {
- LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(openAlready);
- if (delegate != null) {
- GraphicalEditorPart gEditor = delegate.getGraphicalEditor();
- if (gEditor != null &&
- gEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) {
- gEditor.showIn(next);
- }
- }
- } else {
- try {
- // Set initial state of a new file
- // TODO: Only set rendering target portion of the state
- String state = ConfigurationDescription.getDescription(leavingFile);
- xmlFile.setSessionProperty(GraphicalEditorPart.NAME_INITIAL_STATE,
- state);
- } catch (CoreException e) {
- // pass
- }
-
- if (graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) {
- try {
- xmlFile.setSessionProperty(GraphicalEditorPart.NAME_INCLUDE, next);
- } catch (CoreException e) {
- // pass - worst that can happen is that we don't
- //start with inclusion
- }
- }
- }
-
- EditorUtility.openInEditor(xmlFile, true);
- return;
- } catch (PartInitException ex) {
- AdtPlugin.log(ex, "Can't open %$1s", url); //$NON-NLS-1$
- }
- } else {
- // It's not a path in the workspace; look externally
- // (this is probably an @android: path)
- if (filePath.isAbsolute()) {
- IFileStore fileStore = EFS.getLocalFileSystem().getStore(filePath);
- // fileStore = fileStore.getChild(names[i]);
- if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) {
- try {
- IDE.openEditorOnFileStore(page, fileStore);
- return;
- } catch (PartInitException ex) {
- AdtPlugin.log(ex, "Can't open %$1s", url); //$NON-NLS-1$
- }
- }
- }
- }
-
- // Failed: display message to the user
- String message = String.format("Could not find resource %1$s", url);
- IStatusLineManager status = editorSite.getActionBars().getStatusLineManager();
- status.setErrorMessage(message);
- getDisplay().beep();
- }
-
- /**
- * Returns the layout resource name of this layout
- *
- * @return the layout resource name of this layout
- */
- public String getLayoutResourceName() {
- GraphicalEditorPart graphicalEditor = getGraphicalEditor();
- return graphicalEditor.getLayoutResourceName();
- }
-
- /**
- * Returns the layout resource url of the current layout
- *
- * @return
- */
- /*
- public String getMe() {
- GraphicalEditorPart graphicalEditor = getGraphicalEditor();
- IFile editedFile = graphicalEditor.getEditedFile();
- return editedFile.getProjectRelativePath().toOSString();
- }
- */
-
- /**
- * Show the XML element corresponding to the given {@link CanvasViewInfo} (unless it's
- * a root).
- *
- * @param vi The clicked {@link CanvasViewInfo} whose underlying XML element we want
- * to view
- */
- private void showXml(CanvasViewInfo vi) {
- // Warp to the text editor and show the corresponding XML for the
- // double-clicked widget
- if (vi.isRoot()) {
- return;
- }
-
- Node xmlNode = vi.getXmlNode();
- if (xmlNode != null) {
- boolean found = mEditorDelegate.getEditor().show(xmlNode);
- if (!found) {
- getDisplay().beep();
- }
- }
- }
-
- //---------------
-
- /**
- * Helper to create the drag source for the given control.
- * <p/>
- * This is static with package-access so that {@link OutlinePage} can also
- * create an exact copy of the source with the same attributes.
- */
- /* package */static DragSource createDragSource(Control control) {
- DragSource source = new DragSource(control, DND.DROP_COPY | DND.DROP_MOVE);
- source.setTransfer(new Transfer[] {
- TextTransfer.getInstance(),
- SimpleXmlTransfer.getInstance()
- });
- return source;
- }
-
- /**
- * Helper to create the drop target for the given control.
- */
- private static DropTarget createDropTarget(Control control) {
- DropTarget dropTarget = new DropTarget(
- control, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT);
- dropTarget.setTransfer(new Transfer[] {
- SimpleXmlTransfer.getInstance()
- });
- return dropTarget;
- }
-
- //---------------
-
- /**
- * Invoked by the constructor to add our cut/copy/paste/delete/select-all
- * handlers in the global action handlers of this editor's site.
- * <p/>
- * This will enable the menu items under the global Edit menu and make them
- * invoke our actions as needed. As a benefit, the corresponding shortcut
- * accelerators will do what one would expect.
- */
- private void setupGlobalActionHandlers() {
- mCutAction = new Action() {
- @Override
- public void run() {
- mClipboardSupport.cutSelectionToClipboard(mSelectionManager.getSnapshot());
- updateMenuActionState();
- }
- };
-
- copyActionAttributes(mCutAction, ActionFactory.CUT);
-
- mCopyAction = new Action() {
- @Override
- public void run() {
- mClipboardSupport.copySelectionToClipboard(mSelectionManager.getSnapshot());
- updateMenuActionState();
- }
- };
-
- copyActionAttributes(mCopyAction, ActionFactory.COPY);
-
- mPasteAction = new Action() {
- @Override
- public void run() {
- mClipboardSupport.pasteSelection(mSelectionManager.getSnapshot());
- updateMenuActionState();
- }
- };
-
- copyActionAttributes(mPasteAction, ActionFactory.PASTE);
-
- mDeleteAction = new Action() {
- @Override
- public void run() {
- mClipboardSupport.deleteSelection(
- getDeleteLabel(),
- mSelectionManager.getSnapshot());
- }
- };
-
- copyActionAttributes(mDeleteAction, ActionFactory.DELETE);
-
- mSelectAllAction = new Action() {
- @Override
- public void run() {
- GraphicalEditorPart graphicalEditor = getEditorDelegate().getGraphicalEditor();
- StyledText errorLabel = graphicalEditor.getErrorLabel();
- if (errorLabel.isFocusControl()) {
- errorLabel.selectAll();
- return;
- }
-
- mSelectionManager.selectAll();
- }
- };
-
- copyActionAttributes(mSelectAllAction, ActionFactory.SELECT_ALL);
- }
-
- String getCutLabel() {
- return mCutAction.getText();
- }
-
- String getDeleteLabel() {
- // verb "Delete" from the DELETE action's title
- return mDeleteAction.getText();
- }
-
- /**
- * Updates menu actions that depends on the selection.
- */
- void updateMenuActionState() {
- List<SelectionItem> selections = getSelectionManager().getSelections();
- boolean hasSelection = !selections.isEmpty();
- if (hasSelection && selections.size() == 1 && selections.get(0).isRoot()) {
- hasSelection = false;
- }
-
- StyledText errorLabel = getGraphicalEditor().getErrorLabel();
- mCutAction.setEnabled(hasSelection);
- mCopyAction.setEnabled(hasSelection || errorLabel.getSelectionCount() > 0);
- mDeleteAction.setEnabled(hasSelection);
- // Select All should *always* be selectable, regardless of whether anything
- // is currently selected.
- mSelectAllAction.setEnabled(true);
-
- // The paste operation is only available if we can paste our custom type.
- // We do not currently support pasting random text (e.g. XML). Maybe later.
- boolean hasSxt = mClipboardSupport.hasSxtOnClipboard();
- mPasteAction.setEnabled(hasSxt);
- }
-
- /**
- * Update the actions when this editor is activated
- *
- * @param bars the action bar for this canvas
- */
- public void updateGlobalActions(@NonNull IActionBars bars) {
- updateMenuActionState();
-
- ITextEditor editor = mEditorDelegate.getEditor().getStructuredTextEditor();
- boolean graphical = getEditorDelegate().getEditor().getActivePage() == 0;
- if (graphical) {
- bars.setGlobalActionHandler(ActionFactory.CUT.getId(), mCutAction);
- bars.setGlobalActionHandler(ActionFactory.COPY.getId(), mCopyAction);
- bars.setGlobalActionHandler(ActionFactory.PASTE.getId(), mPasteAction);
- bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), mDeleteAction);
- bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), mSelectAllAction);
-
- // Delegate the Undo and Redo actions to the text editor ones, but wrap them
- // such that we run lint to update the results on the current page (this is
- // normally done on each editor operation that goes through
- // {@link AndroidXmlEditor#wrapUndoEditXmlModel}, but not undo/redo)
- if (mUndoAction == null) {
- IAction undoAction = editor.getAction(ActionFactory.UNDO.getId());
- mUndoAction = new LintEditAction(undoAction, getEditorDelegate().getEditor());
- }
- bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), mUndoAction);
- if (mRedoAction == null) {
- IAction redoAction = editor.getAction(ActionFactory.REDO.getId());
- mRedoAction = new LintEditAction(redoAction, getEditorDelegate().getEditor());
- }
- bars.setGlobalActionHandler(ActionFactory.REDO.getId(), mRedoAction);
- } else {
- bars.setGlobalActionHandler(ActionFactory.CUT.getId(),
- editor.getAction(ActionFactory.CUT.getId()));
- bars.setGlobalActionHandler(ActionFactory.COPY.getId(),
- editor.getAction(ActionFactory.COPY.getId()));
- bars.setGlobalActionHandler(ActionFactory.PASTE.getId(),
- editor.getAction(ActionFactory.PASTE.getId()));
- bars.setGlobalActionHandler(ActionFactory.DELETE.getId(),
- editor.getAction(ActionFactory.DELETE.getId()));
- bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
- editor.getAction(ActionFactory.SELECT_ALL.getId()));
- bars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
- editor.getAction(ActionFactory.UNDO.getId()));
- bars.setGlobalActionHandler(ActionFactory.REDO.getId(),
- editor.getAction(ActionFactory.REDO.getId()));
- }
-
- bars.updateActionBars();
- }
-
- /**
- * Helper for {@link #setupGlobalActionHandlers()}.
- * Copies the action attributes form the given {@link ActionFactory}'s action to
- * our action.
- * <p/>
- * {@link ActionFactory} provides access to the standard global actions in Eclipse.
- * <p/>
- * This allows us to grab the standard labels and icons for the
- * global actions such as copy, cut, paste, delete and select-all.
- */
- private void copyActionAttributes(Action action, ActionFactory factory) {
- IWorkbenchAction wa = factory.create(
- mEditorDelegate.getEditor().getEditorSite().getWorkbenchWindow());
- action.setId(wa.getId());
- action.setText(wa.getText());
- action.setEnabled(wa.isEnabled());
- action.setDescription(wa.getDescription());
- action.setToolTipText(wa.getToolTipText());
- action.setAccelerator(wa.getAccelerator());
- action.setActionDefinitionId(wa.getActionDefinitionId());
- action.setImageDescriptor(wa.getImageDescriptor());
- action.setHoverImageDescriptor(wa.getHoverImageDescriptor());
- action.setDisabledImageDescriptor(wa.getDisabledImageDescriptor());
- action.setHelpListener(wa.getHelpListener());
- }
-
- /**
- * Creates the context menu for the canvas. This is called once from the canvas' constructor.
- * <p/>
- * The menu has a static part with actions that are always available such as
- * copy, cut, paste and show in > explorer. This is created by
- * {@link #setupStaticMenuActions(IMenuManager)}.
- * <p/>
- * There's also a dynamic part that is populated by the rules of the
- * selected elements, created by {@link DynamicContextMenu}.
- */
- @SuppressWarnings("unused")
- private void createContextMenu() {
-
- // This manager is the root of the context menu.
- mMenuManager = new MenuManager() {
- @Override
- public boolean isDynamic() {
- return true;
- }
- };
-
- // Fill the menu manager with the static & dynamic actions
- setupStaticMenuActions(mMenuManager);
- new DynamicContextMenu(mEditorDelegate, this, mMenuManager);
- Menu menu = mMenuManager.createContextMenu(this);
- setMenu(menu);
-
- // Add listener to detect when the menu is about to be posted, such that
- // we can sync the selection. Without this, you can right click on something
- // in the canvas which is NOT selected, and the context menu will show items related
- // to the selection, NOT the item you clicked on!!
- addMenuDetectListener(new MenuDetectListener() {
- @Override
- public void menuDetected(MenuDetectEvent e) {
- mSelectionManager.menuClick(e);
- }
- });
- }
-
- /**
- * Invoked by {@link #createContextMenu()} to create our *static* context menu once.
- * <p/>
- * The content of the menu itself does not change. However the state of the
- * various items is controlled by their associated actions.
- * <p/>
- * For cut/copy/paste/delete/select-all, we explicitly reuse the actions
- * created by {@link #setupGlobalActionHandlers()}, so this method must be
- * invoked after that one.
- */
- private void setupStaticMenuActions(IMenuManager manager) {
- manager.removeAll();
-
- manager.add(new SelectionManager.SelectionMenu(getGraphicalEditor()));
- manager.add(new Separator());
- manager.add(mCutAction);
- manager.add(mCopyAction);
- manager.add(mPasteAction);
- manager.add(new Separator());
- manager.add(mDeleteAction);
- manager.add(new Separator());
- manager.add(new PlayAnimationMenu(this));
- manager.add(new ExportScreenshotAction(this));
- manager.add(new Separator());
-
- // Group "Show Included In" and "Show In" together
- manager.add(new ShowWithinMenu(mEditorDelegate));
-
- // Create a "Show In" sub-menu and automatically populate it using standard
- // actions contributed by the workbench.
- String showInLabel = IDEWorkbenchMessages.Workbench_showIn;
- MenuManager showInSubMenu = new MenuManager(showInLabel);
- showInSubMenu.add(
- ContributionItemFactory.VIEWS_SHOW_IN.create(
- mEditorDelegate.getEditor().getSite().getWorkbenchWindow()));
- manager.add(showInSubMenu);
- }
-
- /**
- * Deletes the selection. Equivalent to pressing the Delete key.
- */
- void delete() {
- mDeleteAction.run();
- }
-
- /**
- * Add new root in an existing empty XML layout.
- * <p/>
- * In case of error (unknown FQCN, document not empty), silently do nothing.
- * In case of success, the new element will have some default attributes set
- * (xmlns:android, layout_width and height). The edit is wrapped in a proper
- * undo.
- * <p/>
- * This is invoked by
- * {@link MoveGesture#drop(org.eclipse.swt.dnd.DropTargetEvent)}.
- *
- * @param root A non-null descriptor of the root element to create.
- */
- void createDocumentRoot(final @NonNull SimpleElement root) {
- String rootFqcn = root.getFqcn();
-
- // Need a valid empty document to create the new root
- final UiDocumentNode uiDoc = mEditorDelegate.getUiRootNode();
- if (uiDoc == null || uiDoc.getUiChildren().size() > 0) {
- debugPrintf("Failed to create document root for %1$s: document is not empty",
- rootFqcn);
- return;
- }
-
- // Find the view descriptor matching our FQCN
- final ViewElementDescriptor viewDesc = mEditorDelegate.getFqcnViewDescriptor(rootFqcn);
- if (viewDesc == null) {
- // TODO this could happen if dropping a custom view not known in this project
- debugPrintf("Failed to add document root, unknown FQCN %1$s", rootFqcn);
- return;
- }
-
- // Get the last segment of the FQCN for the undo title
- String title = rootFqcn;
- int pos = title.lastIndexOf('.');
- if (pos > 0 && pos < title.length() - 1) {
- title = title.substring(pos + 1);
- }
- title = String.format("Create root %1$s in document", title);
-
- mEditorDelegate.getEditor().wrapUndoEditXmlModel(title, new Runnable() {
- @Override
- public void run() {
- UiElementNode uiNew = uiDoc.appendNewUiChild(viewDesc);
-
- // A root node requires the Android XMLNS
- uiNew.setAttributeValue(
- SdkConstants.ANDROID_NS_NAME,
- SdkConstants.XMLNS_URI,
- SdkConstants.NS_RESOURCES,
- true /*override*/);
-
- IDragAttribute[] attributes = root.getAttributes();
- if (attributes != null) {
- for (IDragAttribute attribute : attributes) {
- String uri = attribute.getUri();
- String name = attribute.getName();
- String value = attribute.getValue();
- uiNew.setAttributeValue(name, uri, value, false /*override*/);
- }
- }
-
- // Adjust the attributes
- DescriptorsUtils.setDefaultLayoutAttributes(uiNew, false /*updateLayout*/);
-
- uiNew.createXmlNode();
- }
- });
- }
-
- /**
- * Returns the insets associated with views of the given fully qualified name, for the
- * current theme and screen type.
- *
- * @param fqcn the fully qualified name to the widget type
- * @return the insets, or null if unknown
- */
- public Margins getInsets(String fqcn) {
- if (ViewMetadataRepository.INSETS_SUPPORTED) {
- ConfigurationChooser configComposite = getGraphicalEditor().getConfigurationChooser();
- String theme = configComposite.getThemeName();
- Density density = configComposite.getConfiguration().getDensity();
- return ViewMetadataRepository.getInsets(fqcn, density, theme);
- } else {
- return null;
- }
- }
-
- private void debugPrintf(String message, Object... params) {
- if (DEBUG) {
- AdtPlugin.printToConsole("Canvas", String.format(message, params));
- }
- }
-
- /** The associated editor has been deactivated */
- public void deactivated() {
- // Force the tooltip to be hidden. If you switch from the layout editor
- // to a Java editor with the keyboard, the tooltip can stay open.
- if (mLintTooltipManager != null) {
- mLintTooltipManager.hide();
- }
- }
-
- /** @see #setPreview(RenderPreview) */
- private RenderPreview mPreview;
-
- /**
- * Sets the {@link RenderPreview} associated with the currently rendering
- * configuration.
- * <p>
- * A {@link RenderPreview} has various additional state beyond its rendering,
- * such as its display name (which can be edited by the user). When you click on
- * previews, the layout editor switches to show the given configuration preview.
- * The preview is then no longer shown in the list of previews and is instead rendered
- * in the main editor. However, when you then switch away to some other preview, we
- * want to be able to restore the preview with all its state.
- *
- * @param preview the preview associated with the current canvas
- */
- public void setPreview(@Nullable RenderPreview preview) {
- mPreview = preview;
- }
-
- /**
- * Returns the {@link RenderPreview} associated with this layout canvas.
- *
- * @see #setPreview(RenderPreview)
- * @return the {@link RenderPreview}
- */
- @Nullable
- public RenderPreview getPreview() {
- return mPreview;
- }
-
- /** Ensures that the configuration previews are up to date for this canvas */
- public void syncPreviewMode() {
- if (mImageOverlay != null && mImageOverlay.getImage() != null &&
- getGraphicalEditor().getConfigurationChooser().getResources() != null) {
- if (mPreviewManager.recomputePreviews(false)) {
- // Zoom when syncing modes
- mZoomFitNextImage = true;
- ensureZoomed();
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvasViewer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvasViewer.java
deleted file mode 100644
index e349a1cb0..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvasViewer.java
+++ /dev/null
@@ -1,165 +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 com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-
-import org.eclipse.core.runtime.ListenerList;
-import org.eclipse.jface.util.SafeRunnable;
-import org.eclipse.jface.viewers.IPostSelectionProvider;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.ISelectionProvider;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-
-/**
- * JFace {@link Viewer} wrapper around {@link LayoutCanvas}.
- * <p/>
- * The viewer is owned by {@link GraphicalEditorPart}.
- * <p/>
- * The viewer is an {@link ISelectionProvider} instance and is set as the
- * site's main {@link ISelectionProvider} by the editor part. Consequently
- * canvas' selection changes are broadcasted to anyone listening, which includes
- * the part itself as well as the associated outline and property sheet pages.
- */
-class LayoutCanvasViewer extends Viewer implements IPostSelectionProvider {
-
- private LayoutCanvas mCanvas;
- private final LayoutEditorDelegate mEditorDelegate;
-
- public LayoutCanvasViewer(LayoutEditorDelegate editorDelegate,
- RulesEngine rulesEngine,
- Composite parent,
- int style) {
- mEditorDelegate = editorDelegate;
- mCanvas = new LayoutCanvas(editorDelegate, rulesEngine, parent, style);
-
- mCanvas.getSelectionManager().addSelectionChangedListener(mSelectionListener);
- }
-
- private ISelectionChangedListener mSelectionListener = new ISelectionChangedListener() {
- @Override
- public void selectionChanged(SelectionChangedEvent event) {
- fireSelectionChanged(event);
- firePostSelectionChanged(event);
- }
- };
-
- @Override
- public Control getControl() {
- return mCanvas;
- }
-
- /**
- * Returns the underlying {@link LayoutCanvas}.
- * This is the same control as returned by {@link #getControl()} but clients
- * have it already casted in the right type.
- * <p/>
- * This can never be null.
- * @return The underlying {@link LayoutCanvas}.
- */
- public LayoutCanvas getCanvas() {
- return mCanvas;
- }
-
- /**
- * Returns the current layout editor's input.
- */
- @Override
- public Object getInput() {
- return mEditorDelegate.getEditor().getEditorInput();
- }
-
- /**
- * Unused. We don't support switching the input.
- */
- @Override
- public void setInput(Object input) {
- }
-
- /**
- * Returns a new {@link TreeSelection} where each {@link TreePath} item
- * is a {@link CanvasViewInfo}.
- */
- @Override
- public ISelection getSelection() {
- return mCanvas.getSelectionManager().getSelection();
- }
-
- /**
- * Sets a new selection. <code>reveal</code> is ignored right now.
- * <p/>
- * The selection can be null, which is interpreted as an empty selection.
- */
- @Override
- public void setSelection(ISelection selection, boolean reveal) {
- if (mEditorDelegate.getEditor().getIgnoreXmlUpdate()) {
- return;
- }
- mCanvas.getSelectionManager().setSelection(selection);
- }
-
- /** Unused. Refreshing is done solely by the owning {@link LayoutEditorDelegate}. */
- @Override
- public void refresh() {
- // ignore
- }
-
- public void dispose() {
- if (mSelectionListener != null) {
- mCanvas.getSelectionManager().removeSelectionChangedListener(mSelectionListener);
- }
- if (mCanvas != null) {
- mCanvas.dispose();
- mCanvas = null;
- }
- }
-
- // ---- Implements IPostSelectionProvider ----
-
- private ListenerList mPostChangedListeners = new ListenerList();
-
- @Override
- public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
- mPostChangedListeners.add(listener);
- }
-
- @Override
- public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
- mPostChangedListeners.remove(listener);
- }
-
- protected void firePostSelectionChanged(final SelectionChangedEvent event) {
- Object[] listeners = mPostChangedListeners.getListeners();
- for (int i = 0; i < listeners.length; i++) {
- final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
- SafeRunnable.run(new SafeRunnable() {
- @Override
- public void run() {
- l.selectionChanged(event);
- }
- });
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java
deleted file mode 100644
index b79e3b0a1..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutMetadata.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2011 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_LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_NUM_COLUMNS;
-import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
-import static com.android.SdkConstants.GRID_VIEW;
-import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
-import static com.android.SdkConstants.TOOLS_URI;
-import static com.android.SdkConstants.VALUE_AUTO_FIT;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.api.AdapterBinding;
-import com.android.ide.common.rendering.api.DataBindingItem;
-import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.progress.WorkbenchJob;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xmlpull.v1.XmlPullParser;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Design-time metadata lookup for layouts, such as fragment and AdapterView bindings.
- */
-public class LayoutMetadata {
- /** The default layout to use for list items in expandable list views */
- public static final String DEFAULT_EXPANDABLE_LIST_ITEM = "simple_expandable_list_item_2"; //$NON-NLS-1$
- /** The default layout to use for list items in plain list views */
- public static final String DEFAULT_LIST_ITEM = "simple_list_item_2"; //$NON-NLS-1$
- /** The default layout to use for list items in spinners */
- public static final String DEFAULT_SPINNER_ITEM = "simple_spinner_item"; //$NON-NLS-1$
-
- /** The string to start metadata comments with */
- private static final String COMMENT_PROLOGUE = " Preview: ";
- /** The property key, included in comments, which references a list item layout */
- public static final String KEY_LV_ITEM = "listitem"; //$NON-NLS-1$
- /** The property key, included in comments, which references a list header layout */
- public static final String KEY_LV_HEADER = "listheader"; //$NON-NLS-1$
- /** The property key, included in comments, which references a list footer layout */
- public static final String KEY_LV_FOOTER = "listfooter"; //$NON-NLS-1$
- /** The property key, included in comments, which references a fragment layout to show */
- public static final String KEY_FRAGMENT_LAYOUT = "layout"; //$NON-NLS-1$
- // NOTE: If you add additional keys related to resources, make sure you update the
- // ResourceRenameParticipant
-
- /** Utility class, do not create instances */
- private LayoutMetadata() {
- }
-
- /**
- * Returns the given property specified in the <b>current</b> element being
- * processed by the given pull parser.
- *
- * @param parser the pull parser, which must be in the middle of processing
- * the target element
- * @param name the property name to look up
- * @return the property value, or null if not defined
- */
- @Nullable
- public static String getProperty(@NonNull XmlPullParser parser, @NonNull String name) {
- String value = parser.getAttributeValue(TOOLS_URI, name);
- if (value != null && value.isEmpty()) {
- value = null;
- }
-
- return value;
- }
-
- /**
- * Clears the old metadata from the given node
- *
- * @param node the XML node to associate metadata with
- * @deprecated this method clears metadata using the old comment-based style;
- * should only be used for migration at this point
- */
- @Deprecated
- public static void clearLegacyComment(Node node) {
- NodeList children = node.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- Node child = children.item(i);
- if (child.getNodeType() == Node.COMMENT_NODE) {
- String text = child.getNodeValue();
- if (text.startsWith(COMMENT_PROLOGUE)) {
- Node commentNode = child;
- // Remove the comment, along with surrounding whitespace if applicable
- Node previous = commentNode.getPreviousSibling();
- if (previous != null && previous.getNodeType() == Node.TEXT_NODE) {
- if (previous.getNodeValue().trim().length() == 0) {
- node.removeChild(previous);
- }
- }
- node.removeChild(commentNode);
- Node first = node.getFirstChild();
- if (first != null && first.getNextSibling() == null
- && first.getNodeType() == Node.TEXT_NODE) {
- if (first.getNodeValue().trim().length() == 0) {
- node.removeChild(first);
- }
- }
- }
- }
- }
- }
-
- /**
- * Returns the given property of the given DOM node, or null
- *
- * @param node the XML node to associate metadata with
- * @param name the name of the property to look up
- * @return the value stored with the given node and name, or null
- */
- @Nullable
- public static String getProperty(
- @NonNull Node node,
- @NonNull String name) {
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- Element element = (Element) node;
- String value = element.getAttributeNS(TOOLS_URI, name);
- if (value != null && value.isEmpty()) {
- value = null;
- }
-
- return value;
- }
-
- return null;
- }
-
- /**
- * Sets the given property of the given DOM node to a given value, or if null clears
- * the property.
- *
- * @param editor the editor associated with the property
- * @param node the XML node to associate metadata with
- * @param name the name of the property to set
- * @param value the value to store for the given node and name, or null to remove it
- */
- public static void setProperty(
- @NonNull final AndroidXmlEditor editor,
- @NonNull final Node node,
- @NonNull final String name,
- @Nullable final String value) {
- // Clear out the old metadata
- clearLegacyComment(node);
-
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- final Element element = (Element) node;
- final String undoLabel = "Bind View";
- AdtUtils.setToolsAttribute(editor, element, undoLabel, name, value,
- false /*reveal*/, false /*append*/);
-
- // Also apply the same layout to any corresponding elements in other configurations
- // of this layout.
- final IFile file = editor.getInputFile();
- if (file != null) {
- final List<IFile> variations = AdtUtils.getResourceVariations(file, false);
- if (variations.isEmpty()) {
- return;
- }
- Display display = AdtPlugin.getDisplay();
- WorkbenchJob job = new WorkbenchJob(display, "Update alternate views") {
- @Override
- public IStatus runInUIThread(IProgressMonitor monitor) {
- for (IFile variation : variations) {
- if (variation.equals(file)) {
- continue;
- }
- try {
- // If the corresponding file is open in the IDE, use the
- // editor version instead
- if (!AdtPrefs.getPrefs().isSharedLayoutEditor()) {
- if (setPropertyInEditor(undoLabel, variation, element, name,
- value)) {
- return Status.OK_STATUS;
- }
- }
-
- boolean old = editor.getIgnoreXmlUpdate();
- try {
- editor.setIgnoreXmlUpdate(true);
- setPropertyInFile(undoLabel, variation, element, name, value);
- } finally {
- editor.setIgnoreXmlUpdate(old);
- }
- } catch (Exception e) {
- AdtPlugin.log(e, variation.getFullPath().toOSString());
- }
- }
- return Status.OK_STATUS;
- }
-
- };
- job.setSystem(true);
- job.schedule();
- }
- }
- }
-
- private static boolean setPropertyInEditor(
- @NonNull String undoLabel,
- @NonNull IFile variation,
- @NonNull final Element equivalentElement,
- @NonNull final String name,
- @Nullable final String value) {
- Collection<IEditorPart> editors =
- AdtUtils.findEditorsFor(variation, false /*restore*/);
- for (IEditorPart part : editors) {
- AndroidXmlEditor editor = AdtUtils.getXmlEditor(part);
- if (editor != null) {
- Document doc = DomUtilities.getDocument(editor);
- if (doc != null) {
- Element element = DomUtilities.findCorresponding(equivalentElement, doc);
- if (element != null) {
- AdtUtils.setToolsAttribute(editor, element, undoLabel, name,
- value, false /*reveal*/, false /*append*/);
- if (part instanceof GraphicalEditorPart) {
- GraphicalEditorPart g = (GraphicalEditorPart) part;
- g.recomputeLayout();
- g.getCanvasControl().redraw();
- }
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- private static boolean setPropertyInFile(
- @NonNull String undoLabel,
- @NonNull IFile variation,
- @NonNull final Element element,
- @NonNull final String name,
- @Nullable final String value) {
- Document doc = DomUtilities.getDocument(variation);
- if (doc != null && element.getOwnerDocument() != doc) {
- Element other = DomUtilities.findCorresponding(element, doc);
- if (other != null) {
- AdtUtils.setToolsAttribute(variation, other, undoLabel,
- name, value, false);
-
- return true;
- }
- }
-
- return false;
- }
-
- /** Strips out @layout/ or @android:layout/ from the given layout reference */
- private static String stripLayoutPrefix(String layout) {
- if (layout.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX)) {
- layout = layout.substring(ANDROID_LAYOUT_RESOURCE_PREFIX.length());
- } else if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) {
- layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length());
- }
-
- return layout;
- }
-
- /**
- * Creates an {@link AdapterBinding} for the given view object, or null if the user
- * has not yet chosen a target layout to use for the given AdapterView.
- *
- * @param viewObject the view object to create an adapter binding for
- * @param map a map containing tools attribute metadata
- * @return a binding, or null
- */
- @Nullable
- public static AdapterBinding getNodeBinding(
- @Nullable Object viewObject,
- @NonNull Map<String, String> map) {
- String header = map.get(KEY_LV_HEADER);
- String footer = map.get(KEY_LV_FOOTER);
- String layout = map.get(KEY_LV_ITEM);
- if (layout != null || header != null || footer != null) {
- int count = 12;
- return getNodeBinding(viewObject, header, footer, layout, count);
- }
-
- return null;
- }
-
- /**
- * Creates an {@link AdapterBinding} for the given view object, or null if the user
- * has not yet chosen a target layout to use for the given AdapterView.
- *
- * @param viewObject the view object to create an adapter binding for
- * @param uiNode the ui node corresponding to the view object
- * @return a binding, or null
- */
- @Nullable
- public static AdapterBinding getNodeBinding(
- @Nullable Object viewObject,
- @NonNull UiViewElementNode uiNode) {
- Node xmlNode = uiNode.getXmlNode();
-
- String header = getProperty(xmlNode, KEY_LV_HEADER);
- String footer = getProperty(xmlNode, KEY_LV_FOOTER);
- String layout = getProperty(xmlNode, KEY_LV_ITEM);
- if (layout != null || header != null || footer != null) {
- int count = 12;
- // If we're dealing with a grid view, multiply the list item count
- // by the number of columns to ensure we have enough items
- if (xmlNode instanceof Element && xmlNode.getNodeName().endsWith(GRID_VIEW)) {
- Element element = (Element) xmlNode;
- String columns = element.getAttributeNS(ANDROID_URI, ATTR_NUM_COLUMNS);
- int multiplier = 2;
- if (columns != null && columns.length() > 0 &&
- !columns.equals(VALUE_AUTO_FIT)) {
- try {
- int c = Integer.parseInt(columns);
- if (c >= 1 && c <= 10) {
- multiplier = c;
- }
- } catch (NumberFormatException nufe) {
- // some unexpected numColumns value: just stick with 2 columns for
- // preview purposes
- }
- }
- count *= multiplier;
- }
-
- return getNodeBinding(viewObject, header, footer, layout, count);
- }
-
- return null;
- }
-
- private static AdapterBinding getNodeBinding(Object viewObject,
- String header, String footer, String layout, int count) {
- if (layout != null || header != null || footer != null) {
- AdapterBinding binding = new AdapterBinding(count);
-
- if (header != null) {
- boolean isFramework = header.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX);
- binding.addHeader(new ResourceReference(stripLayoutPrefix(header),
- isFramework));
- }
-
- if (footer != null) {
- boolean isFramework = footer.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX);
- binding.addFooter(new ResourceReference(stripLayoutPrefix(footer),
- isFramework));
- }
-
- if (layout != null) {
- boolean isFramework = layout.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX);
- if (isFramework) {
- layout = layout.substring(ANDROID_LAYOUT_RESOURCE_PREFIX.length());
- } else if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) {
- layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length());
- }
-
- binding.addItem(new DataBindingItem(layout, isFramework, 1));
- } else if (viewObject != null) {
- String listFqcn = ProjectCallback.getListAdapterViewFqcn(viewObject.getClass());
- if (listFqcn != null) {
- if (listFqcn.endsWith(EXPANDABLE_LIST_VIEW)) {
- binding.addItem(
- new DataBindingItem(DEFAULT_EXPANDABLE_LIST_ITEM,
- true /* isFramework */, 1));
- } else {
- binding.addItem(
- new DataBindingItem(DEFAULT_LIST_ITEM,
- true /* isFramework */, 1));
- }
- }
- } else {
- binding.addItem(
- new DataBindingItem(DEFAULT_LIST_ITEM,
- true /* isFramework */, 1));
- }
- return binding;
- }
-
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPoint.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPoint.java
deleted file mode 100644
index 818b2c4ef..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPoint.java
+++ /dev/null
@@ -1,156 +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 com.android.ide.common.api.Point;
-
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-
-/**
- * A {@link LayoutPoint} is a coordinate in the Android canvas (in other words,
- * it may differ from the canvas control mouse coordinate because the canvas may
- * be zoomed and scrolled.)
- */
-public final class LayoutPoint {
- /** Containing canvas which the point is relative to. */
- private final LayoutCanvas mCanvas;
-
- /** The X coordinate of the canvas coordinate. */
- public final int x;
-
- /** The Y coordinate of the canvas coordinate. */
- public final int y;
-
- /**
- * Constructs a new {@link LayoutPoint} from the given event. The event
- * must be from a {@link MouseListener} associated with the
- * {@link LayoutCanvas} such that the {@link MouseEvent#x} and
- * {@link MouseEvent#y} fields are relative to the canvas.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The mouse event to construct the {@link LayoutPoint}
- * from.
- * @return A {@link LayoutPoint} which corresponds to the given
- * {@link MouseEvent}.
- */
- public static LayoutPoint create(LayoutCanvas canvas, MouseEvent event) {
- // The mouse event coordinates should already be relative to the canvas
- // widget.
- assert event.widget == canvas : event.widget;
- return ControlPoint.create(canvas, event).toLayout();
- }
-
- /**
- * Constructs a new {@link LayoutPoint} from the given event. The event
- * must be from a {@link DragSourceListener} associated with the
- * {@link LayoutCanvas} such that the {@link DragSourceEvent#x} and
- * {@link DragSourceEvent#y} fields are relative to the canvas.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param event The mouse event to construct the {@link LayoutPoint}
- * from.
- * @return A {@link LayoutPoint} which corresponds to the given
- * {@link DragSourceEvent}.
- */
- public static LayoutPoint create(LayoutCanvas canvas, DragSourceEvent event) {
- // The drag source event coordinates should already be relative to the
- // canvas widget.
- return ControlPoint.create(canvas, event).toLayout();
- }
-
- /**
- * Constructs a new {@link LayoutPoint} from the given x,y coordinates.
- *
- * @param canvas The {@link LayoutCanvas} this point is within.
- * @param x The mouse event x coordinate relative to the canvas
- * @param y The mouse event x coordinate relative to the canvas
- * @return A {@link LayoutPoint} which corresponds to the given
- * layout coordinates.
- */
- public static LayoutPoint create(LayoutCanvas canvas, int x, int y) {
- return new LayoutPoint(canvas, x, y);
- }
-
- /**
- * Constructs a new {@link LayoutPoint} with the given X and Y coordinates.
- *
- * @param canvas The canvas which contains this coordinate
- * @param x The canvas X coordinate
- * @param y The canvas Y coordinate
- */
- private LayoutPoint(LayoutCanvas canvas, int x, int y) {
- mCanvas = canvas;
- this.x = x;
- this.y = y;
- }
-
- /**
- * Returns the equivalent {@link ControlPoint} to this
- * {@link LayoutPoint}.
- *
- * @return The equivalent {@link ControlPoint} to this
- * {@link LayoutPoint}
- */
- public ControlPoint toControl() {
- int cx = mCanvas.getHorizontalTransform().translate(x);
- int cy = mCanvas.getVerticalTransform().translate(y);
-
- return ControlPoint.create(mCanvas, cx, cy);
- }
-
- /**
- * Returns this {@link LayoutPoint} as a {@link Point}, in the same coordinate space.
- *
- * @return a new {@link Point} in the same coordinate space
- */
- public Point toPoint() {
- return new Point(x, y);
- }
-
- @Override
- public String toString() {
- return "LayoutPoint [x=" + x + ", y=" + y + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + x;
- result = prime * result + y;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- LayoutPoint other = (LayoutPoint) obj;
- if (x != other.x)
- return false;
- if (y != other.y)
- return false;
- return true;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
deleted file mode 100644
index 56b86aa85..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2012 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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.google.common.collect.Maps;
-
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IEditorReference;
-import org.eclipse.ui.IPartListener2;
-import org.eclipse.ui.IPartService;
-import org.eclipse.ui.IViewReference;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.IWorkbenchPartReference;
-import org.eclipse.ui.IWorkbenchWindow;
-
-import java.util.Map;
-
-/**
- * The {@link LayoutWindowCoordinator} keeps track of Eclipse window events (opening, closing,
- * fronting, etc) and uses this information to manage the propertysheet and outline
- * views such that they are always(*) showing:
- * <ul>
- * <li> If the Property Sheet and Outline Eclipse views are showing, it does nothing.
- * "Showing" means "is open", not necessary "is visible", e.g. in a tabbed view
- * there could be a different view on top.
- * <li> If just the outline is showing, then the property sheet is shown in a sashed
- * pane below or to the right of the outline (depending on the dominant dimension
- * of the window).
- * <li> TBD: If just the property sheet is showing, should the outline be showed
- * inside that window? Not yet done.
- * <li> If the outline is *not* showing, then the outline is instead shown
- * <b>inside</b> the editor area, in a right-docked view! This right docked view
- * also includes the property sheet!
- * <li> If the property sheet is not showing (which includes not showing in the outline
- * view as well), then it will be shown inside the editor area, along with the outline
- * which should also be there (since if the outline was showing outside the editor
- * area, the property sheet would have docked there).
- * <li> When the editor is maximized, then all views are temporarily hidden. In this
- * case, the property sheet and outline will show up inside the editor.
- * When the editor view is un-maximized, the view state will return to what it
- * was before.
- * </ul>
- * </p>
- * There is one coordinator per workbench window, shared between all editors in that window.
- * <p>
- * TODO: Rename this class to AdtWindowCoordinator. It is used for more than just layout
- * window coordination now. For example, it's also used to dispatch {@code activated()} and
- * {@code deactivated()} events to all the XML editors, to ensure that key bindings are
- * properly dispatched to the right editors in Eclipse 4.x.
- */
-public class LayoutWindowCoordinator implements IPartListener2 {
- static final String PROPERTY_SHEET_PART_ID = "org.eclipse.ui.views.PropertySheet"; //$NON-NLS-1$
- static final String OUTLINE_PART_ID = "org.eclipse.ui.views.ContentOutline"; //$NON-NLS-1$
- /** The workbench window */
- private final IWorkbenchWindow mWindow;
- /** Is the Eclipse property sheet ViewPart open? */
- private boolean mPropertiesOpen;
- /** Is the Eclipse outline ViewPart open? */
- private boolean mOutlineOpen;
- /** Is the editor maximized? */
- private boolean mEditorMaximized;
- /**
- * Has the coordinator been initialized? We may have to delay initialization
- * and perform it lazily if the workbench window does not have an active
- * page when the coordinator is first started
- */
- private boolean mInitialized;
-
- /** Map from workbench windows to each layout window coordinator instance for that window */
- private static Map<IWorkbenchWindow, LayoutWindowCoordinator> sCoordinators =
- Maps.newHashMapWithExpectedSize(2);
-
- /**
- * Returns the coordinator for the given window.
- *
- * @param window the associated window
- * @param create whether to create the window if it does not already exist
- * @return the new coordinator, never null if {@code create} is true
- */
- @Nullable
- public static LayoutWindowCoordinator get(@NonNull IWorkbenchWindow window, boolean create) {
- synchronized (LayoutWindowCoordinator.class){
- LayoutWindowCoordinator coordinator = sCoordinators.get(window);
- if (coordinator == null && create) {
- coordinator = new LayoutWindowCoordinator(window);
-
- IPartService service = window.getPartService();
- if (service != null) {
- // What if the editor part is *already* open? How do I deal with that?
- service.addPartListener(coordinator);
- }
-
- sCoordinators.put(window, coordinator);
- }
-
- return coordinator;
- }
- }
-
-
- /** Disposes this coordinator (when a window is closed) */
- public void dispose() {
- IPartService service = mWindow.getPartService();
- if (service != null) {
- service.removePartListener(this);
- }
-
- synchronized (LayoutWindowCoordinator.class){
- sCoordinators.remove(mWindow);
- }
- }
-
- /**
- * Returns true if the main editor window is maximized
- *
- * @return true if the main editor window is maximized
- */
- public boolean isEditorMaximized() {
- return mEditorMaximized;
- }
-
- private LayoutWindowCoordinator(@NonNull IWorkbenchWindow window) {
- mWindow = window;
-
- initialize();
- }
-
- private void initialize() {
- if (mInitialized) {
- return;
- }
-
- IWorkbenchPage activePage = mWindow.getActivePage();
- if (activePage == null) {
- return;
- }
-
- mInitialized = true;
-
- // Look up current state of the properties and outline windows (in case
- // they have already been opened before we added our part listener)
- IViewReference ref = findPropertySheetView(activePage);
- if (ref != null) {
- IWorkbenchPart part = ref.getPart(false /*restore*/);
- if (activePage.isPartVisible(part)) {
- mPropertiesOpen = true;
- }
- }
- ref = findOutlineView(activePage);
- if (ref != null) {
- IWorkbenchPart part = ref.getPart(false /*restore*/);
- if (activePage.isPartVisible(part)) {
- mOutlineOpen = true;
- }
- }
- if (!syncMaximizedState(activePage)) {
- syncActive();
- }
- }
-
- static IViewReference findPropertySheetView(IWorkbenchPage activePage) {
- return activePage.findViewReference(PROPERTY_SHEET_PART_ID);
- }
-
- static IViewReference findOutlineView(IWorkbenchPage activePage) {
- return activePage.findViewReference(OUTLINE_PART_ID);
- }
-
- /**
- * Checks the maximized state of the page and updates internal state if
- * necessary.
- * <p>
- * This is used in Eclipse 4.x, where the {@link IPartListener2} does not
- * fire {@link IPartListener2#partHidden(IWorkbenchPartReference)} when the
- * editor is maximized anymore (see issue
- * https://bugs.eclipse.org/bugs/show_bug.cgi?id=382120 for details).
- * Instead, the layout editor listens for resize events, and upon resize it
- * looks up the part state and calls this method to ensure that the right
- * maximized state is known to the layout coordinator.
- *
- * @param page the active workbench page
- * @return true if the state changed, false otherwise
- */
- public boolean syncMaximizedState(IWorkbenchPage page) {
- boolean maximized = isPageZoomed(page);
- if (mEditorMaximized != maximized) {
- mEditorMaximized = maximized;
- syncActive();
- return true;
- }
- return false;
- }
-
- private boolean isPageZoomed(IWorkbenchPage page) {
- IWorkbenchPartReference reference = page.getActivePartReference();
- if (reference != null && reference instanceof IEditorReference) {
- int state = page.getPartState(reference);
- boolean maximized = (state & IWorkbenchPage.STATE_MAXIMIZED) != 0;
- return maximized;
- }
-
- // If the active reference isn't the editor, then the editor can't be maximized
- return false;
- }
-
- /**
- * Syncs the given editor's view state such that the property sheet and or
- * outline are shown or hidden according to the visibility of the global
- * outline and property sheet views.
- * <p>
- * This is typically done when a layout editor is fronted. For view updates
- * when the view is already showing, the {@link LayoutWindowCoordinator}
- * will automatically handle the current fronted window.
- *
- * @param editor the editor to sync
- */
- private void sync(@Nullable GraphicalEditorPart editor) {
- if (editor == null) {
- return;
- }
- if (mEditorMaximized) {
- editor.showStructureViews(true /*outline*/, true /*properties*/, true /*layout*/);
- } else if (mOutlineOpen) {
- editor.showStructureViews(false /*outline*/, false /*properties*/, true /*layout*/);
- editor.getCanvasControl().getOutlinePage().setShowPropertySheet(!mPropertiesOpen);
- } else {
- editor.showStructureViews(true /*outline*/, !mPropertiesOpen /*properties*/,
- true /*layout*/);
- }
- }
-
- private void sync(IWorkbenchPart part) {
- if (part instanceof AndroidXmlEditor) {
- LayoutEditorDelegate editor = LayoutEditorDelegate.fromEditor((IEditorPart) part);
- if (editor != null) {
- sync(editor.getGraphicalEditor());
- }
- }
- }
-
- private void syncActive() {
- IWorkbenchPage activePage = mWindow.getActivePage();
- if (activePage != null) {
- IEditorPart editor = activePage.getActiveEditor();
- sync(editor);
- }
- }
-
- private void propertySheetClosed() {
- mPropertiesOpen = false;
- syncActive();
- }
-
- private void propertySheetOpened() {
- mPropertiesOpen = true;
- syncActive();
- }
-
- private void outlineClosed() {
- mOutlineOpen = false;
- syncActive();
- }
-
- private void outlineOpened() {
- mOutlineOpen = true;
- syncActive();
- }
-
- // ---- Implements IPartListener2 ----
-
- @Override
- public void partOpened(IWorkbenchPartReference partRef) {
- // We ignore partOpened() and partClosed() because these methods are only
- // called when a view is opened in the first perspective, and closed in the
- // last perspective. The outline is typically used in multiple perspectives,
- // so closing it in the Java perspective does *not* fire a partClosed event.
- // There is no notification for "part closed in perspective" (see issue
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=54559 for details).
- // However, the workaround we can use is to listen to partVisible() and
- // partHidden(). These will be called more often than we'd like (e.g.
- // when the tab order causes a view to be obscured), however, we can use
- // the workaround of looking up IWorkbenchPage.findViewReference(id) after
- // partHidden(), which will return null if the view is closed in the current
- // perspective. For partOpened, we simply look in partVisible() for whether
- // our flags tracking the view state have been initialized already.
- }
-
- @Override
- public void partClosed(IWorkbenchPartReference partRef) {
- // partClosed() doesn't get called when a window is closed unless it has
- // been closed in *all* perspectives. See partOpened() for more.
- }
-
- @Override
- public void partHidden(IWorkbenchPartReference partRef) {
- IWorkbenchPage activePage = mWindow.getActivePage();
- if (activePage == null) {
- return;
- }
- initialize();
-
- // See if this looks like the window was closed in this workspace
- // See partOpened() for an explanation.
- String id = partRef.getId();
- if (PROPERTY_SHEET_PART_ID.equals(id)) {
- if (activePage.findViewReference(id) == null) {
- propertySheetClosed();
- return;
- }
- } else if (OUTLINE_PART_ID.equals(id)) {
- if (activePage.findViewReference(id) == null) {
- outlineClosed();
- return;
- }
- }
-
- // Does this look like a window getting maximized?
- syncMaximizedState(activePage);
- }
-
- @Override
- public void partVisible(IWorkbenchPartReference partRef) {
- IWorkbenchPage activePage = mWindow.getActivePage();
- if (activePage == null) {
- return;
- }
- initialize();
-
- String id = partRef.getId();
- if (mEditorMaximized) {
- // Return to their non-maximized state
- mEditorMaximized = false;
- syncActive();
- }
-
- IWorkbenchPart part = partRef.getPart(false /*restore*/);
- sync(part);
-
- // See partOpened() for an explanation
- if (PROPERTY_SHEET_PART_ID.equals(id)) {
- if (!mPropertiesOpen) {
- propertySheetOpened();
- assert mPropertiesOpen;
- }
- } else if (OUTLINE_PART_ID.equals(id)) {
- if (!mOutlineOpen) {
- outlineOpened();
- assert mOutlineOpen;
- }
- }
- }
-
- @Override
- public void partInputChanged(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partActivated(IWorkbenchPartReference partRef) {
- IWorkbenchPart part = partRef.getPart(false);
- if (part instanceof AndroidXmlEditor) {
- ((AndroidXmlEditor)part).activated();
- }
- }
-
- @Override
- public void partBroughtToTop(IWorkbenchPartReference partRef) {
- }
-
- @Override
- public void partDeactivated(IWorkbenchPartReference partRef) {
- IWorkbenchPart part = partRef.getPart(false);
- if (part instanceof AndroidXmlEditor) {
- ((AndroidXmlEditor)part).deactivated();
- }
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintOverlay.java
deleted file mode 100644
index ca74493e8..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintOverlay.java
+++ /dev/null
@@ -1,140 +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 com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.google.common.collect.Lists;
-
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Rectangle;
-import org.w3c.dom.Node;
-
-import java.util.Collection;
-
-/**
- * The {@link LintOverlay} paints an icon over each view that contains at least one
- * lint error (unless the view is smaller than the icon)
- */
-public class LintOverlay extends Overlay {
- /** Approximate size of lint overlay icons */
- static final int ICON_SIZE = 8;
- /** Alpha to draw lint overlay icons with */
- private static final int ALPHA = 192;
-
- private final LayoutCanvas mCanvas;
- private Image mWarningImage;
- private Image mErrorImage;
-
- /**
- * Constructs a new {@link LintOverlay}
- *
- * @param canvas the associated canvas
- */
- public LintOverlay(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- @Override
- public boolean isHiding() {
- return super.isHiding() || !AdtPrefs.getPrefs().isLintOnSave();
- }
-
- @Override
- public void paint(GC gc) {
- LayoutEditorDelegate editor = mCanvas.getEditorDelegate();
- Collection<Node> nodes = editor.getLintNodes();
- if (nodes != null && !nodes.isEmpty()) {
- // Copy list before iterating through it to avoid a concurrent list modification
- // in case lint runs in the background while painting and updates this list
- nodes = Lists.newArrayList(nodes);
- ViewHierarchy hierarchy = mCanvas.getViewHierarchy();
- Image icon = getWarningIcon();
- ImageData imageData = icon.getImageData();
- int iconWidth = imageData.width;
- int iconHeight = imageData.height;
- CanvasTransform mHScale = mCanvas.getHorizontalTransform();
- CanvasTransform mVScale = mCanvas.getVerticalTransform();
-
- // Right/bottom edges of the canvas image; don't paint overlays outside of
- // that. (With for example RelativeLayouts with margins rendered on smaller
- // screens than they are intended for this can happen.)
- int maxX = mHScale.translate(0) + mHScale.getScaledImgSize();
- int maxY = mVScale.translate(0) + mVScale.getScaledImgSize();
-
- int oldAlpha = gc.getAlpha();
- try {
- gc.setAlpha(ALPHA);
- for (Node node : nodes) {
- CanvasViewInfo vi = hierarchy.findViewInfoFor(node);
- if (vi != null) {
- Rectangle bounds = vi.getAbsRect();
- int x = mHScale.translate(bounds.x);
- int y = mVScale.translate(bounds.y);
- int w = mHScale.scale(bounds.width);
- int h = mVScale.scale(bounds.height);
- if (w < iconWidth || h < iconHeight) {
- // Don't draw badges on tiny widgets (including those
- // that aren't tiny but are zoomed out too far)
- continue;
- }
-
- x += w - iconWidth;
- y += h - iconHeight;
-
- if (x > maxX || y > maxY) {
- continue;
- }
-
- boolean isError = false;
- IMarker marker = editor.getIssueForNode(vi.getUiViewNode());
- if (marker != null) {
- int severity = marker.getAttribute(IMarker.SEVERITY, 0);
- isError = severity == IMarker.SEVERITY_ERROR;
- }
-
- icon = isError ? getErrorIcon() : getWarningIcon();
-
- gc.drawImage(icon, x, y);
- }
- }
- } finally {
- gc.setAlpha(oldAlpha);
- }
- }
- }
-
- private Image getWarningIcon() {
- if (mWarningImage == null) {
- mWarningImage = IconFactory.getInstance().getIcon("warning-badge"); //$NON-NLS-1$
- }
-
- return mWarningImage;
- }
-
- private Image getErrorIcon() {
- if (mErrorImage == null) {
- mErrorImage = IconFactory.getInstance().getIcon("error-badge"); //$NON-NLS-1$
- }
-
- return mErrorImage;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltip.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltip.java
deleted file mode 100644
index cedd43659..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltip.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2012 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.ATTR_ID;
-
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-
-import org.eclipse.core.resources.IMarker;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-
-import java.util.List;
-
-/** Actual tooltip showing multiple lines for various widgets that have lint errors */
-class LintTooltip extends Shell {
- private final LayoutCanvas mCanvas;
- private final List<UiViewElementNode> mNodes;
-
- LintTooltip(LayoutCanvas canvas, List<UiViewElementNode> nodes) {
- super(canvas.getDisplay(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
- mCanvas = canvas;
- mNodes = nodes;
-
- createContents();
- }
-
- protected void createContents() {
- Display display = getDisplay();
- Color fg = display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
- Color bg = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
- setBackground(bg);
- GridLayout gridLayout = new GridLayout(2, false);
- setLayout(gridLayout);
-
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
-
- boolean first = true;
- for (UiViewElementNode node : mNodes) {
- IMarker marker = delegate.getIssueForNode(node);
- if (marker != null) {
- String message = marker.getAttribute(IMarker.MESSAGE, null);
- if (message != null) {
- Label icon = new Label(this, SWT.NONE);
- icon.setForeground(fg);
- icon.setBackground(bg);
- icon.setImage(node.getIcon());
-
- Label label = new Label(this, SWT.WRAP);
- if (first) {
- label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
- first = false;
- }
-
- String id = BaseLayoutRule.stripIdPrefix(node.getAttributeValue(ATTR_ID));
- if (id.isEmpty()) {
- if (node.getXmlNode() != null) {
- id = node.getXmlNode().getNodeName();
- } else {
- id = node.getDescriptor().getUiName();
- }
- }
-
- label.setText(String.format("%1$s: %2$s", id, message));
- }
- }
- }
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltipManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltipManager.java
deleted file mode 100644
index f71935889..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LintTooltipManager.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.gle2.LintOverlay.ICON_SIZE;
-
-import com.android.annotations.Nullable;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Shell;
-import org.w3c.dom.Node;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/** Tooltip in the layout editor showing lint errors under the cursor */
-class LintTooltipManager implements Listener {
- private final LayoutCanvas mCanvas;
- private Shell mTip = null;
- private List<UiViewElementNode> mShowingNodes;
-
- /**
- * 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).
- */
- LintTooltipManager(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- void register() {
- mCanvas.addListener(SWT.Dispose, this);
- mCanvas.addListener(SWT.KeyDown, this);
- mCanvas.addListener(SWT.MouseMove, this);
- mCanvas.addListener(SWT.MouseHover, this);
- }
-
- void unregister() {
- if (!mCanvas.isDisposed()) {
- mCanvas.removeListener(SWT.Dispose, this);
- mCanvas.removeListener(SWT.KeyDown, this);
- mCanvas.removeListener(SWT.MouseMove, this);
- mCanvas.removeListener(SWT.MouseHover, this);
- }
- }
-
- @Override
- public void handleEvent(Event event) {
- switch(event.type) {
- case SWT.MouseMove:
- // See if we're still overlapping this or *other* errors; if so, keep the
- // tip up (or update it).
- if (mShowingNodes != null) {
- List<UiViewElementNode> nodes = computeNodes(event);
- if (nodes != null && !nodes.isEmpty()) {
- if (nodes.equals(mShowingNodes)) {
- return;
- } else {
- show(nodes);
- }
- break;
- }
- }
-
- // If not, fall through and hide the tooltip
-
- //$FALL-THROUGH$
- case SWT.Dispose:
- case SWT.FocusOut:
- case SWT.KeyDown:
- case SWT.MouseExit:
- case SWT.MouseDown:
- hide();
- break;
- case SWT.MouseHover:
- hide();
- show(event);
- break;
- }
- }
-
- void hide() {
- if (mTip != null) {
- mTip.dispose();
- mTip = null;
- }
- mShowingNodes = null;
- }
-
- private void show(Event event) {
- List<UiViewElementNode> nodes = computeNodes(event);
- if (nodes != null && !nodes.isEmpty()) {
- show(nodes);
- }
- }
-
- /** Show a tooltip listing the lint errors for the given nodes */
- private void show(List<UiViewElementNode> nodes) {
- hide();
-
- if (!AdtPrefs.getPrefs().isLintOnSave()) {
- return;
- }
-
- mTip = new LintTooltip(mCanvas, nodes);
- Rectangle rect = mCanvas.getBounds();
- Point size = mTip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
- Point pos = mCanvas.toDisplay(rect.x, rect.y + rect.height);
- if (size.x > rect.width) {
- size = mTip.computeSize(rect.width, SWT.DEFAULT);
- }
- mTip.setBounds(pos.x, pos.y, size.x, size.y);
-
- mShowingNodes = nodes;
- mTip.setVisible(true);
- }
-
- /**
- * Compute the list of nodes which have lint warnings near the given mouse
- * coordinates
- *
- * @param event the mouse cursor event
- * @return a list of nodes, possibly empty
- */
- @Nullable
- private List<UiViewElementNode> computeNodes(Event event) {
- LayoutPoint p = ControlPoint.create(mCanvas, event.x, event.y).toLayout();
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- CanvasTransform mHScale = mCanvas.getHorizontalTransform();
- CanvasTransform mVScale = mCanvas.getVerticalTransform();
-
- int layoutIconSize = mHScale.inverseScale(ICON_SIZE);
- int slop = mVScale.inverseScale(10); // extra space around icon where tip triggers
-
- Collection<Node> xmlNodes = delegate.getLintNodes();
- if (xmlNodes == null) {
- return null;
- }
- List<UiViewElementNode> nodes = new ArrayList<UiViewElementNode>();
- for (Node xmlNode : xmlNodes) {
- CanvasViewInfo v = viewHierarchy.findViewInfoFor(xmlNode);
- if (v != null) {
- Rectangle b = v.getAbsRect();
- int x2 = b.x + b.width;
- int y2 = b.y + b.height;
- if (p.x < x2 - layoutIconSize - slop
- || p.x > x2 + slop
- || p.y < y2 - layoutIconSize - slop
- || p.y > y2 + slop) {
- continue;
- }
-
- nodes.add(v.getUiViewNode());
- }
- }
-
- return nodes;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java
deleted file mode 100644
index 4577f8d12..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ListViewTypeMenu.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2011 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_LAYOUT_RESOURCE_PREFIX;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_FOOTER;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_HEADER;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_LV_ITEM;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
-import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
-import com.android.resources.ResourceType;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ActionContributionItem;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.widgets.Menu;
-import org.w3c.dom.Node;
-
-/**
- * "Preview List Content" context menu which lists available data types and layouts
- * the user can choose to view the ListView as.
- */
-public class ListViewTypeMenu extends SubmenuAction {
- /** Associated canvas */
- private final LayoutCanvas mCanvas;
- /** When true, this menu is for a grid rather than a simple list */
- private boolean mGrid;
- /** When true, this menu is for a spinner rather than a simple list */
- private boolean mSpinner;
-
- /**
- * Creates a "Preview List Content" menu
- *
- * @param canvas associated canvas
- * @param isGrid whether the menu is for a grid rather than a list
- * @param isSpinner whether the menu is for a spinner rather than a list
- */
- public ListViewTypeMenu(LayoutCanvas canvas, boolean isGrid, boolean isSpinner) {
- super(isGrid ? "Preview Grid Content" : isSpinner ? "Preview Spinner Layout"
- : "Preview List Content");
- mCanvas = canvas;
- mGrid = isGrid;
- mSpinner = isSpinner;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- GraphicalEditorPart graphicalEditor = mCanvas.getEditorDelegate().getGraphicalEditor();
- if (graphicalEditor.renderingSupports(Capability.ADAPTER_BINDING)) {
- IAction action = new PickLayoutAction("Choose Layout...", KEY_LV_ITEM);
- new ActionContributionItem(action).fill(menu, -1);
- new Separator().fill(menu, -1);
-
- String selected = getSelectedLayout();
- if (selected != null) {
- if (selected.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX)) {
- selected = selected.substring(ANDROID_LAYOUT_RESOURCE_PREFIX.length());
- }
- }
-
- if (mSpinner) {
- action = new SetListTypeAction("Spinner Item",
- "simple_spinner_item", selected); //$NON-NLS-1$
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Spinner Dropdown Item",
- "simple_spinner_dropdown_item", selected); //$NON-NLS-1$
- new ActionContributionItem(action).fill(menu, -1);
- return;
- }
-
- action = new SetListTypeAction("Simple List Item",
- "simple_list_item_1", selected); //$NON-NLS-1$
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Simple 2-Line List Item",
- "simple_list_item_2", //$NON-NLS-1$
- selected);
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Checked List Item",
- "simple_list_item_checked", //$NON-NLS-1$
- selected);
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Single Choice List Item",
- "simple_list_item_single_choice", //$NON-NLS-1$
- selected);
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Multiple Choice List Item",
- "simple_list_item_multiple_choice", //$NON-NLS-1$
- selected);
- if (!mGrid) {
- new Separator().fill(menu, -1);
- action = new SetListTypeAction("Simple Expandable List Item",
- "simple_expandable_list_item_1", selected); //$NON-NLS-1$
- new ActionContributionItem(action).fill(menu, -1);
- action = new SetListTypeAction("Simple 2-Line Expandable List Item",
- "simple_expandable_list_item_2", //$NON-NLS-1$
- selected);
- new ActionContributionItem(action).fill(menu, -1);
-
- new Separator().fill(menu, -1);
- action = new PickLayoutAction("Choose Header...", KEY_LV_HEADER);
- new ActionContributionItem(action).fill(menu, -1);
- action = new PickLayoutAction("Choose Footer...", KEY_LV_FOOTER);
- new ActionContributionItem(action).fill(menu, -1);
- }
- } else {
- // Should we just hide the menu item instead?
- addDisabledMessageItem(
- "Not supported for this SDK version; try changing the Render Target");
- }
- }
-
- private class SetListTypeAction extends Action {
- private final String mLayout;
-
- public SetListTypeAction(String title, String layout, String selected) {
- super(title, IAction.AS_RADIO_BUTTON);
- mLayout = layout;
-
- if (layout.equals(selected)) {
- setChecked(true);
- }
- }
-
- @Override
- public void run() {
- if (isChecked()) {
- setNewType(KEY_LV_ITEM, ANDROID_LAYOUT_RESOURCE_PREFIX + mLayout);
- }
- }
- }
-
- /**
- * Action which brings up a resource chooser to choose an arbitrary layout as the
- * layout to be previewed in the list.
- */
- private class PickLayoutAction extends Action {
- private final String mType;
-
- public PickLayoutAction(String title, String type) {
- super(title, IAction.AS_PUSH_BUTTON);
- mType = type;
- }
-
- @Override
- public void run() {
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- IFile file = delegate.getEditor().getInputFile();
- GraphicalEditorPart editor = delegate.getGraphicalEditor();
- ResourceChooser dlg = ResourceChooser.create(editor, ResourceType.LAYOUT)
- .setInputValidator(CyclicDependencyValidator.create(file))
- .setInitialSize(85, 10)
- .setCurrentResource(getSelectedLayout());
- int result = dlg.open();
- if (result == ResourceChooser.CLEAR_RETURN_CODE) {
- setNewType(mType, null);
- } else if (result == Window.OK) {
- String newType = dlg.getCurrentResource();
- setNewType(mType, newType);
- }
- }
- }
-
- @Nullable
- private String getSelectedLayout() {
- String layout = null;
- SelectionManager selectionManager = mCanvas.getSelectionManager();
- for (SelectionItem item : selectionManager.getSelections()) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- Node xmlNode = node.getXmlNode();
- layout = LayoutMetadata.getProperty(xmlNode, KEY_LV_ITEM);
- if (layout != null) {
- return layout;
- }
- }
- }
-
- return null;
- }
-
- private void setNewType(@NonNull String type, @Nullable String layout) {
- LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
- GraphicalEditorPart graphicalEditor = delegate.getGraphicalEditor();
- SelectionManager selectionManager = mCanvas.getSelectionManager();
-
- for (SelectionItem item : selectionManager.getSnapshot()) {
- UiViewElementNode node = item.getViewInfo().getUiViewNode();
- if (node != null) {
- Node xmlNode = node.getXmlNode();
- LayoutMetadata.setProperty(delegate.getEditor(), xmlNode, type, layout);
- }
- }
-
- // Refresh
- graphicalEditor.recomputeLayout();
- mCanvas.redraw();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MarqueeGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MarqueeGesture.java
deleted file mode 100644
index 4cfd4fe3d..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MarqueeGesture.java
+++ /dev/null
@@ -1,160 +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 org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A {@link MarqueeGesture} is a gesture for swiping out a selection rectangle.
- * With a modifier key, items that intersect the rectangle can be toggled
- * instead of added to the new selection set.
- */
-public class MarqueeGesture extends Gesture {
- /** The {@link Overlay} drawn for the marquee. */
- private MarqueeOverlay mOverlay;
-
- /** The canvas associated with this gesture. */
- private LayoutCanvas mCanvas;
-
- /** A copy of the initial selection, when we're toggling the marquee. */
- private Collection<CanvasViewInfo> mInitialSelection;
-
- /**
- * Creates a new marquee selection (selection swiping).
- *
- * @param canvas The canvas where selection is performed.
- * @param toggle If true, toggle the membership of contained elements
- * instead of adding it.
- */
- public MarqueeGesture(LayoutCanvas canvas, boolean toggle) {
- mCanvas = canvas;
-
- if (toggle) {
- List<SelectionItem> selection = canvas.getSelectionManager().getSelections();
- mInitialSelection = new ArrayList<CanvasViewInfo>(selection.size());
- for (SelectionItem item : selection) {
- mInitialSelection.add(item.getViewInfo());
- }
- } else {
- mInitialSelection = Collections.emptySet();
- }
- }
-
- @Override
- public void update(ControlPoint pos) {
- if (mOverlay == null) {
- return;
- }
-
- int x = Math.min(pos.x, mStart.x);
- int y = Math.min(pos.y, mStart.y);
- int w = Math.abs(pos.x - mStart.x);
- int h = Math.abs(pos.y - mStart.y);
-
- mOverlay.updateSize(x, y, w, h);
-
- // Compute selection overlaps
- LayoutPoint topLeft = ControlPoint.create(mCanvas, x, y).toLayout();
- LayoutPoint bottomRight = ControlPoint.create(mCanvas, x + w, y + h).toLayout();
- mCanvas.getSelectionManager().selectWithin(topLeft, bottomRight, mInitialSelection);
- }
-
- @Override
- public List<Overlay> createOverlays() {
- mOverlay = new MarqueeOverlay();
- return Collections.<Overlay> singletonList(mOverlay);
- }
-
- /**
- * An {@link Overlay} for the {@link MarqueeGesture}; paints a selection
- * overlay rectangle matching the mouse coordinate delta between gesture
- * start and the current position.
- */
- private static class MarqueeOverlay extends Overlay {
- /** Rectangle border color. */
- private Color mStroke;
-
- /** Rectangle fill color. */
- private Color mFill;
-
- /** Current rectangle coordinates (in terms of control coordinates). */
- private Rectangle mRectangle = new Rectangle(0, 0, 0, 0);
-
- /** Alpha value of the fill. */
- private int mFillAlpha;
-
- /** Alpha value of the border. */
- private int mStrokeAlpha;
-
- /** Constructs a new {@link MarqueeOverlay}. */
- public MarqueeOverlay() {
- }
-
- /**
- * Updates the size of the marquee rectangle.
- *
- * @param x The top left corner of the rectangle, x coordinate.
- * @param y The top left corner of the rectangle, y coordinate.
- * @param w Rectangle width.
- * @param h Rectangle height.
- */
- public void updateSize(int x, int y, int w, int h) {
- mRectangle.x = x;
- mRectangle.y = y;
- mRectangle.width = w;
- mRectangle.height = h;
- }
-
- @Override
- public void create(Device device) {
- // TODO: Integrate DrawingStyles with this?
- mStroke = new Color(device, 255, 255, 255);
- mFill = new Color(device, 128, 128, 128);
- mFillAlpha = 64;
- mStrokeAlpha = 255;
- }
-
- @Override
- public void dispose() {
- mStroke.dispose();
- mFill.dispose();
- }
-
- @Override
- public void paint(GC gc) {
- if (mRectangle.width > 0 && mRectangle.height > 0) {
- gc.setLineStyle(SWT.LINE_SOLID);
- gc.setLineWidth(1);
- gc.setForeground(mStroke);
- gc.setBackground(mFill);
- gc.setAlpha(mStrokeAlpha);
- gc.drawRectangle(mRectangle);
- gc.setAlpha(mFillAlpha);
- gc.fillRectangle(mRectangle);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
deleted file mode 100644
index 7cf3a647a..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java
+++ /dev/null
@@ -1,852 +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 com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode.NodeCreationListener;
-
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.widgets.Display;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The Move gesture provides the operation for moving widgets around in the canvas.
- */
-public class MoveGesture extends DropGesture {
- /** The associated {@link LayoutCanvas}. */
- private LayoutCanvas mCanvas;
-
- /** Overlay which paints the drag &amp; drop feedback. */
- private MoveOverlay mOverlay;
-
- private static final boolean DEBUG = false;
-
- /**
- * The top view right under the drag'n'drop cursor.
- * This can only be null during a drag'n'drop when there is no view under the cursor
- * or after the state was all cleared.
- */
- private CanvasViewInfo mCurrentView;
-
- /**
- * The elements currently being dragged. This will always be non-null for a valid
- * drag'n'drop that happens within the same instance of Eclipse.
- * <p/>
- * In the event that the drag and drop happens between different instances of Eclipse
- * this will remain null.
- */
- private SimpleElement[] mCurrentDragElements;
-
- /**
- * The first view under the cursor that responded to onDropEnter is called the "target view".
- * It can differ from mCurrentView, typically because a terminal View doesn't
- * accept drag'n'drop so its parent layout became the target drag'n'drop receiver.
- * <p/>
- * The target node is the proxy node associated with the target view.
- * This can be null if no view under the cursor accepted the drag'n'drop or if the node
- * factory couldn't create a proxy for it.
- */
- private NodeProxy mTargetNode;
-
- /**
- * The latest drop feedback returned by IViewRule.onDropEnter/Move.
- */
- private DropFeedback mFeedback;
-
- /**
- * {@link #dragLeave(DropTargetEvent)} is unfortunately called right before data is
- * about to be dropped (between the last {@link #dragOver(DropTargetEvent)} and the
- * next {@link #dropAccept(DropTargetEvent)}). That means we can't just
- * trash the current DropFeedback from the current view rule in dragLeave().
- * Instead we preserve it in mLeaveTargetNode and mLeaveFeedback in case a dropAccept
- * happens next.
- */
- private NodeProxy mLeaveTargetNode;
-
- /**
- * @see #mLeaveTargetNode
- */
- private DropFeedback mLeaveFeedback;
-
- /**
- * @see #mLeaveTargetNode
- */
- private CanvasViewInfo mLeaveView;
-
- /** Singleton used to keep track of drag selection in the same Eclipse instance. */
- private final GlobalCanvasDragInfo mGlobalDragInfo;
-
- /**
- * Constructs a new {@link MoveGesture}, tied to the given canvas.
- *
- * @param canvas The canvas to associate the {@link MoveGesture} with.
- */
- public MoveGesture(LayoutCanvas canvas) {
- mCanvas = canvas;
- mGlobalDragInfo = GlobalCanvasDragInfo.getInstance();
- }
-
- @Override
- public List<Overlay> createOverlays() {
- mOverlay = new MoveOverlay();
- return Collections.<Overlay> singletonList(mOverlay);
- }
-
- @Override
- public void begin(ControlPoint pos, int startMask) {
- super.begin(pos, startMask);
-
- // Hide selection overlays during a move drag
- mCanvas.getSelectionOverlay().setHidden(true);
- }
-
- @Override
- public void end(ControlPoint pos, boolean canceled) {
- super.end(pos, canceled);
-
- mCanvas.getSelectionOverlay().setHidden(false);
-
- // Ensure that the outline is back to showing the current selection, since during
- // a drag gesture we temporarily set it to show the current target node instead.
- mCanvas.getSelectionManager().syncOutlineSelection();
- }
-
- /* TODO: Pass modifier mask to drag rules as well! This doesn't work yet since
- the drag &amp; drop code seems to steal keyboard events.
- @Override
- public boolean keyPressed(KeyEvent event) {
- update(mCanvas.getGestureManager().getCurrentControlPoint());
- mCanvas.redraw();
- return true;
- }
-
- @Override
- public boolean keyReleased(KeyEvent event) {
- update(mCanvas.getGestureManager().getCurrentControlPoint());
- mCanvas.redraw();
- return true;
- }
- */
-
- /*
- * The cursor has entered the drop target boundaries.
- * {@inheritDoc}
- */
- @Override
- public void dragEnter(DropTargetEvent event) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "drag enter", event);
-
- // Make sure we don't have any residual data from an earlier operation.
- clearDropInfo();
- mLeaveTargetNode = null;
- mLeaveFeedback = null;
- mLeaveView = null;
-
- // Get the dragged elements.
- //
- // The current transfered type can be extracted from the event.
- // As described in dragOver(), this works basically works on Windows but
- // not on Linux or Mac, in which case we can't get the type until we
- // receive dropAccept/drop().
- // For consistency we try to use the GlobalCanvasDragInfo instance first,
- // and if it fails we use the event transfer type as a backup (but as said
- // before it will most likely work only on Windows.)
- // In any case this can be null even for a valid transfer.
-
- mCurrentDragElements = mGlobalDragInfo.getCurrentElements();
-
- if (mCurrentDragElements == null) {
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
- if (sxt.isSupportedType(event.currentDataType)) {
- mCurrentDragElements = (SimpleElement[]) sxt.nativeToJava(event.currentDataType);
- }
- }
-
- // if there is no data to transfer, invalidate the drag'n'drop.
- // The assumption is that the transfer should have at least one element with a
- // a non-null non-empty FQCN. Everything else is optional.
- if (mCurrentDragElements == null ||
- mCurrentDragElements.length == 0 ||
- mCurrentDragElements[0] == null ||
- mCurrentDragElements[0].getFqcn() == null ||
- mCurrentDragElements[0].getFqcn().length() == 0) {
- event.detail = DND.DROP_NONE;
- }
-
- dragOperationChanged(event);
- }
-
- /*
- * The operation being performed has changed (e.g. modifier key).
- * {@inheritDoc}
- */
- @Override
- public void dragOperationChanged(DropTargetEvent event) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "drag changed", event);
-
- checkDataType(event);
- recomputeDragType(event);
- }
-
- private void recomputeDragType(DropTargetEvent event) {
- if (event.detail == DND.DROP_DEFAULT) {
- // Default means we can now choose the default operation, either copy or move.
- // If the drag comes from the same canvas we default to move, otherwise we
- // default to copy.
-
- if (mGlobalDragInfo.getSourceCanvas() == mCanvas &&
- (event.operations & DND.DROP_MOVE) != 0) {
- event.detail = DND.DROP_MOVE;
- } else if ((event.operations & DND.DROP_COPY) != 0) {
- event.detail = DND.DROP_COPY;
- }
- }
-
- // We don't support other types than copy and move
- if (event.detail != DND.DROP_COPY && event.detail != DND.DROP_MOVE) {
- event.detail = DND.DROP_NONE;
- }
- }
-
- /*
- * The cursor has left the drop target boundaries OR data is about to be dropped.
- * {@inheritDoc}
- */
- @Override
- public void dragLeave(DropTargetEvent event) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "drag leave");
-
- // dragLeave is unfortunately called right before data is about to be dropped
- // (between the last dropMove and the next dropAccept). That means we can't just
- // trash the current DropFeedback from the current view rule, we need to preserve
- // it in case a dropAccept happens next.
- // See the corresponding kludge in dropAccept().
- mLeaveTargetNode = mTargetNode;
- mLeaveFeedback = mFeedback;
- mLeaveView = mCurrentView;
-
- clearDropInfo();
- }
-
- /*
- * The cursor is moving over the drop target.
- * {@inheritDoc}
- */
- @Override
- public void dragOver(DropTargetEvent event) {
- processDropEvent(event);
- }
-
- /*
- * The drop is about to be performed.
- * The drop target is given a last chance to change the nature of the drop.
- * {@inheritDoc}
- */
- @Override
- public void dropAccept(DropTargetEvent event) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "drop accept");
-
- checkDataType(event);
-
- // If we have a valid target node and it matches the one we saved in
- // dragLeave then we restore the DropFeedback that we saved in dragLeave.
- if (mLeaveTargetNode != null) {
- mTargetNode = mLeaveTargetNode;
- mFeedback = mLeaveFeedback;
- mCurrentView = mLeaveView;
- }
-
- if (mFeedback != null && mFeedback.invalidTarget) {
- // The script said we can't drop here.
- event.detail = DND.DROP_NONE;
- }
-
- if (mLeaveTargetNode == null || event.detail == DND.DROP_NONE) {
- clearDropInfo();
- }
-
- mLeaveTargetNode = null;
- mLeaveFeedback = null;
- mLeaveView = null;
- }
-
- /*
- * The data is being dropped.
- * {@inheritDoc}
- */
- @Override
- public void drop(final DropTargetEvent event) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "dropped");
-
- SimpleElement[] elements = null;
-
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
-
- if (sxt.isSupportedType(event.currentDataType)) {
- if (event.data instanceof SimpleElement[]) {
- elements = (SimpleElement[]) event.data;
- }
- }
-
- if (elements == null || elements.length < 1) {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "drop missing drop data");
- return;
- }
-
- if (mCurrentDragElements != null && Arrays.equals(elements, mCurrentDragElements)) {
- elements = mCurrentDragElements;
- }
-
- if (mTargetNode == null) {
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- if (viewHierarchy.isValid() && viewHierarchy.isEmpty()) {
- // There is no target node because the drop happens on an empty document.
- // Attempt to create a root node accordingly.
- createDocumentRoot(elements);
- } else {
- if (DEBUG) AdtPlugin.printErrorToConsole("DEBUG", "dropped on null targetNode");
- }
- return;
- }
-
- updateDropFeedback(mFeedback, event);
-
- final SimpleElement[] elementsFinal = elements;
- final LayoutPoint canvasPoint = getDropLocation(event).toLayout();
- String label = computeUndoLabel(mTargetNode, elements, event.detail);
-
- // Create node listener which (during the drop) listens for node additions
- // and stores the list of added node such that they can be selected afterwards.
- final List<UiElementNode> added = new ArrayList<UiElementNode>();
- // List of "index within parent" for each node
- final List<Integer> indices = new ArrayList<Integer>();
- NodeCreationListener listener = new NodeCreationListener() {
- @Override
- public void nodeCreated(UiElementNode parent, UiElementNode child, int index) {
- if (parent == mTargetNode.getNode()) {
- added.add(child);
-
- // Adjust existing indices
- for (int i = 0, n = indices.size(); i < n; i++) {
- int idx = indices.get(i);
- if (idx >= index) {
- indices.set(i, idx + 1);
- }
- }
-
- indices.add(index);
- }
- }
-
- @Override
- public void nodeDeleted(UiElementNode parent, UiElementNode child, int previousIndex) {
- if (parent == mTargetNode.getNode()) {
- // Adjust existing indices
- for (int i = 0, n = indices.size(); i < n; i++) {
- int idx = indices.get(i);
- if (idx >= previousIndex) {
- indices.set(i, idx - 1);
- }
- }
-
- // Make sure we aren't removing the same nodes that are being added
- // No, that can happen when canceling out of a drop handler such as
- // when dropping an included layout, then canceling out of the
- // resource chooser.
- //assert !added.contains(child);
- }
- }
- };
-
- try {
- UiElementNode.addNodeCreationListener(listener);
- mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(label, new Runnable() {
- @Override
- public void run() {
- InsertType insertType = getInsertType(event, mTargetNode);
- mCanvas.getRulesEngine().callOnDropped(mTargetNode,
- elementsFinal,
- mFeedback,
- new Point(canvasPoint.x, canvasPoint.y),
- insertType);
- mTargetNode.applyPendingChanges();
- // Clean up drag if applicable
- if (event.detail == DND.DROP_MOVE) {
- GlobalCanvasDragInfo.getInstance().removeSource();
- }
- mTargetNode.applyPendingChanges();
- }
- });
- } finally {
- UiElementNode.removeNodeCreationListener(listener);
- }
-
- final List<INode> nodes = new ArrayList<INode>();
- NodeFactory nodeFactory = mCanvas.getNodeFactory();
- for (UiElementNode uiNode : added) {
- if (uiNode instanceof UiViewElementNode) {
- NodeProxy node = nodeFactory.create((UiViewElementNode) uiNode);
- if (node != null) {
- nodes.add(node);
- }
- }
- }
-
- // Select the newly dropped nodes:
- // Find out which nodes were added, and look up their corresponding
- // CanvasViewInfos.
- final SelectionManager selectionManager = mCanvas.getSelectionManager();
- // Don't use the indices to search for corresponding nodes yet, since a
- // render may not have happened yet and we'd rather use an up to date
- // view hierarchy than indices to look up the right view infos.
- if (!selectionManager.selectDropped(nodes, null /* indices */)) {
- // In some scenarios we can't find the actual view infos yet; this
- // seems to happen when you drag from one canvas to another (see the
- // related comment next to the setFocus() call below). In that case
- // defer selection briefly until the view hierarchy etc is up to
- // date.
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- selectionManager.selectDropped(nodes, indices);
- }
- });
- }
-
- clearDropInfo();
- mCanvas.redraw();
- // Request focus: This is *necessary* when you are dragging from one canvas editor
- // to another, because without it, the redraw does not seem to be processed (the change
- // is invisible until you click on the target canvas to give it focus).
- mCanvas.setFocus();
- }
-
- /**
- * Returns the right {@link InsertType} to use for the given drop target event and the
- * given target node
- *
- * @param event the drop target event
- * @param mTargetNode the node targeted by the drop
- * @return the {link InsertType} to use for the drop
- */
- public static InsertType getInsertType(DropTargetEvent event, NodeProxy mTargetNode) {
- GlobalCanvasDragInfo dragInfo = GlobalCanvasDragInfo.getInstance();
- if (event.detail == DND.DROP_MOVE) {
- SelectionItem[] selection = dragInfo.getCurrentSelection();
- if (selection != null) {
- for (SelectionItem item : selection) {
- if (item.getNode() != null
- && item.getNode().getParent() == mTargetNode) {
- return InsertType.MOVE_WITHIN;
- }
- }
- }
-
- return InsertType.MOVE_INTO;
- } else if (dragInfo.getSourceCanvas() != null) {
- return InsertType.PASTE;
- } else {
- return InsertType.CREATE;
- }
- }
-
- /**
- * Computes a suitable Undo label to use for a drop operation, such as
- * "Drop Button in LinearLayout" and "Move Widgets in RelativeLayout".
- *
- * @param targetNode The target of the drop
- * @param elements The dragged widgets
- * @param detail The DnD mode, as used in {@link DropTargetEvent#detail}.
- * @return A string suitable as an undo-label for the drop event
- */
- public static String computeUndoLabel(NodeProxy targetNode,
- SimpleElement[] elements, int detail) {
- // Decide whether it's a move or a copy; we'll label moves specifically
- // as a move and consider everything else a "Drop"
- String verb = (detail == DND.DROP_MOVE) ? "Move" : "Drop";
-
- // Get the type of widget being dropped/moved, IF there is only one. If
- // there is more than one, just reference it as "Widgets".
- String object;
- if (elements != null && elements.length == 1) {
- object = getSimpleName(elements[0].getFqcn());
- } else {
- object = "Widgets";
- }
-
- String where = getSimpleName(targetNode.getFqcn());
-
- // When we localize this: $1 is the verb (Move or Drop), $2 is the
- // object (such as "Button"), and $3 is the place we are doing it (such
- // as "LinearLayout").
- return String.format("%1$s %2$s in %3$s", verb, object, where);
- }
-
- /**
- * Returns simple name (basename, following last dot) of a fully qualified
- * class name.
- *
- * @param fqcn The fqcn to reduce
- * @return The base name of the fqcn
- */
- public static String getSimpleName(String fqcn) {
- // Note that the following works even when there is no dot, since
- // lastIndexOf will return -1 so we get fcqn.substring(-1+1) =
- // fcqn.substring(0) = fqcn
- return fqcn.substring(fqcn.lastIndexOf('.') + 1);
- }
-
- /**
- * Updates the {@link DropFeedback#isCopy} and {@link DropFeedback#sameCanvas} fields
- * of the given {@link DropFeedback}. This is generally called right before invoking
- * one of the callOnXyz methods of GRE to refresh the fields.
- *
- * @param df The current {@link DropFeedback}.
- * @param event An optional event to determine if the current operation is copy or move.
- */
- private void updateDropFeedback(DropFeedback df, DropTargetEvent event) {
- if (event != null) {
- df.isCopy = event.detail == DND.DROP_COPY;
- }
- df.sameCanvas = mCanvas == mGlobalDragInfo.getSourceCanvas();
- df.invalidTarget = false;
- df.dipScale = mCanvas.getEditorDelegate().getGraphicalEditor().getDipScale();
- df.modifierMask = mCanvas.getGestureManager().getRuleModifierMask();
-
- // Set the drag bounds, after converting it from control coordinates to
- // layout coordinates
- GlobalCanvasDragInfo dragInfo = GlobalCanvasDragInfo.getInstance();
- Rect dragBounds = null;
- Rect controlDragBounds = dragInfo.getDragBounds();
- if (controlDragBounds != null) {
- CanvasTransform ht = mCanvas.getHorizontalTransform();
- CanvasTransform vt = mCanvas.getVerticalTransform();
- double horizScale = ht.getScale();
- double verticalScale = vt.getScale();
- int x = (int) (controlDragBounds.x / horizScale);
- int y = (int) (controlDragBounds.y / verticalScale);
- int w = (int) (controlDragBounds.w / horizScale);
- int h = (int) (controlDragBounds.h / verticalScale);
- dragBounds = new Rect(x, y, w, h);
- }
- int baseline = dragInfo.getDragBaseline();
- if (baseline != -1) {
- df.dragBaseline = baseline;
- }
- df.dragBounds = dragBounds;
- }
-
- /**
- * Verifies that event.currentDataType is of type {@link SimpleXmlTransfer}.
- * If not, try to find a valid data type.
- * Otherwise set the drop to {@link DND#DROP_NONE} to cancel it.
- *
- * @return True if the data type is accepted.
- */
- private static boolean checkDataType(DropTargetEvent event) {
-
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
-
- TransferData current = event.currentDataType;
-
- if (sxt.isSupportedType(current)) {
- return true;
- }
-
- // We only support SimpleXmlTransfer and the current data type is not right.
- // Let's see if we can find another one.
-
- for (TransferData td : event.dataTypes) {
- if (td != current && sxt.isSupportedType(td)) {
- // We like this type better.
- event.currentDataType = td;
- return true;
- }
- }
-
- // We failed to find any good transfer type.
- event.detail = DND.DROP_NONE;
- return false;
- }
-
- /**
- * Returns the mouse location of the drop target event.
- *
- * @param event the drop target event
- * @return a {@link ControlPoint} location corresponding to the top left corner
- */
- private ControlPoint getDropLocation(DropTargetEvent event) {
- return ControlPoint.create(mCanvas, event);
- }
-
- /**
- * Called on both dragEnter and dragMove.
- * Generates the onDropEnter/Move/Leave events depending on the currently
- * selected target node.
- */
- private void processDropEvent(DropTargetEvent event) {
- if (!mCanvas.getViewHierarchy().isValid()) {
- // We don't allow drop on an invalid layout, even if we have some obsolete
- // layout info for it.
- event.detail = DND.DROP_NONE;
- clearDropInfo();
- return;
- }
-
- LayoutPoint p = getDropLocation(event).toLayout();
-
- // Is the mouse currently captured by a DropFeedback.captureArea?
- boolean isCaptured = false;
- if (mFeedback != null) {
- Rect r = mFeedback.captureArea;
- isCaptured = r != null && r.contains(p.x, p.y);
- }
-
- // We can't switch views/nodes when the mouse is captured
- CanvasViewInfo vi;
- if (isCaptured) {
- vi = mCurrentView;
- } else {
- vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
-
- // When dragging into the canvas, if you are not over any other view, target
- // the root element (since it may not "fill" the screen, e.g. if you have a linear
- // layout but have layout_height wrap_content, then the layout will only extend
- // to cover the children in the layout, not the whole visible screen area, which
- // may be surprising
- if (vi == null) {
- vi = mCanvas.getViewHierarchy().getRoot();
- }
- }
-
- boolean isMove = true;
- boolean needRedraw = false;
-
- if (vi != mCurrentView) {
- // Current view has changed. Does that also change the target node?
- // Note that either mCurrentView or vi can be null.
-
- if (vi == null) {
- // vi is null but mCurrentView is not, no view is a target anymore
- // We don't need onDropMove in this case
- isMove = false;
- needRedraw = true;
- event.detail = DND.DROP_NONE;
- clearDropInfo(); // this will call callDropLeave.
-
- } else {
- // vi is a new current view.
- // Query GRE for onDropEnter on the ViewInfo hierarchy, starting from the child
- // towards its parent, till we find one that returns a non-null drop feedback.
-
- DropFeedback df = null;
- NodeProxy targetNode = null;
-
- for (CanvasViewInfo targetVi = vi;
- targetVi != null && df == null;
- targetVi = targetVi.getParent()) {
- targetNode = mCanvas.getNodeFactory().create(targetVi);
- df = mCanvas.getRulesEngine().callOnDropEnter(targetNode,
- targetVi.getViewObject(), mCurrentDragElements);
-
- if (df != null) {
- // We should also dispatch an onDropMove() call to the initial enter
- // position, such that the view is notified of the position where
- // we are within the node immediately (before we for example attempt
- // to draw feedback). This is necessary since most views perform the
- // guideline computations in onDropMove (since only onDropMove is handed
- // the -position- of the mouse), and we want this computation to happen
- // before we ask the view to draw its feedback.
- updateDropFeedback(df, event);
- df = mCanvas.getRulesEngine().callOnDropMove(targetNode,
- mCurrentDragElements, df, new Point(p.x, p.y));
- }
-
- if (df != null &&
- event.detail == DND.DROP_MOVE &&
- mCanvas == mGlobalDragInfo.getSourceCanvas()) {
- // You can't move an object into itself in the same canvas.
- // E.g. case of moving a layout and the node under the mouse is the
- // layout itself: a copy would be ok but not a move operation of the
- // layout into himself.
-
- SelectionItem[] selection = mGlobalDragInfo.getCurrentSelection();
- if (selection != null) {
- for (SelectionItem cs : selection) {
- if (cs.getViewInfo() == targetVi) {
- // The node that responded is one of the selection roots.
- // Simply invalidate the drop feedback and move on the
- // parent in the ViewInfo chain.
-
- updateDropFeedback(df, event);
- mCanvas.getRulesEngine().callOnDropLeave(
- targetNode, mCurrentDragElements, df);
- df = null;
- targetNode = null;
- }
- }
- }
- }
- }
-
- if (df == null) {
- // Provide visual feedback that we are refusing the drop
- event.detail = DND.DROP_NONE;
- clearDropInfo();
-
- } else if (targetNode != mTargetNode) {
- // We found a new target node for the drag'n'drop.
- // Release the previous one, if any.
- callDropLeave();
-
- // And assign the new one
- mTargetNode = targetNode;
- mFeedback = df;
-
- // We don't need onDropMove in this case
- isMove = false;
- }
- }
-
- mCurrentView = vi;
- }
-
- if (isMove && mTargetNode != null && mFeedback != null) {
- // this is a move inside the same view
- com.android.ide.common.api.Point p2 =
- new com.android.ide.common.api.Point(p.x, p.y);
- updateDropFeedback(mFeedback, event);
- DropFeedback df = mCanvas.getRulesEngine().callOnDropMove(
- mTargetNode, mCurrentDragElements, mFeedback, p2);
- mCanvas.getGestureManager().updateMessage(mFeedback);
-
- if (df == null) {
- // The target is no longer interested in the drop move.
- event.detail = DND.DROP_NONE;
- callDropLeave();
-
- } else if (df != mFeedback) {
- mFeedback = df;
- }
- }
-
- if (mFeedback != null) {
- if (event.detail == DND.DROP_NONE && !mFeedback.invalidTarget) {
- // If we previously provided visual feedback that we were refusing
- // the drop, we now need to change it to mean we're accepting it.
- event.detail = DND.DROP_DEFAULT;
- recomputeDragType(event);
-
- } else if (mFeedback.invalidTarget) {
- // Provide visual feedback that we are refusing the drop
- event.detail = DND.DROP_NONE;
- }
- }
-
- if (needRedraw || (mFeedback != null && mFeedback.requestPaint)) {
- mCanvas.redraw();
- }
-
- // Update outline to show the target node there
- OutlinePage outline = mCanvas.getOutlinePage();
- TreeSelection newSelection = TreeSelection.EMPTY;
- if (mCurrentView != null && mTargetNode != null) {
- // Find the view corresponding to the target node. The current view can be a leaf
- // view whereas the target node is always a parent layout.
- if (mCurrentView.getUiViewNode() != mTargetNode.getNode()) {
- mCurrentView = mCurrentView.getParent();
- }
- if (mCurrentView != null && mCurrentView.getUiViewNode() == mTargetNode.getNode()) {
- TreePath treePath = SelectionManager.getTreePath(mCurrentView);
- newSelection = new TreeSelection(treePath);
- }
- }
-
- ISelection currentSelection = outline.getSelection();
- if (currentSelection == null || !currentSelection.equals(newSelection)) {
- outline.setSelection(newSelection);
- }
- }
-
- /**
- * Calls onDropLeave on mTargetNode with the current mFeedback. <br/>
- * Then clears mTargetNode and mFeedback.
- */
- private void callDropLeave() {
- if (mTargetNode != null && mFeedback != null) {
- updateDropFeedback(mFeedback, null);
- mCanvas.getRulesEngine().callOnDropLeave(mTargetNode, mCurrentDragElements, mFeedback);
- }
-
- mTargetNode = null;
- mFeedback = null;
- }
-
- private void clearDropInfo() {
- callDropLeave();
- mCurrentView = null;
- mCanvas.redraw();
- }
-
- /**
- * Creates a root element in an empty document.
- * Only the first element's FQCN of the dragged elements is used.
- * <p/>
- * Actual XML handling is done by {@link LayoutCanvas#createDocumentRoot(String)}.
- */
- private void createDocumentRoot(SimpleElement[] elements) {
- if (elements == null || elements.length < 1 || elements[0] == null) {
- return;
- }
-
- mCanvas.createDocumentRoot(elements[0]);
- }
-
- /**
- * An {@link Overlay} to paint the move feedback. This just delegates to the
- * layout rules.
- */
- private class MoveOverlay extends Overlay {
- @Override
- public void paint(GC gc) {
- if (mTargetNode != null && mFeedback != null) {
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- rulesEngine.callDropFeedbackPaint(mCanvas.getGcWrapper(), mTargetNode, mFeedback);
- mFeedback.requestPaint = false;
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDragListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDragListener.java
deleted file mode 100644
index 1af3053e3..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDragListener.java
+++ /dev/null
@@ -1,129 +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 org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeItem;
-
-import java.util.ArrayList;
-
-/** Drag listener for the outline page */
-/* package */ class OutlineDragListener implements DragSourceListener {
- private TreeViewer mTreeViewer;
- private OutlinePage mOutlinePage;
- private final ArrayList<SelectionItem> mDragSelection = new ArrayList<SelectionItem>();
- private SimpleElement[] mDragElements;
-
- public OutlineDragListener(OutlinePage outlinePage, TreeViewer treeViewer) {
- super();
- mOutlinePage = outlinePage;
- mTreeViewer = treeViewer;
- }
-
- @Override
- public void dragStart(DragSourceEvent e) {
- Tree tree = mTreeViewer.getTree();
-
- TreeItem overTreeItem = tree.getItem(new Point(e.x, e.y));
- if (overTreeItem == null) {
- // Not dragging over a tree item
- e.doit = false;
- return;
- }
- CanvasViewInfo over = getViewInfo(overTreeItem);
- if (over == null) {
- e.doit = false;
- return;
- }
-
- // The selection logic for the outline is much simpler than in the canvas,
- // because for one thing, the tree selection is updated synchronously on mouse
- // down, so it's not possible to start dragging a non-selected item.
- // We also don't deliberately disallow root-element dragging since you can
- // drag it into another form.
- final LayoutCanvas canvas = mOutlinePage.getEditor().getCanvasControl();
- SelectionManager selectionManager = canvas.getSelectionManager();
- TreeItem[] treeSelection = tree.getSelection();
- mDragSelection.clear();
- for (TreeItem item : treeSelection) {
- CanvasViewInfo viewInfo = getViewInfo(item);
- if (viewInfo != null) {
- mDragSelection.add(selectionManager.createSelection(viewInfo));
- }
- }
- SelectionManager.sanitize(mDragSelection);
-
- e.doit = !mDragSelection.isEmpty();
- int imageCount = mDragSelection.size();
- if (e.doit) {
- mDragElements = SelectionItem.getAsElements(mDragSelection);
- GlobalCanvasDragInfo.getInstance().startDrag(mDragElements,
- mDragSelection.toArray(new SelectionItem[imageCount]),
- canvas, new Runnable() {
- @Override
- public void run() {
- canvas.getClipboardSupport().deleteSelection("Remove",
- mDragSelection);
- }
- });
- return;
- }
-
- e.detail = DND.DROP_NONE;
- }
-
- @Override
- public void dragSetData(DragSourceEvent e) {
- if (TextTransfer.getInstance().isSupportedType(e.dataType)) {
- LayoutCanvas canvas = mOutlinePage.getEditor().getCanvasControl();
- e.data = SelectionItem.getAsText(canvas, mDragSelection);
- return;
- }
-
- if (SimpleXmlTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = mDragElements;
- return;
- }
-
- // otherwise we failed
- e.detail = DND.DROP_NONE;
- e.doit = false;
- }
-
- @Override
- public void dragFinished(DragSourceEvent e) {
- // Unregister the dragged data.
- // Clear the selection
- mDragSelection.clear();
- mDragElements = null;
- GlobalCanvasDragInfo.getInstance().stopDrag();
- }
-
- private CanvasViewInfo getViewInfo(TreeItem item) {
- Object data = item.getData();
- if (data != null) {
- return OutlinePage.getViewInfo(data);
- }
-
- return null;
- }
-} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
deleted file mode 100644
index f4a826fa2..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineDropListener.java
+++ /dev/null
@@ -1,217 +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 com.android.ide.common.api.INode;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.layout.BaseLayoutRule;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.ViewerDropAdapter;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TransferData;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/** Drop listener for the outline page */
-/*package*/ class OutlineDropListener extends ViewerDropAdapter {
- private final OutlinePage mOutlinePage;
-
- public OutlineDropListener(OutlinePage outlinePage, TreeViewer treeViewer) {
- super(treeViewer);
- mOutlinePage = outlinePage;
- }
-
- @Override
- public void dragEnter(DropTargetEvent event) {
- if (event.detail == DND.DROP_NONE && GlobalCanvasDragInfo.getInstance().isDragging()) {
- // For some inexplicable reason, we get DND.DROP_NONE from the palette
- // even though in its drag start we set DND.DROP_COPY, so correct that here...
- int operation = DND.DROP_COPY;
- event.detail = operation;
- }
- super.dragEnter(event);
- }
-
- @Override
- public boolean performDrop(Object data) {
- final DropTargetEvent event = getCurrentEvent();
- if (event == null) {
- return false;
- }
- int location = determineLocation(event);
- if (location == LOCATION_NONE) {
- return false;
- }
-
- final SimpleElement[] elements;
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
- if (sxt.isSupportedType(event.currentDataType)) {
- if (data instanceof SimpleElement[]) {
- elements = (SimpleElement[]) data;
- } else {
- return false;
- }
- } else {
- return false;
- }
- if (elements.length == 0) {
- return false;
- }
-
- // Determine target:
- CanvasViewInfo parent = OutlinePage.getViewInfo(event.item.getData());
- if (parent == null) {
- return false;
- }
-
- int index = -1;
- UiViewElementNode parentNode = parent.getUiViewNode();
- if (location == LOCATION_BEFORE || location == LOCATION_AFTER) {
- UiViewElementNode node = parentNode;
- parent = parent.getParent();
- if (parent == null) {
- return false;
- }
- parentNode = parent.getUiViewNode();
-
- // Determine index
- index = 0;
- for (UiElementNode child : parentNode.getUiChildren()) {
- if (child == node) {
- break;
- }
- index++;
- }
- if (location == LOCATION_AFTER) {
- index++;
- }
- }
-
- // Copy into new position.
- final LayoutCanvas canvas = mOutlinePage.getEditor().getCanvasControl();
- 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, event.detail);
- final int indexFinal = index;
- canvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(label, new Runnable() {
- @Override
- public void run() {
- InsertType insertType = MoveGesture.getInsertType(event, targetNode);
- canvas.getRulesEngine().setInsertType(insertType);
-
- Object sourceCanvas = GlobalCanvasDragInfo.getInstance().getSourceCanvas();
- boolean createNew = event.detail == DND.DROP_COPY || sourceCanvas != canvas;
- BaseLayoutRule.insertAt(targetNode, elements, createNew, indexFinal);
- targetNode.applyPendingChanges();
-
- // Clean up drag if applicable
- if (event.detail == DND.DROP_MOVE) {
- GlobalCanvasDragInfo.getInstance().removeSource();
- }
- }
- });
-
- // 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);
- }
- }
- // Select the newly dropped nodes
- final SelectionManager selectionManager = canvas.getSelectionManager();
- selectionManager.setOutlineSelection(added);
-
- canvas.redraw();
-
- return true;
- }
-
- @Override
- public boolean validateDrop(Object target, int operation,
- TransferData transferType) {
- DropTargetEvent event = getCurrentEvent();
- if (event == null) {
- return false;
- }
- int location = determineLocation(event);
- if (location == LOCATION_NONE) {
- return false;
- }
-
- SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance();
- if (!sxt.isSupportedType(transferType)) {
- return false;
- }
-
- CanvasViewInfo parent = OutlinePage.getViewInfo(event.item.getData());
- if (parent == null) {
- return false;
- }
-
- UiViewElementNode parentNode = parent.getUiViewNode();
-
- if (location == LOCATION_ON) {
- // Targeting the middle of an item means to add it as a new child
- // of the given element. This is only allowed on some types of nodes.
- if (!DescriptorsUtils.canInsertChildren(parentNode.getDescriptor(),
- parent.getViewObject())) {
- return false;
- }
- }
-
- // Check that the drop target position is not a child or identical to
- // one of the dragged items
- SelectionItem[] sel = GlobalCanvasDragInfo.getInstance().getCurrentSelection();
- if (sel != null) {
- for (SelectionItem item : sel) {
- if (isAncestor(item.getViewInfo().getUiViewNode(), parentNode)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /** Returns true if the given parent node is an ancestor of the given child node */
- private boolean isAncestor(UiElementNode parent, UiElementNode child) {
- while (child != null) {
- if (child == parent) {
- return true;
- }
- child = child.getUiParent();
- }
- return false;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineOverlay.java
deleted file mode 100644
index e63fff7ab..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlineOverlay.java
+++ /dev/null
@@ -1,107 +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 org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Rectangle;
-
-/**
- * The {@link OutlineOverlay} paints an optional outline on top of the layout,
- * showing the structure of the individual Android View elements.
- */
-public class OutlineOverlay extends Overlay {
- /** The {@link ViewHierarchy} this outline visualizes */
- private final ViewHierarchy mViewHierarchy;
-
- /** Outline color. Must be disposed, it's NOT a system color. */
- private Color mOutlineColor;
-
- /** Vertical scaling & scrollbar information. */
- private CanvasTransform mVScale;
-
- /** Horizontal scaling & scrollbar information. */
- private CanvasTransform mHScale;
-
- /**
- * Constructs a new {@link OutlineOverlay} linked to the given view
- * hierarchy.
- *
- * @param viewHierarchy The {@link ViewHierarchy} to render
- * @param hScale The {@link CanvasTransform} to use to transfer horizontal layout
- * coordinates to screen coordinates
- * @param vScale The {@link CanvasTransform} to use to transfer vertical layout
- * coordinates to screen coordinates
- */
- public OutlineOverlay(
- ViewHierarchy viewHierarchy,
- CanvasTransform hScale,
- CanvasTransform vScale) {
- super();
- mViewHierarchy = viewHierarchy;
- mHScale = hScale;
- mVScale = vScale;
- }
-
- @Override
- public void create(Device device) {
- mOutlineColor = new Color(device, SwtDrawingStyle.OUTLINE.getStrokeColor());
- }
-
- @Override
- public void dispose() {
- if (mOutlineColor != null) {
- mOutlineColor.dispose();
- mOutlineColor = null;
- }
- }
-
- @Override
- public void paint(GC gc) {
- CanvasViewInfo lastRoot = mViewHierarchy.getRoot();
- if (lastRoot != null) {
- gc.setForeground(mOutlineColor);
- gc.setLineStyle(SwtDrawingStyle.OUTLINE.getLineStyle());
- int oldAlpha = gc.getAlpha();
- gc.setAlpha(SwtDrawingStyle.OUTLINE.getStrokeAlpha());
- drawOutline(gc, lastRoot);
- gc.setAlpha(oldAlpha);
- }
- }
-
- private void drawOutline(GC gc, CanvasViewInfo info) {
- Rectangle r = info.getAbsRect();
-
- int x = mHScale.translate(r.x);
- int y = mVScale.translate(r.y);
- int w = mHScale.scale(r.width);
- int h = mVScale.scale(r.height);
-
- // Add +1 to the width and +1 to the height such that when you have a
- // series of boxes (in say a LinearLayout), instead of the bottom of one
- // box and the top of the next box being -adjacent-, they -overlap-.
- // This makes the outline nicer visually since you don't get
- // "double thickness" lines for all adjacent boxes.
- gc.drawRectangle(x, y, w + 1, h + 1);
-
- for (CanvasViewInfo vi : info.getChildren()) {
- drawOutline(gc, vi);
- }
- }
-
-}
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);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Overlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Overlay.java
deleted file mode 100644
index 9b7e0eb18..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/Overlay.java
+++ /dev/null
@@ -1,91 +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 org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.GC;
-
-/**
- * An Overlay is a set of graphics which can be painted on top of the visual
- * editor. Different {@link Gesture}s produce context specific overlays, such as
- * swiping rectangles from the {@link MarqueeGesture} and guidelines from the
- * {@link MoveGesture}.
- */
-public abstract class Overlay {
- private Device mDevice;
-
- /** Whether the hover is hidden */
- private boolean mHiding;
-
- /**
- * Construct the overlay, using the given graphics context for painting.
- */
- public Overlay() {
- super();
- }
-
- /**
- * Initializes the overlay before the first use, if applicable. This is a
- * good place to initialize resources like colors.
- *
- * @param device The device to allocate resources for; the parameter passed
- * to {@link #paint} will correspond to this device.
- */
- public void create(Device device) {
- mDevice = device;
- }
-
- /**
- * Releases resources held by the overlay. Called by the editor when an
- * overlay has been removed.
- */
- public void dispose() {
- }
-
- /**
- * Paints the overlay.
- *
- * @param gc The SWT {@link GC} object to draw into.
- */
- public void paint(GC gc) {
- throw new IllegalArgumentException("paint() not implemented, probably done "
- + "with specialized paint signature");
- }
-
- /** Returns the device associated with this overlay */
- public Device getDevice() {
- return mDevice;
- }
-
- /**
- * Returns whether the overlay is hidden
- *
- * @return true if the selection overlay is hidden
- */
- public boolean isHiding() {
- return mHiding;
- }
-
- /**
- * Hides the overlay
- *
- * @param hiding true to hide the overlay, false to unhide it (default)
- */
- public void setHiding(boolean hiding) {
- mHiding = hiding;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
deleted file mode 100644
index 46168b70f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
- * Copyright (C) 2009 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_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.SdkConstants.XMLNS_ANDROID;
-import static com.android.SdkConstants.XMLNS_URI;
-
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction.Toggle;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ViewInfo;
-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.DescriptorsUtils;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.CustomViewDescriptorService;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-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.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
-import com.android.utils.Pair;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.IToolBarManager;
-import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DragSource;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.MenuDetectEvent;
-import org.eclipse.swt.events.MenuDetectListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.RGB;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FillLayout;
-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.Display;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.eclipse.wb.internal.core.editor.structure.IPage;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A palette control for the {@link GraphicalEditorPart}.
- * <p/>
- * The palette contains several groups, each with a UI name (e.g. layouts and views) and each
- * with a list of element descriptors.
- * <p/>
- *
- * TODO list:
- * - The available items should depend on the actual GLE2 Canvas selection. Selected android
- * views should force filtering on what they accept can be dropped on them (e.g. TabHost,
- * TableLayout). Should enable/disable them, not hide them, to avoid shuffling around.
- * - Optional: a text filter
- * - Optional: have context-sensitive tools items, e.g. selection arrow tool,
- * group selection tool, alignment, etc.
- */
-public class PaletteControl extends Composite {
-
- /**
- * Wrapper to create a {@link PaletteControl}
- */
- static class PalettePage implements IPage {
- private final GraphicalEditorPart mEditorPart;
- private PaletteControl mControl;
-
- PalettePage(GraphicalEditorPart editor) {
- mEditorPart = editor;
- }
-
- @Override
- public void createControl(Composite parent) {
- mControl = new PaletteControl(parent, mEditorPart);
- }
-
- @Override
- public Control getControl() {
- return mControl;
- }
-
- @Override
- public void dispose() {
- mControl.dispose();
- }
-
- @Override
- public void setToolBar(IToolBarManager toolBarManager) {
- }
-
- /**
- * Add tool bar items to the given toolbar
- *
- * @param toolbar the toolbar to add items into
- */
- void createToolbarItems(final ToolBar toolbar) {
- final ToolItem popupMenuItem = new ToolItem(toolbar, SWT.PUSH);
- popupMenuItem.setToolTipText("View Menu");
- popupMenuItem.setImage(IconFactory.getInstance().getIcon("view_menu"));
- popupMenuItem.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- Rectangle bounds = popupMenuItem.getBounds();
- // Align menu horizontally with the toolbar button and
- // vertically with the bottom of the toolbar
- Point point = toolbar.toDisplay(bounds.x, bounds.y + bounds.height);
- mControl.showMenu(point.x, point.y);
- }
- });
- }
-
- @Override
- public void setFocus() {
- mControl.setFocus();
- }
- }
-
- /**
- * The parent grid layout that contains all the {@link Toggle} and
- * {@link IconTextItem} widgets.
- */
- private GraphicalEditorPart mEditor;
- private Color mBackground;
- private Color mForeground;
-
- /** The palette modes control various ways to visualize and lay out the views */
- private static enum PaletteMode {
- /** Show rendered previews of the views */
- PREVIEW("Show Previews", true),
- /** Show rendered previews of the views, scaled down to 75% */
- SMALL_PREVIEW("Show Small Previews", true),
- /** Show rendered previews of the views, scaled down to 50% */
- TINY_PREVIEW("Show Tiny Previews", true),
- /** Show an icon + text label */
- ICON_TEXT("Show Icon and Text", false),
- /** Show only icons, packed multiple per row */
- ICON_ONLY("Show Only Icons", true);
-
- PaletteMode(String actionLabel, boolean wrap) {
- mActionLabel = actionLabel;
- mWrap = wrap;
- }
-
- public String getActionLabel() {
- return mActionLabel;
- }
-
- public boolean getWrap() {
- return mWrap;
- }
-
- public boolean isPreview() {
- return this == PREVIEW || this == SMALL_PREVIEW || this == TINY_PREVIEW;
- }
-
- public boolean isScaledPreview() {
- return this == SMALL_PREVIEW || this == TINY_PREVIEW;
- }
-
- private final String mActionLabel;
- private final boolean mWrap;
- };
-
- /** Token used in preference string to record alphabetical sorting */
- private static final String VALUE_ALPHABETICAL = "alpha"; //$NON-NLS-1$
- /** Token used in preference string to record categories being turned off */
- private static final String VALUE_NO_CATEGORIES = "nocat"; //$NON-NLS-1$
- /** Token used in preference string to record auto close being turned off */
- private static final String VALUE_NO_AUTOCLOSE = "noauto"; //$NON-NLS-1$
-
- private final PreviewIconFactory mPreviewIconFactory = new PreviewIconFactory(this);
- private PaletteMode mPaletteMode = null;
- /** Use alphabetical sorting instead of natural order? */
- private boolean mAlphabetical;
- /** Use categories instead of a single large list of views? */
- private boolean mCategories = true;
- /** Auto-close the previous category when new categories are opened */
- private boolean mAutoClose = true;
- private AccordionControl mAccordion;
- private String mCurrentTheme;
- private String mCurrentDevice;
- private IAndroidTarget mCurrentTarget;
- private AndroidTargetData mCurrentTargetData;
-
- /**
- * Create the composite.
- * @param parent The parent composite.
- * @param editor An editor associated with this palette.
- */
- public PaletteControl(Composite parent, GraphicalEditorPart editor) {
- super(parent, SWT.NONE);
-
- mEditor = editor;
- }
-
- /** Reads UI mode from persistent store to preserve palette mode across IDE sessions */
- private void loadPaletteMode() {
- String paletteModes = AdtPrefs.getPrefs().getPaletteModes();
- if (paletteModes.length() > 0) {
- String[] tokens = paletteModes.split(","); //$NON-NLS-1$
- try {
- mPaletteMode = PaletteMode.valueOf(tokens[0]);
- } catch (Throwable t) {
- mPaletteMode = PaletteMode.values()[0];
- }
- mAlphabetical = paletteModes.contains(VALUE_ALPHABETICAL);
- mCategories = !paletteModes.contains(VALUE_NO_CATEGORIES);
- mAutoClose = !paletteModes.contains(VALUE_NO_AUTOCLOSE);
- } else {
- mPaletteMode = PaletteMode.SMALL_PREVIEW;
- }
- }
-
- /**
- * Returns the most recently stored version of auto-close-mode; this is the last
- * user-initiated setting of the auto-close mode (we programmatically switch modes when
- * you enter icons-only mode, and set it back to this when going to any other mode)
- */
- private boolean getSavedAutoCloseMode() {
- return !AdtPrefs.getPrefs().getPaletteModes().contains(VALUE_NO_AUTOCLOSE);
- }
-
- /** Saves UI mode to persistent store to preserve palette mode across IDE sessions */
- private void savePaletteMode() {
- StringBuilder sb = new StringBuilder();
- sb.append(mPaletteMode);
- if (mAlphabetical) {
- sb.append(',').append(VALUE_ALPHABETICAL);
- }
- if (!mCategories) {
- sb.append(',').append(VALUE_NO_CATEGORIES);
- }
- if (!mAutoClose) {
- sb.append(',').append(VALUE_NO_AUTOCLOSE);
- }
- AdtPrefs.getPrefs().setPaletteModes(sb.toString());
- }
-
- private void refreshPalette() {
- IAndroidTarget oldTarget = mCurrentTarget;
- mCurrentTarget = null;
- mCurrentTargetData = null;
- mCurrentTheme = null;
- mCurrentDevice = null;
- reloadPalette(oldTarget);
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- @Override
- public void dispose() {
- if (mBackground != null) {
- mBackground.dispose();
- mBackground = null;
- }
- if (mForeground != null) {
- mForeground.dispose();
- mForeground = null;
- }
-
- super.dispose();
- }
-
- /**
- * Returns the currently displayed target
- *
- * @return the current target, or null
- */
- public IAndroidTarget getCurrentTarget() {
- return mCurrentTarget;
- }
-
- /**
- * Returns the currently displayed theme (in palette modes that support previewing)
- *
- * @return the current theme, or null
- */
- public String getCurrentTheme() {
- return mCurrentTheme;
- }
-
- /**
- * Returns the currently displayed device (in palette modes that support previewing)
- *
- * @return the current device, or null
- */
- public String getCurrentDevice() {
- return mCurrentDevice;
- }
-
- /** Returns true if previews in the palette should be made available */
- private boolean previewsAvailable() {
- // Not layoutlib 5 -- we require custom background support to do
- // a decent job with previews
- LayoutLibrary layoutLibrary = mEditor.getLayoutLibrary();
- return layoutLibrary != null && layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR);
- }
-
- /**
- * Loads or reloads the palette elements by using the layout and view descriptors from the
- * given target data.
- *
- * @param target The target that has just been loaded
- */
- public void reloadPalette(IAndroidTarget target) {
- ConfigurationChooser configChooser = mEditor.getConfigurationChooser();
- String theme = configChooser.getThemeName();
- String device = configChooser.getDeviceName();
- if (device == null) {
- return;
- }
- AndroidTargetData targetData =
- target != null ? Sdk.getCurrent().getTargetData(target) : null;
- if (target == mCurrentTarget && targetData == mCurrentTargetData
- && mCurrentTheme != null && mCurrentTheme.equals(theme)
- && mCurrentDevice != null && mCurrentDevice.equals(device)) {
- return;
- }
- mCurrentTheme = theme;
- mCurrentTarget = target;
- mCurrentTargetData = targetData;
- mCurrentDevice = device;
- mPreviewIconFactory.reset();
-
- if (targetData == null) {
- return;
- }
-
- Set<String> expandedCategories = null;
- if (mAccordion != null) {
- expandedCategories = mAccordion.getExpandedCategories();
- // We auto-expand all categories when showing icons-only. When returning to some
- // other mode we don't want to retain all categories open.
- if (expandedCategories.size() > 3) {
- expandedCategories = null;
- }
- }
-
- // Erase old content and recreate new
- for (Control c : getChildren()) {
- c.dispose();
- }
-
- if (mPaletteMode == null) {
- loadPaletteMode();
- assert mPaletteMode != null;
- }
-
- // Ensure that the palette mode is supported on this version of the layout library
- if (!previewsAvailable()) {
- if (mPaletteMode.isPreview()) {
- mPaletteMode = PaletteMode.ICON_TEXT;
- }
- }
-
- if (mPaletteMode.isPreview()) {
- if (mForeground != null) {
- mForeground.dispose();
- mForeground = null;
- }
- if (mBackground != null) {
- mBackground.dispose();
- mBackground = null;
- }
- RGB background = mPreviewIconFactory.getBackgroundColor();
- if (background != null) {
- mBackground = new Color(getDisplay(), background);
- }
- RGB foreground = mPreviewIconFactory.getForegroundColor();
- if (foreground != null) {
- mForeground = new Color(getDisplay(), foreground);
- }
- }
-
- List<String> headers = Collections.emptyList();
- final Map<String, List<ViewElementDescriptor>> categoryToItems;
- categoryToItems = new HashMap<String, List<ViewElementDescriptor>>();
- headers = new ArrayList<String>();
- List<Pair<String,List<ViewElementDescriptor>>> paletteEntries =
- ViewMetadataRepository.get().getPaletteEntries(targetData,
- mAlphabetical, mCategories);
- for (Pair<String,List<ViewElementDescriptor>> pair : paletteEntries) {
- String category = pair.getFirst();
- List<ViewElementDescriptor> categoryItems = pair.getSecond();
- headers.add(category);
- categoryToItems.put(category, categoryItems);
- }
-
- headers.add("Custom & Library Views");
-
- // Set the categories to expand the first item if
- // (1) we don't have a previously selected category, or
- // (2) there's just one category anyway, or
- // (3) the set of categories have changed so our previously selected category
- // doesn't exist anymore (can happen when you toggle "Show Categories")
- if ((expandedCategories == null && headers.size() > 0) || headers.size() == 1 ||
- (expandedCategories != null && expandedCategories.size() >= 1
- && !headers.contains(
- expandedCategories.iterator().next().replace("&&", "&")))) { //$NON-NLS-1$ //$NON-NLS-2$
- // Expand the first category if we don't have a previous selection (e.g. refresh)
- expandedCategories = Collections.singleton(headers.get(0));
- }
-
- boolean wrap = mPaletteMode.getWrap();
-
- // Pack icon-only view vertically; others stretch to fill palette region
- boolean fillVertical = mPaletteMode != PaletteMode.ICON_ONLY;
-
- mAccordion = new AccordionControl(this, SWT.NONE, headers, fillVertical, wrap,
- expandedCategories) {
- @Override
- protected Composite createChildContainer(Composite parent, Object header, int style) {
- assert categoryToItems != null;
- List<ViewElementDescriptor> list = categoryToItems.get(header);
- final Composite composite;
- if (list == null) {
- assert header.equals("Custom & Library Views");
-
- Composite wrapper = new Composite(parent, SWT.NONE);
- GridLayout gridLayout = new GridLayout(1, false);
- gridLayout.marginWidth = gridLayout.marginHeight = 0;
- gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
- gridLayout.marginBottom = 3;
- wrapper.setLayout(gridLayout);
- if (mPaletteMode.isPreview() && mBackground != null) {
- wrapper.setBackground(mBackground);
- }
- composite = super.createChildContainer(wrapper, header, style);
- if (mPaletteMode.isPreview() && mBackground != null) {
- composite.setBackground(mBackground);
- }
- composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- Button refreshButton = new Button(wrapper, SWT.PUSH | SWT.FLAT);
- refreshButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
- false, false, 1, 1));
- refreshButton.setText("Refresh");
- refreshButton.setImage(IconFactory.getInstance().getIcon("refresh")); //$NON-NLS-1$
- refreshButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
- finder.refresh(new ViewFinderListener(composite));
- }
- });
-
- wrapper.layout(true);
- } else {
- composite = super.createChildContainer(parent, header, style);
- if (mPaletteMode.isPreview() && mBackground != null) {
- composite.setBackground(mBackground);
- }
- }
- addMenu(composite);
- return composite;
- }
- @Override
- protected void createChildren(Composite parent, Object header) {
- assert categoryToItems != null;
- List<ViewElementDescriptor> list = categoryToItems.get(header);
- if (list == null) {
- assert header.equals("Custom & Library Views");
- addCustomItems(parent);
- return;
- } else {
- for (ViewElementDescriptor desc : list) {
- createItem(parent, desc);
- }
- }
- }
- };
- addMenu(mAccordion);
- for (CLabel headerLabel : mAccordion.getHeaderLabels()) {
- addMenu(headerLabel);
- }
- setLayout(new FillLayout());
-
- // Expand All for icon-only mode, but don't store it as the persistent auto-close mode;
- // when we enter other modes it will read back whatever persistent mode.
- if (mPaletteMode == PaletteMode.ICON_ONLY) {
- mAccordion.expandAll(true);
- mAccordion.setAutoClose(false);
- } else {
- mAccordion.setAutoClose(getSavedAutoCloseMode());
- }
-
- layout(true);
- }
-
- protected void addCustomItems(final Composite parent) {
- final CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
- Collection<String> allViews = finder.getAllViews();
- if (allViews == null) { // Not yet initialized: trigger an async refresh
- finder.refresh(new ViewFinderListener(parent));
- return;
- }
-
- // Remove previous content
- for (Control c : parent.getChildren()) {
- c.dispose();
- }
-
- // Add new views
- for (final String fqcn : allViews) {
- CustomViewDescriptorService service = CustomViewDescriptorService.getInstance();
- ViewElementDescriptor desc = service.getDescriptor(mEditor.getProject(), fqcn);
- if (desc == null) {
- // The descriptor lookup performs validation steps of the class, and may
- // in some cases determine that this is not a view and will return null;
- // guard against that.
- continue;
- }
-
- Control item = createItem(parent, desc);
-
- // Add control-click listener on custom view items to you can warp to
- // (and double click listener too -- the more discoverable, the better.)
- if (item instanceof IconTextItem) {
- IconTextItem it = (IconTextItem) item;
- it.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- if ((e.stateMask & SWT.MOD1) != 0) {
- AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
- }
- }
- });
- }
- }
- }
-
- /* package */ GraphicalEditorPart getEditor() {
- return mEditor;
- }
-
- private Control createItem(Composite parent, ViewElementDescriptor desc) {
- Control item = null;
- switch (mPaletteMode) {
- case SMALL_PREVIEW:
- case TINY_PREVIEW:
- case PREVIEW: {
- ImageDescriptor descriptor = mPreviewIconFactory.getImageDescriptor(desc);
- if (descriptor != null) {
- Image image = descriptor.createImage();
- ImageControl imageControl = new ImageControl(parent, SWT.None, image);
- if (mPaletteMode.isScaledPreview()) {
- // Try to preserve the overall size since rendering sizes typically
- // vary with the dpi - so while the scaling factor for a 160 dpi
- // rendering the scaling factor should be 0.5, for a 320 dpi one the
- // scaling factor should be half that, 0.25.
- float scale = 1.0f;
- if (mPaletteMode == PaletteMode.SMALL_PREVIEW) {
- scale = 0.75f;
- } else if (mPaletteMode == PaletteMode.TINY_PREVIEW) {
- scale = 0.5f;
- }
- ConfigurationChooser chooser = mEditor.getConfigurationChooser();
- int dpi = chooser.getConfiguration().getDensity().getDpiValue();
- while (dpi > 160) {
- scale = scale / 2;
- dpi = dpi / 2;
- }
- imageControl.setScale(scale);
- }
- imageControl.setHoverColor(getDisplay().getSystemColor(SWT.COLOR_WHITE));
- if (mBackground != null) {
- imageControl.setBackground(mBackground);
- }
- String toolTip = desc.getUiName();
- // It appears pretty much none of the descriptors have tooltips
- //String descToolTip = desc.getTooltip();
- //if (descToolTip != null && descToolTip.length() > 0) {
- // toolTip = toolTip + "\n" + descToolTip;
- //}
- imageControl.setToolTipText(toolTip);
-
- item = imageControl;
- } else {
- // Just use an Icon+Text item for these for now
- item = new IconTextItem(parent, desc);
- if (mForeground != null) {
- item.setForeground(mForeground);
- item.setBackground(mBackground);
- }
- }
- break;
- }
- case ICON_TEXT: {
- item = new IconTextItem(parent, desc);
- break;
- }
- case ICON_ONLY: {
- item = new ImageControl(parent, SWT.None, desc.getGenericIcon());
- item.setToolTipText(desc.getUiName());
- break;
- }
- default:
- throw new IllegalArgumentException("Not yet implemented");
- }
-
- final DragSource source = new DragSource(item, DND.DROP_COPY);
- source.setTransfer(new Transfer[] { SimpleXmlTransfer.getInstance() });
- source.addDragListener(new DescDragSourceListener(desc));
- item.addDisposeListener(new DisposeListener() {
- @Override
- public void widgetDisposed(DisposeEvent e) {
- source.dispose();
- }
- });
- addMenu(item);
-
- return item;
- }
-
- /**
- * An Item widget represents one {@link ElementDescriptor} that can be dropped on the
- * GLE2 canvas using drag'n'drop.
- */
- private static class IconTextItem extends CLabel implements MouseTrackListener {
-
- private boolean mMouseIn;
-
- public IconTextItem(Composite parent, ViewElementDescriptor desc) {
- super(parent, SWT.NONE);
- mMouseIn = false;
-
- setText(desc.getUiName());
- setImage(desc.getGenericIcon());
- setToolTipText(desc.getTooltip());
- addMouseTrackListener(this);
- }
-
- @Override
- public int getStyle() {
- int style = super.getStyle();
- if (mMouseIn) {
- style |= SWT.SHADOW_IN;
- }
- return style;
- }
-
- @Override
- public void mouseEnter(MouseEvent e) {
- if (!mMouseIn) {
- mMouseIn = true;
- redraw();
- }
- }
-
- @Override
- public void mouseExit(MouseEvent e) {
- if (mMouseIn) {
- mMouseIn = false;
- redraw();
- }
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- // pass
- }
- }
-
- /**
- * A {@link DragSourceListener} that deals with drag'n'drop of
- * {@link ElementDescriptor}s.
- */
- private class DescDragSourceListener implements DragSourceListener {
- private final ViewElementDescriptor mDesc;
- private SimpleElement[] mElements;
-
- public DescDragSourceListener(ViewElementDescriptor desc) {
- mDesc = desc;
- }
-
- @Override
- public void dragStart(DragSourceEvent e) {
- // See if we can find out the bounds of this element from a preview image.
- // Preview images are created before the drag source listener is notified
- // of the started drag.
- Rect bounds = null;
- Rect dragBounds = null;
-
- createDragImage(e);
- if (mImage != null && !mIsPlaceholder) {
- int width = mImageLayoutBounds.width;
- int height = mImageLayoutBounds.height;
- assert mImageLayoutBounds.x == 0;
- assert mImageLayoutBounds.y == 0;
- bounds = new Rect(0, 0, width, height);
- double scale = mEditor.getCanvasControl().getScale();
- int scaledWidth = (int) (scale * width);
- int scaledHeight = (int) (scale * height);
- int x = -scaledWidth / 2;
- int y = -scaledHeight / 2;
- dragBounds = new Rect(x, y, scaledWidth, scaledHeight);
- }
-
- SimpleElement se = new SimpleElement(
- SimpleXmlTransfer.getFqcn(mDesc),
- null /* parentFqcn */,
- bounds /* bounds */,
- null /* parentBounds */);
- if (mDesc instanceof PaletteMetadataDescriptor) {
- PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
- pm.initializeNew(se);
- }
- mElements = new SimpleElement[] { se };
-
- // Register this as the current dragged data
- GlobalCanvasDragInfo dragInfo = GlobalCanvasDragInfo.getInstance();
- dragInfo.startDrag(
- mElements,
- null /* selection */,
- null /* canvas */,
- null /* removeSource */);
- dragInfo.setDragBounds(dragBounds);
- dragInfo.setDragBaseline(mBaseline);
-
-
- e.doit = true;
- }
-
- @Override
- public void dragSetData(DragSourceEvent e) {
- // Provide the data for the drop when requested by the other side.
- if (SimpleXmlTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = mElements;
- }
- }
-
- @Override
- public void dragFinished(DragSourceEvent e) {
- // Unregister the dragged data.
- GlobalCanvasDragInfo.getInstance().stopDrag();
- mElements = null;
- if (mImage != null) {
- mImage.dispose();
- mImage = null;
- }
- }
-
- // TODO: Figure out the right dimensions to use for rendering.
- // We WILL crop this after rendering, but for performance reasons it would be good
- // not to make it much larger than necessary since to crop this we rely on
- // actually scanning pixels.
-
- /**
- * Width of the rendered preview image (before it is cropped), although the actual
- * width may be smaller (since we also take the device screen's size into account)
- */
- private static final int MAX_RENDER_HEIGHT = 400;
-
- /**
- * Height of the rendered preview image (before it is cropped), although the
- * actual width may be smaller (since we also take the device screen's size into
- * account)
- */
- private static final int MAX_RENDER_WIDTH = 500;
-
- /** Amount of alpha to multiply into the image (divided by 256) */
- private static final int IMG_ALPHA = 128;
-
- /** The image shown during the drag */
- private Image mImage;
- /** The non-effect bounds of the drag image */
- private Rectangle mImageLayoutBounds;
- private int mBaseline = -1;
-
- /**
- * If true, the image is a preview of the view, and if not it is a "fallback"
- * image of some sort, such as a rendering of the palette item itself
- */
- private boolean mIsPlaceholder;
-
- private void createDragImage(DragSourceEvent event) {
- mBaseline = -1;
- Pair<Image, Rectangle> preview = renderPreview();
- if (preview != null) {
- mImage = preview.getFirst();
- mImageLayoutBounds = preview.getSecond();
- } else {
- mImage = null;
- mImageLayoutBounds = null;
- }
-
- mIsPlaceholder = mImage == null;
- if (mIsPlaceholder) {
- // Couldn't render preview (or the preview is a blank image, such as for
- // example the preview of an empty layout), so instead create a placeholder
- // image
- // Render the palette item itself as an image
- Control control = ((DragSource) event.widget).getControl();
- GC gc = new GC(control);
- Point size = control.getSize();
- Display display = getDisplay();
- final Image image = new Image(display, size.x, size.y);
- gc.copyArea(image, 0, 0);
- gc.dispose();
-
- BufferedImage awtImage = SwtUtils.convertToAwt(image);
- if (awtImage != null) {
- awtImage = ImageUtils.createDropShadow(awtImage, 3 /* shadowSize */,
- 0.7f /* shadowAlpha */, 0x000000 /* shadowRgb */);
- mImage = SwtUtils.convertToSwt(display, awtImage, true, IMG_ALPHA);
- } else {
- ImageData data = image.getImageData();
- data.alpha = IMG_ALPHA;
-
- // Changing the ImageData -after- constructing an image on it
- // has no effect, so we have to construct a new image. Luckily these
- // are tiny images.
- mImage = new Image(display, data);
- }
- image.dispose();
- }
-
- event.image = mImage;
-
- if (!mIsPlaceholder) {
- // Shift the drag feedback image up such that it's centered under the
- // mouse pointer
- double scale = mEditor.getCanvasControl().getScale();
- event.offsetX = (int) (scale * mImageLayoutBounds.width / 2);
- event.offsetY = (int) (scale * mImageLayoutBounds.height / 2);
- }
- }
-
- /**
- * Performs the actual rendering of the descriptor into an image and returns the
- * image as well as the layout bounds of the image (not including drop shadow etc)
- */
- private Pair<Image, Rectangle> renderPreview() {
- ViewMetadataRepository repository = ViewMetadataRepository.get();
- RenderMode renderMode = repository.getRenderMode(mDesc.getFullClassName());
- if (renderMode == RenderMode.SKIP) {
- return null;
- }
-
- // Create blank XML document
- Document document = DomUtilities.createEmptyDocument();
-
- // Insert our target view's XML into it as a node
- GraphicalEditorPart editor = getEditor();
- LayoutEditorDelegate layoutEditorDelegate = editor.getEditorDelegate();
-
- String viewName = mDesc.getXmlLocalName();
- Element element = document.createElement(viewName);
-
- // Set up a proper name space
- Attr attr = document.createAttributeNS(XMLNS_URI, XMLNS_ANDROID);
- attr.setValue(ANDROID_URI);
- element.getAttributes().setNamedItemNS(attr);
-
- element.setAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
- element.setAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
-
- // This doesn't apply to all, but doesn't seem to cause harm and makes for a
- // better experience with text-oriented views like buttons and texts
- element.setAttributeNS(ANDROID_URI, ATTR_TEXT,
- DescriptorsUtils.getBasename(mDesc.getUiName()));
-
- // Is this a palette variation?
- if (mDesc instanceof PaletteMetadataDescriptor) {
- PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
- pm.initializeNew(element);
- }
-
- document.appendChild(element);
-
- // Construct UI model from XML
- AndroidTargetData data = layoutEditorDelegate.getEditor().getTargetData();
- DocumentDescriptor documentDescriptor;
- if (data == null) {
- documentDescriptor = new DocumentDescriptor("temp", null/*children*/);//$NON-NLS-1$
- } else {
- documentDescriptor = data.getLayoutDescriptors().getDescriptor();
- }
- UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
- model.setEditor(layoutEditorDelegate.getEditor());
- model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
- model.loadFromXmlNode(document);
-
- // Call the create-hooks such that we for example insert mandatory
- // children into views like the DialerFilter, apply image source attributes
- // to ImageButtons, etc.
- LayoutCanvas canvas = editor.getCanvasControl();
- NodeFactory nodeFactory = canvas.getNodeFactory();
- UiElementNode parent = model.getUiRoot();
- UiElementNode child = parent.getUiChildren().get(0);
- if (child instanceof UiViewElementNode) {
- UiViewElementNode childUiNode = (UiViewElementNode) child;
- NodeProxy childNode = nodeFactory.create(childUiNode);
-
- // Applying create hooks as part of palette render should
- // not trigger model updates
- layoutEditorDelegate.getEditor().setIgnoreXmlUpdate(true);
- try {
- canvas.getRulesEngine().callCreateHooks(layoutEditorDelegate.getEditor(),
- null, childNode, InsertType.CREATE_PREVIEW);
- childNode.applyPendingChanges();
- } catch (Throwable t) {
- AdtPlugin.log(t, "Failed calling creation hooks for widget %1$s", viewName);
- } finally {
- layoutEditorDelegate.getEditor().setIgnoreXmlUpdate(false);
- }
- }
-
- Integer overrideBgColor = null;
- boolean hasTransparency = false;
- LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
- if (layoutLibrary != null &&
- layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR)) {
- // It doesn't matter what the background color is as long as the alpha
- // is 0 (fully transparent). We're using red to make it more obvious if
- // for some reason the background is painted when it shouldn't be.
- overrideBgColor = new Integer(0x00FF0000);
- }
-
- RenderSession session = null;
- try {
- // Use at most the size of the screen for the preview render.
- // This is important since when we fill the size of certain views (like
- // a SeekBar), we want it to at most be the width of the screen, and for small
- // screens the RENDER_WIDTH was wider.
- LayoutLog silentLogger = new LayoutLog();
-
- session = RenderService.create(editor)
- .setModel(model)
- .setMaxRenderSize(MAX_RENDER_WIDTH, MAX_RENDER_HEIGHT)
- .setLog(silentLogger)
- .setOverrideBgColor(overrideBgColor)
- .setDecorations(false)
- .createRenderSession();
- } catch (Throwable t) {
- // Previews can fail for a variety of reasons -- let's not bug
- // the user with it
- return null;
- }
-
- if (session != null) {
- if (session.getResult().isSuccess()) {
- BufferedImage image = session.getImage();
- if (image != null) {
- BufferedImage cropped;
- Rect initialCrop = null;
- ViewInfo viewInfo = null;
-
- List<ViewInfo> viewInfoList = session.getRootViews();
-
- if (viewInfoList != null && viewInfoList.size() > 0) {
- viewInfo = viewInfoList.get(0);
- mBaseline = viewInfo.getBaseLine();
- }
-
- if (viewInfo != null) {
- int x1 = viewInfo.getLeft();
- int x2 = viewInfo.getRight();
- int y2 = viewInfo.getBottom();
- int y1 = viewInfo.getTop();
- initialCrop = new Rect(x1, y1, x2 - x1, y2 - y1);
- }
-
- if (hasTransparency) {
- cropped = ImageUtils.cropBlank(image, initialCrop);
- } else {
- // Find out what the "background" color is such that we can properly
- // crop it out of the image. To do this we pick out a pixel in the
- // bottom right unpainted area. Rather than pick the one in the far
- // bottom corner, we pick one as close to the bounds of the view as
- // possible (but still outside of the bounds), such that we can
- // deal with themes like the dialog theme.
- int edgeX = image.getWidth() -1;
- int edgeY = image.getHeight() -1;
- if (viewInfo != null) {
- if (viewInfo.getRight() < image.getWidth()-1) {
- edgeX = viewInfo.getRight()+1;
- }
- if (viewInfo.getBottom() < image.getHeight()-1) {
- edgeY = viewInfo.getBottom()+1;
- }
- }
- int edgeColor = image.getRGB(edgeX, edgeY);
- cropped = ImageUtils.cropColor(image, edgeColor, initialCrop);
- }
-
- if (cropped != null) {
- int width = initialCrop != null ? initialCrop.w : cropped.getWidth();
- int height = initialCrop != null ? initialCrop.h : cropped.getHeight();
- boolean needsContrast = hasTransparency
- && !ImageUtils.containsDarkPixels(cropped);
- cropped = ImageUtils.createDropShadow(cropped,
- hasTransparency ? 3 : 5 /* shadowSize */,
- !hasTransparency ? 0.6f : needsContrast ? 0.8f : 0.7f/*alpha*/,
- 0x000000 /* shadowRgb */);
-
- double scale = canvas.getScale();
- if (scale != 1L) {
- cropped = ImageUtils.scale(cropped, scale, scale);
- }
-
- Display display = getDisplay();
- int alpha = (!hasTransparency || !needsContrast) ? IMG_ALPHA : -1;
- Image swtImage = SwtUtils.convertToSwt(display, cropped, true, alpha);
- Rectangle imageBounds = new Rectangle(0, 0, width, height);
- return Pair.of(swtImage, imageBounds);
- }
- }
- }
-
- session.dispose();
- }
-
- return null;
- }
-
- /**
- * Utility method to print out the contents of the given XML document. This is
- * really useful when working on the preview code above. I'm including all the
- * code inside a constant false, which means the compiler will omit all the code,
- * but I'd like to leave it in the code base and by doing it this way rather than
- * as commented out code the code won't be accidentally broken.
- */
- @SuppressWarnings("all")
- private void dumpDocument(Document document) {
- // Diagnostics: print out the XML that we're about to render
- if (false) { // Will be omitted by the compiler
- org.apache.xml.serialize.OutputFormat outputFormat =
- new org.apache.xml.serialize.OutputFormat(
- "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$
- outputFormat.setIndent(2);
- outputFormat.setLineWidth(100);
- outputFormat.setIndenting(true);
- outputFormat.setOmitXMLDeclaration(true);
- outputFormat.setOmitDocumentType(true);
- StringWriter stringWriter = new StringWriter();
- // Using FQN here to avoid having an import above, which will result
- // in a deprecation warning, and there isn't a way to annotate a single
- // import element with a SuppressWarnings.
- org.apache.xml.serialize.XMLSerializer serializer =
- new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat);
- serializer.setNamespaces(true);
- try {
- serializer.serialize(document.getDocumentElement());
- System.out.println(stringWriter.toString());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /** Action for switching view modes via radio buttons */
- private class PaletteModeAction extends Action {
- private final PaletteMode mMode;
-
- PaletteModeAction(PaletteMode mode) {
- super(mode.getActionLabel(), IAction.AS_RADIO_BUTTON);
- mMode = mode;
- boolean selected = mMode == mPaletteMode;
- setChecked(selected);
- setEnabled(!selected);
- }
-
- @Override
- public void run() {
- if (isEnabled()) {
- mPaletteMode = mMode;
- refreshPalette();
- savePaletteMode();
- }
- }
- }
-
- /** Action for toggling various checkbox view modes - categories, sorting, etc */
- private class ToggleViewOptionAction extends Action {
- private final int mAction;
- final static int TOGGLE_CATEGORY = 1;
- final static int TOGGLE_ALPHABETICAL = 2;
- final static int TOGGLE_AUTO_CLOSE = 3;
- final static int REFRESH = 4;
- final static int RESET = 5;
-
- ToggleViewOptionAction(String title, int action, boolean checked) {
- super(title, (action == REFRESH || action == RESET) ? IAction.AS_PUSH_BUTTON
- : IAction.AS_CHECK_BOX);
- mAction = action;
- if (checked) {
- setChecked(checked);
- }
- }
-
- @Override
- public void run() {
- switch (mAction) {
- case TOGGLE_CATEGORY:
- mCategories = !mCategories;
- refreshPalette();
- break;
- case TOGGLE_ALPHABETICAL:
- mAlphabetical = !mAlphabetical;
- refreshPalette();
- break;
- case TOGGLE_AUTO_CLOSE:
- mAutoClose = !mAutoClose;
- mAccordion.setAutoClose(mAutoClose);
- break;
- case REFRESH:
- mPreviewIconFactory.refresh();
- refreshPalette();
- break;
- case RESET:
- mAlphabetical = false;
- mCategories = true;
- mAutoClose = true;
- mPaletteMode = PaletteMode.SMALL_PREVIEW;
- refreshPalette();
- break;
- }
- savePaletteMode();
- }
- }
-
- private void addMenu(Control control) {
- control.addMenuDetectListener(new MenuDetectListener() {
- @Override
- public void menuDetected(MenuDetectEvent e) {
- showMenu(e.x, e.y);
- }
- });
- }
-
- private void showMenu(int x, int y) {
- MenuManager manager = new MenuManager() {
- @Override
- public boolean isDynamic() {
- return true;
- }
- };
- boolean previews = previewsAvailable();
- for (PaletteMode mode : PaletteMode.values()) {
- if (mode.isPreview() && !previews) {
- continue;
- }
- manager.add(new PaletteModeAction(mode));
- }
- if (mPaletteMode.isPreview()) {
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Refresh Previews",
- ToggleViewOptionAction.REFRESH,
- false));
- }
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Show Categories",
- ToggleViewOptionAction.TOGGLE_CATEGORY,
- mCategories));
- manager.add(new ToggleViewOptionAction("Sort Alphabetically",
- ToggleViewOptionAction.TOGGLE_ALPHABETICAL,
- mAlphabetical));
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Auto Close Previous",
- ToggleViewOptionAction.TOGGLE_AUTO_CLOSE,
- mAutoClose));
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Reset",
- ToggleViewOptionAction.RESET,
- false));
-
- Menu menu = manager.createContextMenu(PaletteControl.this);
- menu.setLocation(x, y);
- menu.setVisible(true);
- }
-
- private final class ViewFinderListener implements CustomViewFinder.Listener {
- private final Composite mParent;
-
- private ViewFinderListener(Composite parent) {
- mParent = parent;
- }
-
- @Override
- public void viewsUpdated(Collection<String> customViews,
- Collection<String> thirdPartyViews) {
- addCustomItems(mParent);
- mParent.layout(true);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java
deleted file mode 100644
index 629a42f18..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PlayAnimationMenu.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2011 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.FD_RESOURCES;
-import static com.android.SdkConstants.FD_RES_ANIMATOR;
-import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
-
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
-import com.android.resources.ResourceType;
-import com.android.utils.Pair;
-
-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.Separator;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchWindow;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * "Play Animation" context menu which lists available animations in the project and in
- * the framework, as well as a "Create Animation" shortcut, and allows the animation to be
- * run on the selection
- * <p/>
- * TODO: Add transport controls for play/rewind/pause/loop, and (if possible) scrubbing
- */
-public class PlayAnimationMenu extends SubmenuAction {
- /** Associated canvas */
- private final LayoutCanvas mCanvas;
- /** Whether this menu is showing local animations or framework animations */
- private boolean mFramework;
-
- /**
- * Creates a "Play Animation" menu
- *
- * @param canvas associated canvas
- */
- public PlayAnimationMenu(LayoutCanvas canvas) {
- this(canvas, "Play Animation", false);
- }
-
- /**
- * Creates an animation menu; this can be used either for the outer Play animation
- * menu, or the inner frameworks-animations list
- *
- * @param canvas the associated canvas
- * @param title menu item name
- * @param framework true to show the framework animations, false for the project (and
- * nested framework-animation-menu) animations
- */
- private PlayAnimationMenu(LayoutCanvas canvas, String title, boolean framework) {
- super(title);
- mCanvas = canvas;
- mFramework = framework;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- SelectionManager selectionManager = mCanvas.getSelectionManager();
- List<SelectionItem> selection = selectionManager.getSelections();
- if (selection.size() != 1) {
- addDisabledMessageItem("Select exactly one widget");
- return;
- }
-
- GraphicalEditorPart graphicalEditor = mCanvas.getEditorDelegate().getGraphicalEditor();
- if (graphicalEditor.renderingSupports(Capability.PLAY_ANIMATION)) {
- // List of animations
- Collection<String> animationNames = graphicalEditor.getResourceNames(mFramework,
- ResourceType.ANIMATOR);
- if (animationNames.size() > 0) {
- // Sort alphabetically
- List<String> sortedNames = new ArrayList<String>(animationNames);
- Collections.sort(sortedNames);
-
- for (String animation : sortedNames) {
- String title = animation;
- IAction action = new PlayAnimationAction(title, animation, mFramework);
- new ActionContributionItem(action).fill(menu, -1);
- }
-
- new Separator().fill(menu, -1);
- }
-
- if (!mFramework) {
- // Not in the framework submenu: include recent list and create new actions
-
- // "Create New" action
- new ActionContributionItem(new CreateAnimationAction()).fill(menu, -1);
-
- // Framework resources submenu
- new Separator().fill(menu, -1);
- PlayAnimationMenu sub = new PlayAnimationMenu(mCanvas, "Android Builtin", true);
- new ActionContributionItem(sub).fill(menu, -1);
- }
- } else {
- addDisabledMessageItem(
- "Not supported for this SDK version; try changing the Render Target");
- }
- }
-
- private class PlayAnimationAction extends Action {
- private final String mAnimationName;
- private final boolean mIsFrameworkAnim;
-
- public PlayAnimationAction(String title, String animationName, boolean isFrameworkAnim) {
- super(title, IAction.AS_PUSH_BUTTON);
- mAnimationName = animationName;
- mIsFrameworkAnim = isFrameworkAnim;
- }
-
- @Override
- public void run() {
- SelectionManager selectionManager = mCanvas.getSelectionManager();
- List<SelectionItem> selection = selectionManager.getSelections();
- SelectionItem canvasSelection = selection.get(0);
- CanvasViewInfo info = canvasSelection.getViewInfo();
-
- Object viewObject = info.getViewObject();
- if (viewObject != null) {
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- RenderSession session = viewHierarchy.getSession();
- Result r = session.animate(viewObject, mAnimationName, mIsFrameworkAnim,
- new IAnimationListener() {
- private boolean mPendingDrawing = false;
-
- @Override
- public void onNewFrame(RenderSession s) {
- SelectionOverlay selectionOverlay = mCanvas.getSelectionOverlay();
- if (!selectionOverlay.isHiding()) {
- selectionOverlay.setHiding(true);
- }
- HoverOverlay hoverOverlay = mCanvas.getHoverOverlay();
- if (!hoverOverlay.isHiding()) {
- hoverOverlay.setHiding(true);
- }
-
- ImageOverlay imageOverlay = mCanvas.getImageOverlay();
- imageOverlay.setImage(s.getImage(), s.isAlphaChannelImage());
- synchronized (this) {
- if (mPendingDrawing == false) {
- mCanvas.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- synchronized (this) {
- mPendingDrawing = false;
- }
- mCanvas.redraw();
- }
- });
- mPendingDrawing = true;
- }
- }
- }
-
- @Override
- public boolean isCanceled() {
- return false;
- }
-
- @Override
- public void done(Result result) {
- SelectionOverlay selectionOverlay = mCanvas.getSelectionOverlay();
- selectionOverlay.setHiding(false);
- HoverOverlay hoverOverlay = mCanvas.getHoverOverlay();
- hoverOverlay.setHiding(false);
-
- // Must refresh view hierarchy to force objects back to
- // their original positions in case animations have left
- // them elsewhere
- mCanvas.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- GraphicalEditorPart graphicalEditor = mCanvas
- .getEditorDelegate().getGraphicalEditor();
- graphicalEditor.recomputeLayout();
- }
- });
- }
- });
-
- if (!r.isSuccess()) {
- if (r.getErrorMessage() != null) {
- AdtPlugin.log(r.getException(), r.getErrorMessage());
- }
- }
- }
- }
- }
-
- /**
- * Action which brings up the "Create new XML File" wizard, pre-selected with the
- * animation category
- */
- private class CreateAnimationAction extends Action {
- public CreateAnimationAction() {
- super("Create...", IAction.AS_PUSH_BUTTON);
- }
-
- @Override
- public void run() {
- Shell parent = mCanvas.getShell();
- NewXmlFileWizard wizard = new NewXmlFileWizard();
- LayoutEditorDelegate editor = mCanvas.getEditorDelegate();
- IWorkbenchWindow workbenchWindow =
- editor.getEditor().getEditorSite().getWorkbenchWindow();
- IWorkbench workbench = workbenchWindow.getWorkbench();
- String animationDir = FD_RESOURCES + WS_SEP + FD_RES_ANIMATOR;
- Pair<IProject, String> pair = Pair.of(editor.getEditor().getProject(), animationDir);
- IStructuredSelection selection = new StructuredSelection(pair);
- wizard.init(workbench, selection);
- WizardDialog dialog = new WizardDialog(parent, wizard);
- dialog.create();
- dialog.open();
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
deleted file mode 100644
index 5661b2919..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PreviewIconFactory.java
+++ /dev/null
@@ -1,642 +0,0 @@
-/*
- * Copyright (C) 2011 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.DOT_PNG;
-import static com.android.SdkConstants.FQCN_DATE_PICKER;
-import static com.android.SdkConstants.FQCN_EXPANDABLE_LIST_VIEW;
-import static com.android.SdkConstants.FQCN_LIST_VIEW;
-import static com.android.SdkConstants.FQCN_TIME_PICKER;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.rendering.api.StyleResourceValue;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
-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.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.sdklib.IAndroidTarget;
-import com.android.utils.Pair;
-
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.graphics.RGB;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import java.awt.image.BufferedImage;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
-
-import javax.imageio.ImageIO;
-
-/**
- * Factory which can provide preview icons for android views of a particular SDK and
- * editor's configuration chooser
- */
-public class PreviewIconFactory {
- private PaletteControl mPalette;
- private RGB mBackground;
- private RGB mForeground;
- private File mImageDir;
-
- private static final String PREVIEW_INFO_FILE = "preview.properties"; //$NON-NLS-1$
-
- public PreviewIconFactory(PaletteControl palette) {
- mPalette = palette;
- }
-
- /**
- * Resets the state in the preview icon factory such that it will re-fetch information
- * like the theme and SDK (the icons themselves are cached in a directory across IDE
- * session though)
- */
- public void reset() {
- mImageDir = null;
- mBackground = null;
- mForeground = null;
- }
-
- /**
- * Deletes all the persistent state for the current settings such that it will be regenerated
- */
- public void refresh() {
- File imageDir = getImageDir(false);
- if (imageDir != null && imageDir.exists()) {
- File[] files = imageDir.listFiles();
- for (File file : files) {
- file.delete();
- }
- imageDir.delete();
- reset();
- }
- }
-
- /**
- * Returns an image descriptor for the given element descriptor, or null if no image
- * could be computed. The rendering parameters (SDK, theme etc) correspond to those
- * stored in the associated palette.
- *
- * @param desc the element descriptor to get an image for
- * @return an image descriptor, or null if no image could be rendered
- */
- public ImageDescriptor getImageDescriptor(ElementDescriptor desc) {
- File imageDir = getImageDir(false);
- if (!imageDir.exists()) {
- render();
- }
- File file = new File(imageDir, getFileName(desc));
- if (file.exists()) {
- try {
- return ImageDescriptor.createFromURL(file.toURI().toURL());
- } catch (MalformedURLException e) {
- AdtPlugin.log(e, "Could not create image descriptor for %s", file);
- }
- }
-
- return null;
- }
-
- /**
- * Partition the elements in the document according to their rendering preferences;
- * elements that should be skipped are removed, elements that should be rendered alone
- * are placed in their own list, etc
- *
- * @param document the document containing render fragments for the various elements
- * @return
- */
- private List<List<Element>> partitionRenderElements(Document document) {
- List<List<Element>> elements = new ArrayList<List<Element>>();
-
- List<Element> shared = new ArrayList<Element>();
- Element root = document.getDocumentElement();
- elements.add(shared);
-
- ViewMetadataRepository repository = ViewMetadataRepository.get();
-
- NodeList children = root.getChildNodes();
- for (int i = 0, n = children.getLength(); i < n; i++) {
- Node node = children.item(i);
- if (node.getNodeType() == Node.ELEMENT_NODE) {
- Element element = (Element) node;
- String fqn = repository.getFullClassName(element);
- assert fqn.length() > 0 : element.getNodeName();
- RenderMode renderMode = repository.getRenderMode(fqn);
-
- // Temporary special cases
- if (fqn.equals(FQCN_LIST_VIEW) || fqn.equals(FQCN_EXPANDABLE_LIST_VIEW)) {
- if (!mPalette.getEditor().renderingSupports(Capability.ADAPTER_BINDING)) {
- renderMode = RenderMode.SKIP;
- }
- } else if (fqn.equals(FQCN_DATE_PICKER) || fqn.equals(FQCN_TIME_PICKER)) {
- IAndroidTarget renderingTarget = mPalette.getEditor().getRenderingTarget();
- // In Honeycomb, these widgets only render properly in the Holo themes.
- int apiLevel = renderingTarget.getVersion().getApiLevel();
- if (apiLevel == 11) {
- String themeName = mPalette.getCurrentTheme();
- if (themeName == null || !themeName.startsWith("Theme.Holo")) { //$NON-NLS-1$
- // Note - it's possible that the the theme is some other theme
- // such as a user theme which inherits from Theme.Holo and that
- // the render -would- have worked, but it's harder to detect that
- // scenario, so we err on the side of caution and just show an
- // icon + name for the time widgets.
- renderMode = RenderMode.SKIP;
- }
- } else if (apiLevel >= 12) {
- // Currently broken, even for Holo.
- renderMode = RenderMode.SKIP;
- } // apiLevel <= 10 is fine
- }
-
- if (renderMode == RenderMode.ALONE) {
- elements.add(Collections.singletonList(element));
- } else if (renderMode == RenderMode.NORMAL) {
- shared.add(element);
- } else {
- assert renderMode == RenderMode.SKIP;
- }
- }
- }
-
- return elements;
- }
-
- /**
- * Renders ALL the widgets and then extracts image data for each view and saves it on
- * disk
- */
- private boolean render() {
- File imageDir = getImageDir(true);
-
- GraphicalEditorPart editor = mPalette.getEditor();
- LayoutEditorDelegate layoutEditorDelegate = editor.getEditorDelegate();
- LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
- Integer overrideBgColor = null;
- if (layoutLibrary != null) {
- if (layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR)) {
- Pair<RGB, RGB> themeColors = getColorsFromTheme();
- RGB bg = themeColors.getFirst();
- RGB fg = themeColors.getSecond();
- if (bg != null) {
- storeBackground(imageDir, bg, fg);
- overrideBgColor = Integer.valueOf(ImageUtils.rgbToInt(bg, 0xFF));
- }
- }
- }
-
- ViewMetadataRepository repository = ViewMetadataRepository.get();
- Document document = repository.getRenderingConfigDoc();
-
- if (document == null) {
- return false;
- }
-
- // Construct UI model from XML
- AndroidTargetData data = layoutEditorDelegate.getEditor().getTargetData();
- DocumentDescriptor documentDescriptor;
- if (data == null) {
- documentDescriptor = new DocumentDescriptor("temp", null/*children*/);//$NON-NLS-1$
- } else {
- documentDescriptor = data.getLayoutDescriptors().getDescriptor();
- }
- UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
- model.setEditor(layoutEditorDelegate.getEditor());
- model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
-
- Element documentElement = document.getDocumentElement();
- List<List<Element>> elements = partitionRenderElements(document);
- for (List<Element> elementGroup : elements) {
- // Replace the document elements with the current element group
- while (documentElement.getFirstChild() != null) {
- documentElement.removeChild(documentElement.getFirstChild());
- }
- for (Element element : elementGroup) {
- documentElement.appendChild(element);
- }
-
- model.loadFromXmlNode(document);
-
- RenderSession session = null;
- NodeList childNodes = documentElement.getChildNodes();
- try {
- // Important to get these sizes large enough for clients that don't support
- // RenderMode.FULL_EXPAND such as 1.6
- int width = 200;
- int height = childNodes.getLength() == 1 ? 400 : 1600;
-
- session = RenderService.create(editor)
- .setModel(model)
- .setOverrideRenderSize(width, height)
- .setRenderingMode(RenderingMode.FULL_EXPAND)
- .setLog(editor.createRenderLogger("palette"))
- .setOverrideBgColor(overrideBgColor)
- .setDecorations(false)
- .createRenderSession();
- } catch (Throwable t) {
- // If there are internal errors previewing the components just revert to plain
- // icons and labels
- continue;
- }
-
- if (session != null) {
- if (session.getResult().isSuccess()) {
- BufferedImage image = session.getImage();
- if (image != null && image.getWidth() > 0 && image.getHeight() > 0) {
-
- // Fallback for older platforms where we couldn't do background rendering
- // at the beginning of this method
- if (mBackground == null) {
- Pair<RGB, RGB> themeColors = getColorsFromTheme();
- RGB bg = themeColors.getFirst();
- RGB fg = themeColors.getSecond();
-
- if (bg == null) {
- // Just use a pixel from the rendering instead.
- int p = image.getRGB(image.getWidth() - 1, image.getHeight() - 1);
- // However, in this case we don't trust the foreground color
- // even if one was found in the themes; pick one that is guaranteed
- // to contrast with the background
- bg = ImageUtils.intToRgb(p);
- if (ImageUtils.getBrightness(ImageUtils.rgbToInt(bg, 255)) < 128) {
- fg = new RGB(255, 255, 255);
- } else {
- fg = new RGB(0, 0, 0);
- }
- }
- storeBackground(imageDir, bg, fg);
- assert mBackground != null;
- }
-
- List<ViewInfo> viewInfoList = session.getRootViews();
- if (viewInfoList != null && viewInfoList.size() > 0) {
- // We don't render previews under a <merge> so there should
- // only be one root.
- ViewInfo firstRoot = viewInfoList.get(0);
- int parentX = firstRoot.getLeft();
- int parentY = firstRoot.getTop();
- List<ViewInfo> infos = firstRoot.getChildren();
- for (ViewInfo info : infos) {
- Object cookie = info.getCookie();
- if (!(cookie instanceof UiElementNode)) {
- continue;
- }
- UiElementNode node = (UiElementNode) cookie;
- String fileName = getFileName(node);
- File file = new File(imageDir, fileName);
- if (file.exists()) {
- // On Windows, perhaps we need to rename instead?
- file.delete();
- }
- int x1 = parentX + info.getLeft();
- int y1 = parentY + info.getTop();
- int x2 = parentX + info.getRight();
- int y2 = parentY + info.getBottom();
- if (x1 != x2 && y1 != y2) {
- savePreview(file, image, x1, y1, x2, y2);
- }
- }
- }
- }
- } else {
- StringBuilder sb = new StringBuilder();
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node node = childNodes.item(i);
- if (node instanceof Element) {
- Element e = (Element) node;
- String fqn = repository.getFullClassName(e);
- fqn = fqn.substring(fqn.lastIndexOf('.') + 1);
- if (sb.length() > 0) {
- sb.append(", "); //$NON-NLS-1$
- }
- sb.append(fqn);
- }
- }
- AdtPlugin.log(IStatus.WARNING, "Failed to render set of icons for %1$s",
- sb.toString());
-
- if (session.getResult().getException() != null) {
- AdtPlugin.log(session.getResult().getException(),
- session.getResult().getErrorMessage());
- } else if (session.getResult().getErrorMessage() != null) {
- AdtPlugin.log(IStatus.WARNING, session.getResult().getErrorMessage());
- }
- }
-
- session.dispose();
- }
- }
-
- mPalette.getEditor().recomputeLayout();
-
- return true;
- }
-
- /**
- * Look up the background and foreground colors from the theme. May not find either
- * the background or foreground or both, but will always return a pair of possibly
- * null colors.
- *
- * @return a pair of possibly null color descriptions
- */
- @NonNull
- private Pair<RGB, RGB> getColorsFromTheme() {
- RGB background = null;
- RGB foreground = null;
-
- ResourceResolver resources = mPalette.getEditor().getResourceResolver();
- if (resources == null) {
- return Pair.of(background, foreground);
- }
- StyleResourceValue theme = resources.getCurrentTheme();
- if (theme != null) {
- background = resolveThemeColor(resources, "windowBackground"); //$NON-NLS-1$
- if (background == null) {
- background = renderDrawableResource("windowBackground"); //$NON-NLS-1$
- // This causes some harm with some themes: We'll find a color, say black,
- // that isn't actually rendered in the theme. Better to use null here,
- // which will cause the caller to pick a pixel from the observed background
- // instead.
- //if (background == null) {
- // background = resolveThemeColor(resources, "colorBackground"); //$NON-NLS-1$
- //}
- }
- foreground = resolveThemeColor(resources, "textColorPrimary"); //$NON-NLS-1$
- }
-
- // Ensure that the foreground color is suitably distinct from the background color
- if (background != null) {
- int bgRgb = ImageUtils.rgbToInt(background, 0xFF);
- int backgroundBrightness = ImageUtils.getBrightness(bgRgb);
- if (foreground == null) {
- if (backgroundBrightness < 128) {
- foreground = new RGB(255, 255, 255);
- } else {
- foreground = new RGB(0, 0, 0);
- }
- } else {
- int fgRgb = ImageUtils.rgbToInt(foreground, 0xFF);
- int foregroundBrightness = ImageUtils.getBrightness(fgRgb);
- if (Math.abs(backgroundBrightness - foregroundBrightness) < 64) {
- if (backgroundBrightness < 128) {
- foreground = new RGB(255, 255, 255);
- } else {
- foreground = new RGB(0, 0, 0);
- }
- }
- }
- }
-
- return Pair.of(background, foreground);
- }
-
- /**
- * Renders the given resource which should refer to a drawable and returns a
- * representative color value for the drawable (such as the color in the center)
- *
- * @param themeItemName the item in the theme to be looked up and rendered
- * @return a color representing a typical color in the drawable
- */
- private RGB renderDrawableResource(String themeItemName) {
- GraphicalEditorPart editor = mPalette.getEditor();
- ResourceResolver resources = editor.getResourceResolver();
- ResourceValue resourceValue = resources.findItemInTheme(themeItemName);
- BufferedImage image = RenderService.create(editor)
- .setOverrideRenderSize(100, 100)
- .renderDrawable(resourceValue);
- if (image != null) {
- // Use the middle pixel as the color since that works better for gradients;
- // solid colors work too.
- int rgb = image.getRGB(image.getWidth() / 2, image.getHeight() / 2);
- return ImageUtils.intToRgb(rgb);
- }
-
- return null;
- }
-
- private static RGB resolveThemeColor(ResourceResolver resources, String resourceName) {
- ResourceValue textColor = resources.findItemInTheme(resourceName);
- return ResourceHelper.resolveColor(resources, textColor);
- }
-
- private String getFileName(ElementDescriptor descriptor) {
- if (descriptor instanceof PaletteMetadataDescriptor) {
- PaletteMetadataDescriptor pmd = (PaletteMetadataDescriptor) descriptor;
- StringBuilder sb = new StringBuilder();
- String name = pmd.getUiName();
- // Strip out whitespace, parentheses, etc.
- for (int i = 0, n = name.length(); i < n; i++) {
- char c = name.charAt(i);
- if (Character.isLetter(c)) {
- sb.append(c);
- }
- }
- return sb.toString() + DOT_PNG;
- }
- return descriptor.getUiName() + DOT_PNG;
- }
-
- private String getFileName(UiElementNode node) {
- ViewMetadataRepository repository = ViewMetadataRepository.get();
- String fqn = repository.getFullClassName((Element) node.getXmlNode());
- return fqn.substring(fqn.lastIndexOf('.') + 1) + DOT_PNG;
- }
-
- /**
- * Cleans up a name by removing punctuation and whitespace etc to make
- * it a better filename
- * @param name the name to clean
- * @return a cleaned up name
- */
- @NonNull
- private static String cleanup(@Nullable String name) {
- if (name == null) {
- return "";
- }
-
- // Extract just the characters (no whitespace, parentheses, punctuation etc)
- // to ensure that the filename is pretty portable
- StringBuilder sb = new StringBuilder(name.length());
- for (int i = 0; i < name.length(); i++) {
- char c = name.charAt(i);
- if (Character.isJavaIdentifierPart(c)) {
- sb.append(Character.toLowerCase(c));
- }
- }
-
- return sb.toString();
- }
-
- /** Returns the location of a directory containing image previews (which may not exist) */
- private File getImageDir(boolean create) {
- if (mImageDir == null) {
- // Location for plugin-related state data
- IPath pluginState = AdtPlugin.getDefault().getStateLocation();
-
- // We have multiple directories - one for each combination of SDK, theme and device
- // (and later, possibly other qualifiers).
- // These are created -lazily-.
- String targetName = mPalette.getCurrentTarget().hashString();
- String androidTargetNamePrefix = "android-";
- String themeNamePrefix = "Theme.";
- if (targetName.startsWith(androidTargetNamePrefix)) {
- targetName = targetName.substring(androidTargetNamePrefix.length());
- }
- String themeName = mPalette.getCurrentTheme();
- if (themeName == null) {
- themeName = "Theme"; //$NON-NLS-1$
- }
- if (themeName.startsWith(themeNamePrefix)) {
- themeName = themeName.substring(themeNamePrefix.length());
- }
- targetName = cleanup(targetName);
- themeName = cleanup(themeName);
- String deviceName = cleanup(mPalette.getCurrentDevice());
- String dirName = String.format("palette-preview-r16b-%s-%s-%s", targetName,
- themeName, deviceName);
- IPath dirPath = pluginState.append(dirName);
-
- mImageDir = new File(dirPath.toOSString());
- }
-
- if (create && !mImageDir.exists()) {
- mImageDir.mkdirs();
- }
-
- return mImageDir;
- }
-
- private void savePreview(File output, BufferedImage image,
- int left, int top, int right, int bottom) {
- try {
- BufferedImage im = ImageUtils.subImage(image, left, top, right, bottom);
- ImageIO.write(im, "PNG", output); //$NON-NLS-1$
- } catch (IOException e) {
- AdtPlugin.log(e, "Failed writing palette file");
- }
- }
-
- private void storeBackground(File imageDir, RGB bg, RGB fg) {
- mBackground = bg;
- mForeground = fg;
- File file = new File(imageDir, PREVIEW_INFO_FILE);
- String colors = String.format(
- "background=#%02x%02x%02x\nforeground=#%02x%02x%02x\n", //$NON-NLS-1$
- bg.red, bg.green, bg.blue,
- fg.red, fg.green, fg.blue);
- AdtPlugin.writeFile(file, colors);
- }
-
- public RGB getBackgroundColor() {
- if (mBackground == null) {
- initColors();
- }
-
- return mBackground;
- }
-
- public RGB getForegroundColor() {
- if (mForeground == null) {
- initColors();
- }
-
- return mForeground;
- }
-
- public void initColors() {
- try {
- // Already initialized? Foreground can be null which would call
- // initColors again and again, but background is never null after
- // initialization so we use it as the have-initialized flag.
- if (mBackground != null) {
- return;
- }
-
- File imageDir = getImageDir(false);
- if (!imageDir.exists()) {
- render();
-
- // Initialized as part of the render
- if (mBackground != null) {
- return;
- }
- }
-
- File file = new File(imageDir, PREVIEW_INFO_FILE);
- if (file.exists()) {
- Properties properties = new Properties();
- InputStream is = null;
- try {
- is = new BufferedInputStream(new FileInputStream(file));
- properties.load(is);
- } catch (IOException e) {
- AdtPlugin.log(e, "Can't read preview properties");
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // Nothing useful can be done.
- }
- }
- }
-
- String colorString = (String) properties.get("background"); //$NON-NLS-1$
- if (colorString != null) {
- int rgb = ImageUtils.getColor(colorString.trim());
- mBackground = ImageUtils.intToRgb(rgb);
- }
- colorString = (String) properties.get("foreground"); //$NON-NLS-1$
- if (colorString != null) {
- int rgb = ImageUtils.getColor(colorString.trim());
- mForeground = ImageUtils.intToRgb(rgb);
- }
- }
-
- if (mBackground == null) {
- mBackground = new RGB(0, 0, 0);
- }
- // mForeground is allowed to be null.
- } catch (Throwable t) {
- AdtPlugin.log(t, "Cannot initialize preview color settings");
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLogger.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLogger.java
deleted file mode 100644
index 8548830bd..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLogger.java
+++ /dev/null
@@ -1,327 +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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.RenderSecurityManager;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.eclipse.adt.AdtPlugin;
-
-import org.eclipse.core.runtime.IStatus;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A {@link LayoutLog} which records the problems it encounters and offers them as a
- * single summary at the end
- */
-public class RenderLogger extends LayoutLog {
- static final String TAG_MISSING_DIMENSION = "missing.dimension"; //$NON-NLS-1$
-
- private final String mName;
- private List<String> mFidelityWarnings;
- private List<String> mWarnings;
- private List<String> mErrors;
- private boolean mHaveExceptions;
- private List<String> mTags;
- private List<Throwable> mTraces;
- private static Set<String> sIgnoredFidelityWarnings;
- private final Object mCredential;
-
- /** Construct a logger for the given named layout */
- RenderLogger(String name, Object credential) {
- mName = name;
- mCredential = credential;
- }
-
- /**
- * Are there any logged errors or warnings during the render?
- *
- * @return true if there were problems during the render
- */
- public boolean hasProblems() {
- return mFidelityWarnings != null || mErrors != null || mWarnings != null ||
- mHaveExceptions;
- }
-
- /**
- * Returns a list of traces encountered during rendering, or null if none
- *
- * @return a list of traces encountered during rendering, or null if none
- */
- @Nullable
- public List<Throwable> getFirstTrace() {
- return mTraces;
- }
-
- /**
- * Returns a (possibly multi-line) description of all the problems
- *
- * @param includeFidelityWarnings if true, include fidelity warnings in the problem
- * summary
- * @return a string describing the rendering problems
- */
- @NonNull
- public String getProblems(boolean includeFidelityWarnings) {
- StringBuilder sb = new StringBuilder();
-
- if (mErrors != null) {
- for (String error : mErrors) {
- sb.append(error).append('\n');
- }
- }
-
- if (mWarnings != null) {
- for (String warning : mWarnings) {
- sb.append(warning).append('\n');
- }
- }
-
- if (includeFidelityWarnings && mFidelityWarnings != null) {
- sb.append("The graphics preview in the layout editor may not be accurate:\n");
- for (String warning : mFidelityWarnings) {
- sb.append("* ");
- sb.append(warning).append('\n');
- }
- }
-
- if (mHaveExceptions) {
- sb.append("Exception details are logged in Window > Show View > Error Log");
- }
-
- return sb.toString();
- }
-
- /**
- * Returns the fidelity warnings
- *
- * @return the fidelity warnings
- */
- @Nullable
- public List<String> getFidelityWarnings() {
- return mFidelityWarnings;
- }
-
- // ---- extends LayoutLog ----
-
- @Override
- public void error(String tag, String message, Object data) {
- String description = describe(message);
-
- appendToIdeLog(null, IStatus.ERROR, description);
-
- // Workaround: older layout libraries don't provide a tag for this error
- if (tag == null && message != null
- && message.startsWith("Failed to find style ")) { //$NON-NLS-1$
- tag = LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR;
- }
-
- addError(tag, description);
- }
-
- @Override
- public void error(String tag, String message, Throwable throwable, Object data) {
- String description = describe(message);
- appendToIdeLog(throwable, IStatus.ERROR, description);
-
- if (throwable != null) {
- if (throwable instanceof ClassNotFoundException) {
- // The project callback is given a chance to resolve classes,
- // and when it fails, it will record it in its own list which
- // is displayed in a special way (with action hyperlinks etc).
- // Therefore, include these messages in the visible render log,
- // especially since the user message from a ClassNotFoundException
- // is really not helpful (it just lists the class name without
- // even mentioning that it is a class-not-found exception.)
- return;
- }
-
- if (description.equals(throwable.getLocalizedMessage()) ||
- description.equals(throwable.getMessage())) {
- description = "Exception raised during rendering: " + description;
- }
- recordThrowable(throwable);
- mHaveExceptions = true;
- }
-
- addError(tag, description);
- }
-
- /**
- * Record that the given exception was encountered during rendering
- *
- * @param throwable the exception that was raised
- */
- public void recordThrowable(@NonNull Throwable throwable) {
- if (mTraces == null) {
- mTraces = new ArrayList<Throwable>();
- }
- mTraces.add(throwable);
- }
-
- @Override
- public void warning(String tag, String message, Object data) {
- String description = describe(message);
-
- boolean log = true;
- if (TAG_RESOURCES_FORMAT.equals(tag)) {
- if (description.equals("You must supply a layout_width attribute.") //$NON-NLS-1$
- || description.equals("You must supply a layout_height attribute.")) {//$NON-NLS-1$
- tag = TAG_MISSING_DIMENSION;
- log = false;
- }
- }
-
- if (log) {
- appendToIdeLog(null, IStatus.WARNING, description);
- }
-
- addWarning(tag, description);
- }
-
- @Override
- public void fidelityWarning(String tag, String message, Throwable throwable, Object data) {
- if (sIgnoredFidelityWarnings != null && sIgnoredFidelityWarnings.contains(message)) {
- return;
- }
-
- String description = describe(message);
- appendToIdeLog(throwable, IStatus.ERROR, description);
-
- if (throwable != null) {
- mHaveExceptions = true;
- }
-
- addFidelityWarning(tag, description);
- }
-
- /**
- * Ignore the given render fidelity warning for the current session
- *
- * @param message the message to be ignored for this session
- */
- public static void ignoreFidelityWarning(String message) {
- if (sIgnoredFidelityWarnings == null) {
- sIgnoredFidelityWarnings = new HashSet<String>();
- }
- sIgnoredFidelityWarnings.add(message);
- }
-
- @NonNull
- private String describe(@Nullable String message) {
- if (message == null) {
- return "";
- } else {
- return message;
- }
- }
-
- private void addWarning(String tag, String description) {
- if (mWarnings == null) {
- mWarnings = new ArrayList<String>();
- } else if (mWarnings.contains(description)) {
- // Avoid duplicates
- return;
- }
- mWarnings.add(description);
- addTag(tag);
- }
-
- private void addError(String tag, String description) {
- if (mErrors == null) {
- mErrors = new ArrayList<String>();
- } else if (mErrors.contains(description)) {
- // Avoid duplicates
- return;
- }
- mErrors.add(description);
- addTag(tag);
- }
-
- private void addFidelityWarning(String tag, String description) {
- if (mFidelityWarnings == null) {
- mFidelityWarnings = new ArrayList<String>();
- } else if (mFidelityWarnings.contains(description)) {
- // Avoid duplicates
- return;
- }
- mFidelityWarnings.add(description);
- addTag(tag);
- }
-
- // ---- Tags ----
-
- private void addTag(String tag) {
- if (tag != null) {
- if (mTags == null) {
- mTags = new ArrayList<String>();
- }
- mTags.add(tag);
- }
- }
-
- /**
- * Returns true if the given tag prefix has been seen
- *
- * @param prefix the tag prefix to look for
- * @return true iff any tags with the given prefix was seen during the render
- */
- public boolean seenTagPrefix(String prefix) {
- if (mTags != null) {
- for (String tag : mTags) {
- if (tag.startsWith(prefix)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Returns true if the given tag has been seen
- *
- * @param tag the tag to look for
- * @return true iff the tag was seen during the render
- */
- public boolean seenTag(String tag) {
- if (mTags != null) {
- return mTags.contains(tag);
- } else {
- return false;
- }
- }
-
- // Append the given message to the ADT log. Bypass the sandbox if necessary
- // such that we can write to the log file.
- private void appendToIdeLog(Throwable throwable, int severity, String description) {
- boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
- try {
- if (throwable != null) {
- AdtPlugin.log(throwable, "%1$s: %2$s", mName, description);
- } else {
- AdtPlugin.log(severity, "%1$s: %2$s", mName, description);
- }
- } finally {
- RenderSecurityManager.exitSafeRegion(token);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java
deleted file mode 100644
index 5621d5f17..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreview.java
+++ /dev/null
@@ -1,1333 +0,0 @@
-/*
- * Copyright (C) 2012 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_STYLE_RESOURCE_PREFIX;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
-import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.MASK_RENDERING;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SMALL_SHADOW_SIZE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.DEFAULT;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.INCLUDES;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
-import com.android.ide.common.resources.ResourceFile;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
-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.DocumentDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationClient;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.NestedConfiguration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.VaryingConfiguration;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
-import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.ide.eclipse.adt.io.IFileWrapper;
-import com.android.io.IAbstractFile;
-import com.android.resources.Density;
-import com.android.resources.ResourceType;
-import com.android.resources.ScreenOrientation;
-import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.devices.Device;
-import com.android.sdklib.devices.Screen;
-import com.android.sdklib.devices.State;
-import com.android.utils.SdkUtils;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.jobs.IJobChangeEvent;
-import org.eclipse.core.runtime.jobs.IJobChangeListener;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Region;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.ISharedImages;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.progress.UIJob;
-import org.w3c.dom.Document;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.lang.ref.SoftReference;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Represents a preview rendering of a given configuration
- */
-public class RenderPreview implements IJobChangeListener {
- /** Whether previews should use large shadows */
- static final boolean LARGE_SHADOWS = false;
-
- /**
- * Still doesn't work; get exceptions from layoutlib:
- * java.lang.IllegalStateException: After scene creation, #init() must be called
- * at com.android.layoutlib.bridge.impl.RenderAction.acquire(RenderAction.java:151)
- * <p>
- * TODO: Investigate.
- */
- private static final boolean RENDER_ASYNC = false;
-
- /**
- * Height of the toolbar shown over a preview during hover. Needs to be
- * large enough to accommodate icons below.
- */
- private static final int HEADER_HEIGHT = 20;
-
- /** Whether to dump out rendering failures of the previews to the log */
- private static final boolean DUMP_RENDER_DIAGNOSTICS = false;
-
- /** Extra error checking in debug mode */
- private static final boolean DEBUG = false;
-
- private static final Image EDIT_ICON;
- private static final Image ZOOM_IN_ICON;
- private static final Image ZOOM_OUT_ICON;
- private static final Image CLOSE_ICON;
- private static final int EDIT_ICON_WIDTH;
- private static final int ZOOM_IN_ICON_WIDTH;
- private static final int ZOOM_OUT_ICON_WIDTH;
- private static final int CLOSE_ICON_WIDTH;
- static {
- ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
- IconFactory icons = IconFactory.getInstance();
- CLOSE_ICON = sharedImages.getImage(ISharedImages.IMG_ETOOL_DELETE);
- EDIT_ICON = icons.getIcon("editPreview"); //$NON-NLS-1$
- ZOOM_IN_ICON = icons.getIcon("zoomplus"); //$NON-NLS-1$
- ZOOM_OUT_ICON = icons.getIcon("zoomminus"); //$NON-NLS-1$
- CLOSE_ICON_WIDTH = CLOSE_ICON.getImageData().width;
- EDIT_ICON_WIDTH = EDIT_ICON.getImageData().width;
- ZOOM_IN_ICON_WIDTH = ZOOM_IN_ICON.getImageData().width;
- ZOOM_OUT_ICON_WIDTH = ZOOM_OUT_ICON.getImageData().width;
- }
-
- /** The configuration being previewed */
- private @NonNull Configuration mConfiguration;
-
- /** Configuration to use if we have an alternate input to be rendered */
- private @NonNull Configuration mAlternateConfiguration;
-
- /** The associated manager */
- private final @NonNull RenderPreviewManager mManager;
- private final @NonNull LayoutCanvas mCanvas;
-
- private @NonNull SoftReference<ResourceResolver> mResourceResolver =
- new SoftReference<ResourceResolver>(null);
- private @Nullable Job mJob;
- private @Nullable Image mThumbnail;
- private @Nullable String mDisplayName;
- private int mWidth;
- private int mHeight;
- private int mX;
- private int mY;
- private int mTitleHeight;
- private double mScale = 1.0;
- private double mAspectRatio;
-
- /** If non null, points to a separate file containing the source */
- private @Nullable IFile mAlternateInput;
-
- /** If included within another layout, the name of that outer layout */
- private @Nullable Reference mIncludedWithin;
-
- /** Whether the mouse is actively hovering over this preview */
- private boolean mActive;
-
- /**
- * Whether this preview cannot be rendered because of a model error - such
- * as an invalid configuration, a missing resource, an error in the XML
- * markup, etc. If non null, contains the error message (or a blank string
- * if not known), and null if the render was successful.
- */
- private String mError;
-
- /** Whether in the current layout, this preview is visible */
- private boolean mVisible;
-
- /** Whether the configuration has changed and needs to be refreshed the next time
- * this preview made visible. This corresponds to the change flags in
- * {@link ConfigurationClient}. */
- private int mDirty;
-
- /**
- * Creates a new {@linkplain RenderPreview}
- *
- * @param manager the manager
- * @param canvas canvas where preview is painted
- * @param configuration the associated configuration
- * @param width the initial width to use for the preview
- * @param height the initial height to use for the preview
- */
- private RenderPreview(
- @NonNull RenderPreviewManager manager,
- @NonNull LayoutCanvas canvas,
- @NonNull Configuration configuration) {
- mManager = manager;
- mCanvas = canvas;
- mConfiguration = configuration;
- updateSize();
-
- // Should only attempt to create configurations for fully configured devices
- assert mConfiguration.getDevice() != null
- && mConfiguration.getDeviceState() != null
- && mConfiguration.getLocale() != null
- && mConfiguration.getTarget() != null
- && mConfiguration.getTheme() != null
- && mConfiguration.getFullConfig() != null
- && mConfiguration.getFullConfig().getScreenSizeQualifier() != null :
- mConfiguration;
- }
-
- /**
- * Sets the configuration to use for this preview
- *
- * @param configuration the new configuration
- */
- public void setConfiguration(@NonNull Configuration configuration) {
- mConfiguration = configuration;
- }
-
- /**
- * Gets the scale being applied to the thumbnail
- *
- * @return the scale being applied to the thumbnail
- */
- public double getScale() {
- return mScale;
- }
-
- /**
- * Sets the scale to apply to the thumbnail
- *
- * @param scale the factor to scale the thumbnail picture by
- */
- public void setScale(double scale) {
- disposeThumbnail();
- mScale = scale;
- }
-
- /**
- * Returns the aspect ratio of this render preview
- *
- * @return the aspect ratio
- */
- public double getAspectRatio() {
- return mAspectRatio;
- }
-
- /**
- * Returns whether the preview is actively hovered
- *
- * @return whether the mouse is hovering over the preview
- */
- public boolean isActive() {
- return mActive;
- }
-
- /**
- * Sets whether the preview is actively hovered
- *
- * @param active if the mouse is hovering over the preview
- */
- public void setActive(boolean active) {
- mActive = active;
- }
-
- /**
- * Returns whether the preview is visible. Previews that are off
- * screen are typically marked invisible during layout, which means we don't
- * have to expend effort computing preview thumbnails etc
- *
- * @return true if the preview is visible
- */
- public boolean isVisible() {
- return mVisible;
- }
-
- /**
- * Returns whether this preview represents a forked layout
- *
- * @return true if this preview represents a separate file
- */
- public boolean isForked() {
- return mAlternateInput != null || mIncludedWithin != null;
- }
-
- /**
- * Returns the file to be used for this preview, or null if this is not a
- * forked layout meaning that the file is the one used in the chooser
- *
- * @return the file or null for non-forked layouts
- */
- @Nullable
- public IFile getAlternateInput() {
- if (mAlternateInput != null) {
- return mAlternateInput;
- } else if (mIncludedWithin != null) {
- return mIncludedWithin.getFile();
- }
-
- return null;
- }
-
- /**
- * Returns the area of this render preview, PRIOR to scaling
- *
- * @return the area (width times height without scaling)
- */
- int getArea() {
- return mWidth * mHeight;
- }
-
- /**
- * Sets whether the preview is visible. Previews that are off
- * screen are typically marked invisible during layout, which means we don't
- * have to expend effort computing preview thumbnails etc
- *
- * @param visible whether this preview is visible
- */
- public void setVisible(boolean visible) {
- if (visible != mVisible) {
- mVisible = visible;
- if (mVisible) {
- if (mDirty != 0) {
- // Just made the render preview visible:
- configurationChanged(mDirty); // schedules render
- } else {
- updateForkStatus();
- mManager.scheduleRender(this);
- }
- } else {
- dispose();
- }
- }
- }
-
- /**
- * Sets the layout position relative to the top left corner of the preview
- * area, in control coordinates
- */
- void setPosition(int x, int y) {
- mX = x;
- mY = y;
- }
-
- /**
- * Gets the layout X position relative to the top left corner of the preview
- * area, in control coordinates
- */
- int getX() {
- return mX;
- }
-
- /**
- * Gets the layout Y position relative to the top left corner of the preview
- * area, in control coordinates
- */
- int getY() {
- return mY;
- }
-
- /** Determine whether this configuration has a better match in a different layout file */
- private void updateForkStatus() {
- ConfigurationChooser chooser = mManager.getChooser();
- FolderConfiguration config = mConfiguration.getFullConfig();
- if (mAlternateInput != null && chooser.isBestMatchFor(mAlternateInput, config)) {
- return;
- }
-
- mAlternateInput = null;
- IFile editedFile = chooser.getEditedFile();
- if (editedFile != null) {
- if (!chooser.isBestMatchFor(editedFile, config)) {
- ProjectResources resources = chooser.getResources();
- if (resources != null) {
- ResourceFile best = resources.getMatchingFile(editedFile.getName(),
- ResourceType.LAYOUT, config);
- if (best != null) {
- IAbstractFile file = best.getFile();
- if (file instanceof IFileWrapper) {
- mAlternateInput = ((IFileWrapper) file).getIFile();
- } else if (file instanceof File) {
- mAlternateInput = AdtUtils.fileToIFile(((File) file));
- }
- }
- }
- if (mAlternateInput != null) {
- mAlternateConfiguration = Configuration.create(mConfiguration,
- mAlternateInput);
- }
- }
- }
- }
-
- /**
- * Creates a new {@linkplain RenderPreview}
- *
- * @param manager the manager
- * @param configuration the associated configuration
- * @return a new configuration
- */
- @NonNull
- public static RenderPreview create(
- @NonNull RenderPreviewManager manager,
- @NonNull Configuration configuration) {
- LayoutCanvas canvas = manager.getCanvas();
- return new RenderPreview(manager, canvas, configuration);
- }
-
- /**
- * Throws away this preview: cancels any pending rendering jobs and disposes
- * of image resources etc
- */
- public void dispose() {
- disposeThumbnail();
-
- if (mJob != null) {
- mJob.cancel();
- mJob = null;
- }
- }
-
- /** Disposes the thumbnail rendering. */
- void disposeThumbnail() {
- if (mThumbnail != null) {
- mThumbnail.dispose();
- mThumbnail = null;
- }
- }
-
- /**
- * Returns the display name of this preview
- *
- * @return the name of the preview
- */
- @NonNull
- public String getDisplayName() {
- if (mDisplayName == null) {
- String displayName = getConfiguration().getDisplayName();
- if (displayName == null) {
- // No display name: this must be the configuration used by default
- // for the view which is originally displayed (before adding thumbnails),
- // and you've switched away to something else; now we need to display a name
- // for this original configuration. For now, just call it "Original"
- return "Original";
- }
-
- return displayName;
- }
-
- return mDisplayName;
- }
-
- /**
- * Sets the display name of this preview. By default, the display name is
- * the display name of the configuration, but it can be overridden by calling
- * this setter (which only sets the preview name, without editing the configuration.)
- *
- * @param displayName the new display name
- */
- public void setDisplayName(@NonNull String displayName) {
- mDisplayName = displayName;
- }
-
- /**
- * Sets an inclusion context to use for this layout, if any. This will render
- * the configuration preview as the outer layout with the current layout
- * embedded within.
- *
- * @param includedWithin a reference to a layout which includes this one
- */
- public void setIncludedWithin(Reference includedWithin) {
- mIncludedWithin = includedWithin;
- }
-
- /**
- * Request a new render after the given delay
- *
- * @param delay the delay to wait before starting the render job
- */
- public void render(long delay) {
- Job job = mJob;
- if (job != null) {
- job.cancel();
- }
- if (RENDER_ASYNC) {
- job = new AsyncRenderJob();
- } else {
- job = new RenderJob();
- }
- job.schedule(delay);
- job.addJobChangeListener(this);
- mJob = job;
- }
-
- /** Render immediately */
- private void renderSync() {
- GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor();
- if (editor.getReadyLayoutLib(false /*displayError*/) == null) {
- // Don't attempt to render when there is no ready layout library: most likely
- // the targets are loading/reloading.
- return;
- }
-
- disposeThumbnail();
-
- Configuration configuration =
- mAlternateInput != null && mAlternateConfiguration != null
- ? mAlternateConfiguration : mConfiguration;
- ResourceResolver resolver = getResourceResolver(configuration);
- RenderService renderService = RenderService.create(editor, configuration, resolver);
-
- if (mIncludedWithin != null) {
- renderService.setIncludedWithin(mIncludedWithin);
- }
-
- if (mAlternateInput != null) {
- IAndroidTarget target = editor.getRenderingTarget();
- AndroidTargetData data = null;
- if (target != null) {
- Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- data = sdk.getTargetData(target);
- }
- }
-
- // Construct UI model from XML
- DocumentDescriptor documentDescriptor;
- if (data == null) {
- documentDescriptor = new DocumentDescriptor("temp", null);//$NON-NLS-1$
- } else {
- documentDescriptor = data.getLayoutDescriptors().getDescriptor();
- }
- UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
- model.setEditor(mCanvas.getEditorDelegate().getEditor());
- model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
-
- Document document = DomUtilities.getDocument(mAlternateInput);
- if (document == null) {
- mError = "No document";
- createErrorThumbnail();
- return;
- }
- model.loadFromXmlNode(document);
- renderService.setModel(model);
- } else {
- renderService.setModel(editor.getModel());
- }
- RenderLogger log = editor.createRenderLogger(getDisplayName());
- renderService.setLog(log);
- RenderSession session = renderService.createRenderSession();
- Result render = session.render(1000);
-
- if (DUMP_RENDER_DIAGNOSTICS) {
- if (log.hasProblems() || !render.isSuccess()) {
- AdtPlugin.log(IStatus.ERROR, "Found problems rendering preview "
- + getDisplayName() + ": "
- + render.getErrorMessage() + " : "
- + log.getProblems(false));
- Throwable exception = render.getException();
- if (exception != null) {
- AdtPlugin.log(exception, "Failure rendering preview " + getDisplayName());
- }
- }
- }
-
- if (render.isSuccess()) {
- mError = null;
- } else {
- mError = render.getErrorMessage();
- if (mError == null) {
- mError = "";
- }
- }
-
- if (render.getStatus() == Status.ERROR_TIMEOUT) {
- // TODO: Special handling? schedule update again later
- return;
- }
- if (render.isSuccess()) {
- BufferedImage image = session.getImage();
- if (image != null) {
- createThumbnail(image);
- }
- }
-
- if (mError != null) {
- createErrorThumbnail();
- }
- }
-
- private ResourceResolver getResourceResolver(Configuration configuration) {
- ResourceResolver resourceResolver = mResourceResolver.get();
- if (resourceResolver != null) {
- return resourceResolver;
- }
-
- GraphicalEditorPart graphicalEditor = mCanvas.getEditorDelegate().getGraphicalEditor();
- String theme = configuration.getTheme();
- if (theme == null) {
- return null;
- }
-
- Map<ResourceType, Map<String, ResourceValue>> configuredFrameworkRes = null;
- Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes = null;
-
- FolderConfiguration config = configuration.getFullConfig();
- IAndroidTarget target = graphicalEditor.getRenderingTarget();
- ResourceRepository frameworkRes = null;
- if (target != null) {
- Sdk sdk = Sdk.getCurrent();
- if (sdk == null) {
- return null;
- }
- AndroidTargetData data = sdk.getTargetData(target);
-
- if (data != null) {
- // TODO: SHARE if possible
- frameworkRes = data.getFrameworkResources();
- configuredFrameworkRes = frameworkRes.getConfiguredResources(config);
- } else {
- return null;
- }
- } else {
- return null;
- }
- assert configuredFrameworkRes != null;
-
-
- // get the resources of the file's project.
- ProjectResources projectRes = ResourceManager.getInstance().getProjectResources(
- graphicalEditor.getProject());
- configuredProjectRes = projectRes.getConfiguredResources(config);
-
- if (!theme.startsWith(PREFIX_RESOURCE_REF)) {
- if (frameworkRes.hasResourceItem(ANDROID_STYLE_RESOURCE_PREFIX + theme)) {
- theme = ANDROID_STYLE_RESOURCE_PREFIX + theme;
- } else {
- theme = STYLE_RESOURCE_PREFIX + theme;
- }
- }
-
- resourceResolver = ResourceResolver.create(
- configuredProjectRes, configuredFrameworkRes,
- ResourceHelper.styleToTheme(theme),
- ResourceHelper.isProjectStyle(theme));
- mResourceResolver = new SoftReference<ResourceResolver>(resourceResolver);
- return resourceResolver;
- }
-
- /**
- * Sets the new image of the preview and generates a thumbnail
- *
- * @param image the full size image
- */
- void createThumbnail(BufferedImage image) {
- if (image == null) {
- mThumbnail = null;
- return;
- }
-
- ImageOverlay imageOverlay = mCanvas.getImageOverlay();
- boolean drawShadows = imageOverlay == null || imageOverlay.getShowDropShadow();
- double scale = getWidth() / (double) image.getWidth();
- int shadowSize;
- if (LARGE_SHADOWS) {
- shadowSize = drawShadows ? SHADOW_SIZE : 0;
- } else {
- shadowSize = drawShadows ? SMALL_SHADOW_SIZE : 0;
- }
- if (scale < 1.0) {
- if (LARGE_SHADOWS) {
- image = ImageUtils.scale(image, scale, scale,
- shadowSize, shadowSize);
- if (drawShadows) {
- ImageUtils.drawRectangleShadow(image, 0, 0,
- image.getWidth() - shadowSize,
- image.getHeight() - shadowSize);
- }
- } else {
- image = ImageUtils.scale(image, scale, scale,
- shadowSize, shadowSize);
- if (drawShadows) {
- ImageUtils.drawSmallRectangleShadow(image, 0, 0,
- image.getWidth() - shadowSize,
- image.getHeight() - shadowSize);
- }
- }
- }
-
- mThumbnail = SwtUtils.convertToSwt(mCanvas.getDisplay(), image,
- true /* transferAlpha */, -1);
- }
-
- void createErrorThumbnail() {
- int shadowSize = LARGE_SHADOWS ? SHADOW_SIZE : SMALL_SHADOW_SIZE;
- int width = getWidth();
- int height = getHeight();
- BufferedImage image = new BufferedImage(width + shadowSize, height + shadowSize,
- BufferedImage.TYPE_INT_ARGB);
-
- Graphics2D g = image.createGraphics();
- g.setColor(new java.awt.Color(0xfffbfcc6));
- g.fillRect(0, 0, width, height);
-
- g.dispose();
-
- ImageOverlay imageOverlay = mCanvas.getImageOverlay();
- boolean drawShadows = imageOverlay == null || imageOverlay.getShowDropShadow();
- if (drawShadows) {
- if (LARGE_SHADOWS) {
- ImageUtils.drawRectangleShadow(image, 0, 0,
- image.getWidth() - SHADOW_SIZE,
- image.getHeight() - SHADOW_SIZE);
- } else {
- ImageUtils.drawSmallRectangleShadow(image, 0, 0,
- image.getWidth() - SMALL_SHADOW_SIZE,
- image.getHeight() - SMALL_SHADOW_SIZE);
- }
- }
-
- mThumbnail = SwtUtils.convertToSwt(mCanvas.getDisplay(), image,
- true /* transferAlpha */, -1);
- }
-
- private static double getScale(int width, int height) {
- int maxWidth = RenderPreviewManager.getMaxWidth();
- int maxHeight = RenderPreviewManager.getMaxHeight();
- if (width > 0 && height > 0
- && (width > maxWidth || height > maxHeight)) {
- if (width >= height) { // landscape
- return maxWidth / (double) width;
- } else { // portrait
- return maxHeight / (double) height;
- }
- }
-
- return 1.0;
- }
-
- /**
- * Returns the width of the preview, in pixels
- *
- * @return the width in pixels
- */
- public int getWidth() {
- return (int) (mWidth * mScale * RenderPreviewManager.getScale());
- }
-
- /**
- * Returns the height of the preview, in pixels
- *
- * @return the height in pixels
- */
- public int getHeight() {
- return (int) (mHeight * mScale * RenderPreviewManager.getScale());
- }
-
- /**
- * Handles clicks within the preview (x and y are positions relative within the
- * preview
- *
- * @param x the x coordinate within the preview where the click occurred
- * @param y the y coordinate within the preview where the click occurred
- * @return true if this preview handled (and therefore consumed) the click
- */
- public boolean click(int x, int y) {
- if (y >= mTitleHeight && y < mTitleHeight + HEADER_HEIGHT) {
- int left = 0;
- left += CLOSE_ICON_WIDTH;
- if (x <= left) {
- // Delete
- mManager.deletePreview(this);
- return true;
- }
- left += ZOOM_IN_ICON_WIDTH;
- if (x <= left) {
- // Zoom in
- mScale = mScale * (1 / 0.5);
- if (Math.abs(mScale-1.0) < 0.0001) {
- mScale = 1.0;
- }
-
- render(0);
- mManager.layout(true);
- mCanvas.redraw();
- return true;
- }
- left += ZOOM_OUT_ICON_WIDTH;
- if (x <= left) {
- // Zoom out
- mScale = mScale * (0.5 / 1);
- if (Math.abs(mScale-1.0) < 0.0001) {
- mScale = 1.0;
- }
- render(0);
-
- mManager.layout(true);
- mCanvas.redraw();
- return true;
- }
- left += EDIT_ICON_WIDTH;
- if (x <= left) {
- // Edit. For now, just rename
- InputDialog d = new InputDialog(
- AdtPlugin.getShell(),
- "Rename Preview", // title
- "Name:",
- getDisplayName(),
- null);
- if (d.open() == Window.OK) {
- String newName = d.getValue();
- mConfiguration.setDisplayName(newName);
- if (mDescription != null) {
- mManager.rename(mDescription, newName);
- }
- mCanvas.redraw();
- }
-
- return true;
- }
-
- // Clicked anywhere else on header
- // Perhaps open Edit dialog here?
- }
-
- mManager.switchTo(this);
- return true;
- }
-
- /**
- * Paints the preview at the given x/y position
- *
- * @param gc the graphics context to paint it into
- * @param x the x coordinate to paint the preview at
- * @param y the y coordinate to paint the preview at
- */
- void paint(GC gc, int x, int y) {
- mTitleHeight = paintTitle(gc, x, y, true /*showFile*/);
- y += mTitleHeight;
- y += 2;
-
- int width = getWidth();
- int height = getHeight();
- if (mThumbnail != null && mError == null) {
- gc.drawImage(mThumbnail, x, y);
-
- if (mActive) {
- int oldWidth = gc.getLineWidth();
- gc.setLineWidth(3);
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_LIST_SELECTION));
- gc.drawRectangle(x - 1, y - 1, width + 2, height + 2);
- gc.setLineWidth(oldWidth);
- }
- } else if (mError != null) {
- if (mThumbnail != null) {
- gc.drawImage(mThumbnail, x, y);
- } else {
- gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_BORDER));
- gc.drawRectangle(x, y, width, height);
- }
-
- gc.setClipping(x, y, width, height);
- Image icon = IconFactory.getInstance().getIcon("renderError"); //$NON-NLS-1$
- ImageData data = icon.getImageData();
- int prevAlpha = gc.getAlpha();
- int alpha = 96;
- if (mThumbnail != null) {
- alpha -= 32;
- }
- gc.setAlpha(alpha);
- gc.drawImage(icon, x + (width - data.width) / 2, y + (height - data.height) / 2);
-
- String msg = mError;
- Density density = mConfiguration.getDensity();
- if (density == Density.TV || density == Density.LOW) {
- msg = "Broken rendering library; unsupported DPI. Try using the SDK manager " +
- "to get updated layout libraries.";
- }
- int charWidth = gc.getFontMetrics().getAverageCharWidth();
- int charsPerLine = (width - 10) / charWidth;
- msg = SdkUtils.wrap(msg, charsPerLine, null);
- gc.setAlpha(255);
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_BLACK));
- gc.drawText(msg, x + 5, y + HEADER_HEIGHT, true);
- gc.setAlpha(prevAlpha);
- gc.setClipping((Region) null);
- } else {
- gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_BORDER));
- gc.drawRectangle(x, y, width, height);
-
- Image icon = IconFactory.getInstance().getIcon("refreshPreview"); //$NON-NLS-1$
- ImageData data = icon.getImageData();
- int prevAlpha = gc.getAlpha();
- gc.setAlpha(96);
- gc.drawImage(icon, x + (width - data.width) / 2,
- y + (height - data.height) / 2);
- gc.setAlpha(prevAlpha);
- }
-
- if (mActive) {
- int left = x ;
- int prevAlpha = gc.getAlpha();
- gc.setAlpha(208);
- Color bg = mCanvas.getDisplay().getSystemColor(SWT.COLOR_WHITE);
- gc.setBackground(bg);
- gc.fillRectangle(left, y, x + width - left, HEADER_HEIGHT);
- gc.setAlpha(prevAlpha);
-
- y += 2;
-
- // Paint icons
- gc.drawImage(CLOSE_ICON, left, y);
- left += CLOSE_ICON_WIDTH;
-
- gc.drawImage(ZOOM_IN_ICON, left, y);
- left += ZOOM_IN_ICON_WIDTH;
-
- gc.drawImage(ZOOM_OUT_ICON, left, y);
- left += ZOOM_OUT_ICON_WIDTH;
-
- gc.drawImage(EDIT_ICON, left, y);
- left += EDIT_ICON_WIDTH;
- }
- }
-
- /**
- * Paints the preview title at the given position (and returns the required
- * height)
- *
- * @param gc the graphics context to paint into
- * @param x the left edge of the preview rectangle
- * @param y the top edge of the preview rectangle
- */
- private int paintTitle(GC gc, int x, int y, boolean showFile) {
- String displayName = getDisplayName();
- return paintTitle(gc, x, y, showFile, displayName);
- }
-
- /**
- * Paints the preview title at the given position (and returns the required
- * height)
- *
- * @param gc the graphics context to paint into
- * @param x the left edge of the preview rectangle
- * @param y the top edge of the preview rectangle
- * @param displayName the title string to be used
- */
- int paintTitle(GC gc, int x, int y, boolean showFile, String displayName) {
- int titleHeight = 0;
-
- if (showFile && mIncludedWithin != null) {
- if (mManager.getMode() != INCLUDES) {
- displayName = "<include>";
- } else {
- // Skip: just paint footer instead
- displayName = null;
- }
- }
-
- int width = getWidth();
- int labelTop = y + 1;
- gc.setClipping(x, labelTop, width, 100);
-
- // Use font height rather than extent height since we want two adjacent
- // previews (which may have different display names and therefore end
- // up with slightly different extent heights) to have identical title
- // heights such that they are aligned identically
- int fontHeight = gc.getFontMetrics().getHeight();
-
- if (displayName != null && displayName.length() > 0) {
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
- Point extent = gc.textExtent(displayName);
- int labelLeft = Math.max(x, x + (width - extent.x) / 2);
- Image icon = null;
- Locale locale = mConfiguration.getLocale();
- if (locale != null && (locale.hasLanguage() || locale.hasRegion())
- && (!(mConfiguration instanceof NestedConfiguration)
- || ((NestedConfiguration) mConfiguration).isOverridingLocale())) {
- icon = locale.getFlagImage();
- }
-
- if (icon != null) {
- int flagWidth = icon.getImageData().width;
- int flagHeight = icon.getImageData().height;
- labelLeft = Math.max(x + flagWidth / 2, labelLeft);
- gc.drawImage(icon, labelLeft - flagWidth / 2 - 1, labelTop);
- labelLeft += flagWidth / 2 + 1;
- gc.drawText(displayName, labelLeft,
- labelTop - (extent.y - flagHeight) / 2, true);
- } else {
- gc.drawText(displayName, labelLeft, labelTop, true);
- }
-
- labelTop += extent.y;
- titleHeight += fontHeight;
- }
-
- if (showFile && (mAlternateInput != null || mIncludedWithin != null)) {
- // Draw file flag, and parent folder name
- IFile file = mAlternateInput != null
- ? mAlternateInput : mIncludedWithin.getFile();
- String fileName = file.getParent().getName() + File.separator
- + file.getName();
- Point extent = gc.textExtent(fileName);
- Image icon = IconFactory.getInstance().getIcon("android_file"); //$NON-NLS-1$
- int flagWidth = icon.getImageData().width;
- int flagHeight = icon.getImageData().height;
-
- int labelLeft = Math.max(x, x + (width - extent.x - flagWidth - 1) / 2);
-
- gc.drawImage(icon, labelLeft, labelTop);
-
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY));
- labelLeft += flagWidth + 1;
- labelTop -= (extent.y - flagHeight) / 2;
- gc.drawText(fileName, labelLeft, labelTop, true);
-
- titleHeight += Math.max(titleHeight, icon.getImageData().height);
- }
-
- gc.setClipping((Region) null);
-
- return titleHeight;
- }
-
- /**
- * Notifies that the preview's configuration has changed.
- *
- * @param flags the change flags, a bitmask corresponding to the
- * {@code CHANGE_} constants in {@link ConfigurationClient}
- */
- public void configurationChanged(int flags) {
- if (!mVisible) {
- mDirty |= flags;
- return;
- }
-
- if ((flags & MASK_RENDERING) != 0) {
- mResourceResolver.clear();
- // Handle inheritance
- mConfiguration.syncFolderConfig();
- updateForkStatus();
- updateSize();
- }
-
- // Sanity check to make sure things are working correctly
- if (DEBUG) {
- RenderPreviewMode mode = mManager.getMode();
- if (mode == DEFAULT) {
- assert mConfiguration instanceof VaryingConfiguration;
- VaryingConfiguration config = (VaryingConfiguration) mConfiguration;
- int alternateFlags = config.getAlternateFlags();
- switch (alternateFlags) {
- case Configuration.CFG_DEVICE_STATE: {
- State configState = config.getDeviceState();
- State chooserState = mManager.getChooser().getConfiguration()
- .getDeviceState();
- assert configState != null && chooserState != null;
- assert !configState.getName().equals(chooserState.getName())
- : configState.toString() + ':' + chooserState;
-
- Device configDevice = config.getDevice();
- Device chooserDevice = mManager.getChooser().getConfiguration()
- .getDevice();
- assert configDevice != null && chooserDevice != null;
- assert configDevice == chooserDevice
- : configDevice.toString() + ':' + chooserDevice;
-
- break;
- }
- case Configuration.CFG_DEVICE: {
- Device configDevice = config.getDevice();
- Device chooserDevice = mManager.getChooser().getConfiguration()
- .getDevice();
- assert configDevice != null && chooserDevice != null;
- assert configDevice != chooserDevice
- : configDevice.toString() + ':' + chooserDevice;
-
- State configState = config.getDeviceState();
- State chooserState = mManager.getChooser().getConfiguration()
- .getDeviceState();
- assert configState != null && chooserState != null;
- assert configState.getName().equals(chooserState.getName())
- : configState.toString() + ':' + chooserState;
-
- break;
- }
- case Configuration.CFG_LOCALE: {
- Locale configLocale = config.getLocale();
- Locale chooserLocale = mManager.getChooser().getConfiguration()
- .getLocale();
- assert configLocale != null && chooserLocale != null;
- assert configLocale != chooserLocale
- : configLocale.toString() + ':' + chooserLocale;
- break;
- }
- default: {
- // Some other type of override I didn't anticipate
- assert false : alternateFlags;
- }
- }
- }
- }
-
- mDirty = 0;
- mManager.scheduleRender(this);
- }
-
- private void updateSize() {
- Device device = mConfiguration.getDevice();
- if (device == null) {
- return;
- }
- Screen screen = device.getDefaultHardware().getScreen();
- if (screen == null) {
- return;
- }
-
- FolderConfiguration folderConfig = mConfiguration.getFullConfig();
- ScreenOrientationQualifier qualifier = folderConfig.getScreenOrientationQualifier();
- ScreenOrientation orientation = qualifier == null
- ? ScreenOrientation.PORTRAIT : qualifier.getValue();
-
- // compute width and height to take orientation into account.
- int x = screen.getXDimension();
- int y = screen.getYDimension();
- int screenWidth, screenHeight;
-
- if (x > y) {
- if (orientation == ScreenOrientation.LANDSCAPE) {
- screenWidth = x;
- screenHeight = y;
- } else {
- screenWidth = y;
- screenHeight = x;
- }
- } else {
- if (orientation == ScreenOrientation.LANDSCAPE) {
- screenWidth = y;
- screenHeight = x;
- } else {
- screenWidth = x;
- screenHeight = y;
- }
- }
-
- int width = RenderPreviewManager.getMaxWidth();
- int height = RenderPreviewManager.getMaxHeight();
- if (screenWidth > 0) {
- double scale = getScale(screenWidth, screenHeight);
- width = (int) (screenWidth * scale);
- height = (int) (screenHeight * scale);
- }
-
- if (width != mWidth || height != mHeight) {
- mWidth = width;
- mHeight = height;
-
- Image thumbnail = mThumbnail;
- mThumbnail = null;
- if (thumbnail != null) {
- thumbnail.dispose();
- }
- if (mHeight != 0) {
- mAspectRatio = mWidth / (double) mHeight;
- }
- }
- }
-
- /**
- * Returns the configuration associated with this preview
- *
- * @return the configuration
- */
- @NonNull
- public Configuration getConfiguration() {
- return mConfiguration;
- }
-
- // ---- Implements IJobChangeListener ----
-
- @Override
- public void aboutToRun(IJobChangeEvent event) {
- }
-
- @Override
- public void awake(IJobChangeEvent event) {
- }
-
- @Override
- public void done(IJobChangeEvent event) {
- mJob = null;
- }
-
- @Override
- public void running(IJobChangeEvent event) {
- }
-
- @Override
- public void scheduled(IJobChangeEvent event) {
- }
-
- @Override
- public void sleeping(IJobChangeEvent event) {
- }
-
- // ---- Delayed Rendering ----
-
- private final class RenderJob extends UIJob {
- public RenderJob() {
- super("RenderPreview");
- setSystem(true);
- setUser(false);
- }
-
- @Override
- public IStatus runInUIThread(IProgressMonitor monitor) {
- mJob = null;
- if (!mCanvas.isDisposed()) {
- renderSync();
- mCanvas.redraw();
- return org.eclipse.core.runtime.Status.OK_STATUS;
- }
-
- return org.eclipse.core.runtime.Status.CANCEL_STATUS;
- }
-
- @Override
- public Display getDisplay() {
- if (mCanvas.isDisposed()) {
- return null;
- }
- return mCanvas.getDisplay();
- }
- }
-
- private final class AsyncRenderJob extends Job {
- public AsyncRenderJob() {
- super("RenderPreview");
- setSystem(true);
- setUser(false);
- }
-
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- mJob = null;
-
- if (mCanvas.isDisposed()) {
- return org.eclipse.core.runtime.Status.CANCEL_STATUS;
- }
-
- renderSync();
-
- // Update display
- mCanvas.getDisplay().asyncExec(new Runnable() {
- @Override
- public void run() {
- mCanvas.redraw();
- }
- });
-
- return org.eclipse.core.runtime.Status.OK_STATUS;
- }
- }
-
- /**
- * Sets the input file to use for rendering. If not set, this will just be
- * the same file as the configuration chooser. This is used to render other
- * layouts, such as variations of the currently edited layout, which are
- * not kept in sync with the main layout.
- *
- * @param file the file to set as input
- */
- public void setAlternateInput(@Nullable IFile file) {
- mAlternateInput = file;
- }
-
- /** Corresponding description for this preview if it is a manually added preview */
- private @Nullable ConfigurationDescription mDescription;
-
- /**
- * Sets the description of this preview, if this preview is a manually added preview
- *
- * @param description the description of this preview
- */
- public void setDescription(@Nullable ConfigurationDescription description) {
- mDescription = description;
- }
-
- /**
- * Returns the description of this preview, if this preview is a manually added preview
- *
- * @return the description
- */
- @Nullable
- public ConfigurationDescription getDescription() {
- return mDescription;
- }
-
- @Override
- public String toString() {
- return getDisplayName() + ':' + mConfiguration;
- }
-
- /** Sorts render previews into increasing aspect ratio order */
- static Comparator<RenderPreview> INCREASING_ASPECT_RATIO = new Comparator<RenderPreview>() {
- @Override
- public int compare(RenderPreview preview1, RenderPreview preview2) {
- return (int) Math.signum(preview1.mAspectRatio - preview2.mAspectRatio);
- }
- };
- /** Sorts render previews into visual order: row by row, column by column */
- static Comparator<RenderPreview> VISUAL_ORDER = new Comparator<RenderPreview>() {
- @Override
- public int compare(RenderPreview preview1, RenderPreview preview2) {
- int delta = preview1.mY - preview2.mY;
- if (delta == 0) {
- delta = preview1.mX - preview2.mX;
- }
- return delta;
- }
- };
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewList.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewList.java
deleted file mode 100644
index 2bcdba382..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewList.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2012 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 com.android.annotations.NonNull;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription;
-import com.android.sdklib.devices.Device;
-import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
-import com.google.common.io.Files;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.QualifiedName;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/** A list of render previews */
-class RenderPreviewList {
- /** Name of file saved in project directory storing previews */
- private static final String PREVIEW_FILE_NAME = "previews.xml"; //$NON-NLS-1$
-
- /** Qualified name for the per-project persistent property include-map */
- private final static QualifiedName PREVIEW_LIST = new QualifiedName(AdtPlugin.PLUGIN_ID,
- "previewlist");//$NON-NLS-1$
-
- private final IProject mProject;
- private final List<ConfigurationDescription> mList = Lists.newArrayList();
-
- private RenderPreviewList(@NonNull IProject project) {
- mProject = project;
- }
-
- /**
- * Returns the {@link RenderPreviewList} for the given project
- *
- * @param project the project the list is associated with
- * @return a {@link RenderPreviewList} for the given project, never null
- */
- @NonNull
- public static RenderPreviewList get(@NonNull IProject project) {
- RenderPreviewList list = null;
- try {
- list = (RenderPreviewList) project.getSessionProperty(PREVIEW_LIST);
- } catch (CoreException e) {
- // Not a problem; we will just create a new one
- }
-
- if (list == null) {
- list = new RenderPreviewList(project);
- try {
- project.setSessionProperty(PREVIEW_LIST, list);
- } catch (CoreException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- return list;
- }
-
- private File getManualFile() {
- return new File(AdtUtils.getAbsolutePath(mProject).toFile(), PREVIEW_FILE_NAME);
- }
-
- void load(Collection<Device> deviceList) throws IOException {
- File file = getManualFile();
- if (file.exists()) {
- load(file, deviceList);
- }
- }
-
- void save() throws IOException {
- deleteFile();
- if (!mList.isEmpty()) {
- File file = getManualFile();
- save(file);
- }
- }
-
- private void save(File file) throws IOException {
- //Document document = DomUtilities.createEmptyPlainDocument();
- Document document = DomUtilities.createEmptyDocument();
- if (document != null) {
- for (ConfigurationDescription description : mList) {
- description.toXml(document);
- }
- String xml = EclipseXmlPrettyPrinter.prettyPrint(document, true);
- Files.write(xml, file, Charsets.UTF_8);
- }
- }
-
- void load(File file, Collection<Device> deviceList) throws IOException {
- mList.clear();
-
- String xml = Files.toString(file, Charsets.UTF_8);
- Document document = DomUtilities.parseDocument(xml, true);
- if (document == null || document.getDocumentElement() == null) {
- return;
- }
- List<Element> elements = DomUtilities.getChildren(document.getDocumentElement());
- for (Element element : elements) {
- ConfigurationDescription description = ConfigurationDescription.fromXml(
- mProject, element, deviceList);
- if (description != null) {
- mList.add(description);
- }
- }
- }
-
- /**
- * Create a list of previews for the given canvas that matches the internal
- * configuration preview list
- *
- * @param canvas the associated canvas
- * @return a new list of previews linked to the given canvas
- */
- @NonNull
- List<RenderPreview> createPreviews(LayoutCanvas canvas) {
- if (mList.isEmpty()) {
- return new ArrayList<RenderPreview>();
- }
- List<RenderPreview> previews = Lists.newArrayList();
- RenderPreviewManager manager = canvas.getPreviewManager();
- ConfigurationChooser chooser = canvas.getEditorDelegate().getGraphicalEditor()
- .getConfigurationChooser();
-
- Configuration chooserConfig = chooser.getConfiguration();
- for (ConfigurationDescription description : mList) {
- Configuration configuration = Configuration.create(chooser);
- configuration.setDisplayName(description.displayName);
- configuration.setActivity(description.activity);
- configuration.setLocale(
- description.locale != null ? description.locale : chooserConfig.getLocale(),
- true);
- // TODO: Make sure this layout isn't in some v-folder which is incompatible
- // with this target!
- configuration.setTarget(
- description.target != null ? description.target : chooserConfig.getTarget(),
- true);
- configuration.setTheme(
- description.theme != null ? description.theme : chooserConfig.getTheme());
- configuration.setDevice(
- description.device != null ? description.device : chooserConfig.getDevice(),
- true);
- configuration.setDeviceState(
- description.state != null ? description.state : chooserConfig.getDeviceState(),
- true);
- configuration.setNightMode(
- description.nightMode != null ? description.nightMode
- : chooserConfig.getNightMode(), true);
- configuration.setUiMode(
- description.uiMode != null ? description.uiMode : chooserConfig.getUiMode(), true);
-
- //configuration.syncFolderConfig();
- configuration.getFullConfig().set(description.folder);
-
- RenderPreview preview = RenderPreview.create(manager, configuration);
-
- preview.setDescription(description);
- previews.add(preview);
- }
-
- return previews;
- }
-
- void remove(@NonNull RenderPreview preview) {
- ConfigurationDescription description = preview.getDescription();
- if (description != null) {
- mList.remove(description);
- }
- }
-
- boolean isEmpty() {
- return mList.isEmpty();
- }
-
- void add(@NonNull RenderPreview preview) {
- Configuration configuration = preview.getConfiguration();
- ConfigurationDescription description =
- ConfigurationDescription.fromConfiguration(mProject, configuration);
- // RenderPreviews can have display names that aren't reflected in the configuration
- description.displayName = preview.getDisplayName();
- mList.add(description);
- preview.setDescription(description);
- }
-
- void delete() {
- mList.clear();
- deleteFile();
- }
-
- private void deleteFile() {
- File file = getManualFile();
- if (file.exists()) {
- file.delete();
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java
deleted file mode 100644
index 98dde86e0..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java
+++ /dev/null
@@ -1,1696 +0,0 @@
-/*
- * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE_STATE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.MASK_ALL;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SMALL_SHADOW_SIZE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreview.LARGE_SHADOWS;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.CUSTOM;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.NONE;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.SCREENS;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.resources.configuration.DensityQualifier;
-import com.android.ide.common.resources.configuration.DeviceConfigHelper;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.common.resources.configuration.LocaleQualifier;
-import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
-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.common.CommonXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationClient;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.NestedConfiguration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.VaryingConfiguration;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.resources.Density;
-import com.android.resources.ScreenSize;
-import com.android.sdklib.devices.Device;
-import com.android.sdklib.devices.Screen;
-import com.android.sdklib.devices.State;
-import com.google.common.collect.Lists;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.ScrollBar;
-import org.eclipse.ui.IWorkbenchPartSite;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.ide.IDE;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Manager for the configuration previews, which handles layout computations,
- * managing the image buffer cache, etc
- */
-public class RenderPreviewManager {
- private static double sScale = 1.0;
- private static final int RENDER_DELAY = 150;
- private static final int PREVIEW_VGAP = 18;
- private static final int PREVIEW_HGAP = 12;
- private static final int MAX_WIDTH = 200;
- private static final int MAX_HEIGHT = MAX_WIDTH;
- private static final int ZOOM_ICON_WIDTH = 16;
- private static final int ZOOM_ICON_HEIGHT = 16;
- private @Nullable List<RenderPreview> mPreviews;
- private @Nullable RenderPreviewList mManualList;
- private final @NonNull LayoutCanvas mCanvas;
- private final @NonNull CanvasTransform mVScale;
- private final @NonNull CanvasTransform mHScale;
- private int mPrevCanvasWidth;
- private int mPrevCanvasHeight;
- private int mPrevImageWidth;
- private int mPrevImageHeight;
- private @NonNull RenderPreviewMode mMode = NONE;
- private @Nullable RenderPreview mActivePreview;
- private @Nullable ScrollBarListener mListener;
- private int mLayoutHeight;
- /** Last seen state revision in this {@link RenderPreviewManager}. If less
- * than {@link #sRevision}, the previews need to be updated on next exposure */
- private static int mRevision;
- /** Current global revision count */
- private static int sRevision;
- private boolean mNeedLayout;
- private boolean mNeedRender;
- private boolean mNeedZoom;
- private SwapAnimation mAnimation;
-
- /**
- * Creates a {@link RenderPreviewManager} associated with the given canvas
- *
- * @param canvas the canvas to manage previews for
- */
- public RenderPreviewManager(@NonNull LayoutCanvas canvas) {
- mCanvas = canvas;
- mHScale = canvas.getHorizontalTransform();
- mVScale = canvas.getVerticalTransform();
- }
-
- /**
- * Revise the global state revision counter. This will cause all layout
- * preview managers to refresh themselves to the latest revision when they
- * are next exposed.
- */
- public static void bumpRevision() {
- sRevision++;
- }
-
- /**
- * Returns the associated chooser
- *
- * @return the associated chooser
- */
- @NonNull
- ConfigurationChooser getChooser() {
- GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor();
- return editor.getConfigurationChooser();
- }
-
- /**
- * Returns the associated canvas
- *
- * @return the canvas
- */
- @NonNull
- public LayoutCanvas getCanvas() {
- return mCanvas;
- }
-
- /** Zooms in (grows all previews) */
- public void zoomIn() {
- sScale = sScale * (1 / 0.9);
- if (Math.abs(sScale-1.0) < 0.0001) {
- sScale = 1.0;
- }
-
- updatedZoom();
- }
-
- /** Zooms out (shrinks all previews) */
- public void zoomOut() {
- sScale = sScale * (0.9 / 1);
- if (Math.abs(sScale-1.0) < 0.0001) {
- sScale = 1.0;
- }
- updatedZoom();
- }
-
- /** Zooms to 100 (resets zoom) */
- public void zoomReset() {
- sScale = 1.0;
- updatedZoom();
- mNeedZoom = mNeedLayout = true;
- mCanvas.redraw();
- }
-
- private void updatedZoom() {
- if (hasPreviews()) {
- for (RenderPreview preview : mPreviews) {
- preview.disposeThumbnail();
- }
- RenderPreview preview = mCanvas.getPreview();
- if (preview != null) {
- preview.disposeThumbnail();
- }
- }
-
- mNeedLayout = mNeedRender = true;
- mCanvas.redraw();
- }
-
- static int getMaxWidth() {
- return (int) (sScale * MAX_WIDTH);
- }
-
- static int getMaxHeight() {
- return (int) (sScale * MAX_HEIGHT);
- }
-
- static double getScale() {
- return sScale;
- }
-
- /**
- * Returns whether there are any manual preview items (provided the current
- * mode is manual previews
- *
- * @return true if there are items in the manual preview list
- */
- public boolean hasManualPreviews() {
- assert mMode == CUSTOM;
- return mManualList != null && !mManualList.isEmpty();
- }
-
- /** Delete all the previews */
- public void deleteManualPreviews() {
- disposePreviews();
- selectMode(NONE);
- mCanvas.setFitScale(true /* onlyZoomOut */, true /*allowZoomIn*/);
-
- if (mManualList != null) {
- mManualList.delete();
- }
- }
-
- /** Dispose all the previews */
- public void disposePreviews() {
- if (mPreviews != null) {
- List<RenderPreview> old = mPreviews;
- mPreviews = null;
- for (RenderPreview preview : old) {
- preview.dispose();
- }
- }
- }
-
- /**
- * Deletes the given preview
- *
- * @param preview the preview to be deleted
- */
- public void deletePreview(RenderPreview preview) {
- mPreviews.remove(preview);
- preview.dispose();
- layout(true);
- mCanvas.redraw();
-
- if (mManualList != null) {
- mManualList.remove(preview);
- saveList();
- }
- }
-
- /**
- * Compute the total width required for the previews, including internal padding
- *
- * @return total width in pixels
- */
- public int computePreviewWidth() {
- int maxPreviewWidth = 0;
- if (hasPreviews()) {
- for (RenderPreview preview : mPreviews) {
- maxPreviewWidth = Math.max(maxPreviewWidth, preview.getWidth());
- }
-
- if (maxPreviewWidth > 0) {
- maxPreviewWidth += 2 * PREVIEW_HGAP; // 2x for left and right side
- maxPreviewWidth += LARGE_SHADOWS ? SHADOW_SIZE : SMALL_SHADOW_SIZE;
- }
-
- return maxPreviewWidth;
- }
-
- return 0;
- }
-
- /**
- * Layout Algorithm. This sets the {@link RenderPreview#getX()} and
- * {@link RenderPreview#getY()} coordinates of all the previews. It also
- * marks previews as visible or invisible via
- * {@link RenderPreview#setVisible(boolean)} according to their position and
- * the current visible view port in the layout canvas. Finally, it also sets
- * the {@code mLayoutHeight} field, such that the scrollbars can compute the
- * right scrolled area, and that scrolling can cause render refreshes on
- * views that are made visible.
- * <p>
- * This is not a traditional bin packing problem, because the objects to be
- * packaged do not have a fixed size; we can scale them up and down in order
- * to provide an "optimal" size.
- * <p>
- * See http://en.wikipedia.org/wiki/Packing_problem See
- * http://en.wikipedia.org/wiki/Bin_packing_problem
- */
- void layout(boolean refresh) {
- mNeedLayout = false;
-
- if (mPreviews == null || mPreviews.isEmpty()) {
- return;
- }
-
- int scaledImageWidth = mHScale.getScaledImgSize();
- int scaledImageHeight = mVScale.getScaledImgSize();
- Rectangle clientArea = mCanvas.getClientArea();
-
- if (!refresh &&
- (scaledImageWidth == mPrevImageWidth
- && scaledImageHeight == mPrevImageHeight
- && clientArea.width == mPrevCanvasWidth
- && clientArea.height == mPrevCanvasHeight)) {
- // No change
- return;
- }
-
- mPrevImageWidth = scaledImageWidth;
- mPrevImageHeight = scaledImageHeight;
- mPrevCanvasWidth = clientArea.width;
- mPrevCanvasHeight = clientArea.height;
-
- if (mListener == null) {
- mListener = new ScrollBarListener();
- mCanvas.getVerticalBar().addSelectionListener(mListener);
- }
-
- beginRenderScheduling();
-
- mLayoutHeight = 0;
-
- if (previewsHaveIdenticalSize() || fixedOrder()) {
- // If all the preview boxes are of identical sizes, or if the order is predetermined,
- // just lay them out in rows.
- rowLayout();
- } else if (previewsFit()) {
- layoutFullFit();
- } else {
- rowLayout();
- }
-
- mCanvas.updateScrollBars();
- }
-
- /**
- * Performs a simple layout where the views are laid out in a row, wrapping
- * around the top left canvas image.
- */
- private void rowLayout() {
- // TODO: Separate layout heuristics for portrait and landscape orientations (though
- // it also depends on the dimensions of the canvas window, which determines the
- // shape of the leftover space)
-
- int scaledImageWidth = mHScale.getScaledImgSize();
- int scaledImageHeight = mVScale.getScaledImgSize();
- Rectangle clientArea = mCanvas.getClientArea();
-
- int availableWidth = clientArea.x + clientArea.width - getX();
- int availableHeight = clientArea.y + clientArea.height - getY();
- int maxVisibleY = clientArea.y + clientArea.height;
-
- int bottomBorder = scaledImageHeight;
- int rightHandSide = scaledImageWidth + PREVIEW_HGAP;
- int nextY = 0;
-
- // First lay out images across the top right hand side
- int x = rightHandSide;
- int y = 0;
- boolean wrapped = false;
-
- int vgap = PREVIEW_VGAP;
- for (RenderPreview preview : mPreviews) {
- // If we have forked previews, double the vgap to allow space for two labels
- if (preview.isForked()) {
- vgap *= 2;
- break;
- }
- }
-
- List<RenderPreview> aspectOrder;
- if (!fixedOrder()) {
- aspectOrder = new ArrayList<RenderPreview>(mPreviews);
- Collections.sort(aspectOrder, RenderPreview.INCREASING_ASPECT_RATIO);
- } else {
- aspectOrder = mPreviews;
- }
-
- for (RenderPreview preview : aspectOrder) {
- if (x > 0 && x + preview.getWidth() > availableWidth) {
- x = rightHandSide;
- int prevY = y;
- y = nextY;
- if ((prevY <= bottomBorder ||
- y <= bottomBorder)
- && Math.max(nextY, y + preview.getHeight()) > bottomBorder) {
- // If there's really no visible room below, don't bother
- // Similarly, don't wrap individually scaled views
- if (bottomBorder < availableHeight - 40 && preview.getScale() < 1.2) {
- // If it's closer to the top row than the bottom, just
- // mark the next row for left justify instead
- if (bottomBorder - y > y + preview.getHeight() - bottomBorder) {
- rightHandSide = 0;
- wrapped = true;
- } else if (!wrapped) {
- y = nextY = Math.max(nextY, bottomBorder + vgap);
- x = rightHandSide = 0;
- wrapped = true;
- }
- }
- }
- }
- if (x > 0 && y <= bottomBorder
- && Math.max(nextY, y + preview.getHeight()) > bottomBorder) {
- if (clientArea.height - bottomBorder < preview.getHeight()) {
- // No room below the device on the left; just continue on the
- // bottom row
- } else if (preview.getScale() < 1.2) {
- if (bottomBorder - y > y + preview.getHeight() - bottomBorder) {
- rightHandSide = 0;
- wrapped = true;
- } else {
- y = nextY = Math.max(nextY, bottomBorder + vgap);
- x = rightHandSide = 0;
- wrapped = true;
- }
- }
- }
-
- preview.setPosition(x, y);
-
- if (y > maxVisibleY && maxVisibleY > 0) {
- preview.setVisible(false);
- } else if (!preview.isVisible()) {
- preview.setVisible(true);
- }
-
- x += preview.getWidth();
- x += PREVIEW_HGAP;
- nextY = Math.max(nextY, y + preview.getHeight() + vgap);
- }
-
- mLayoutHeight = nextY;
- }
-
- private boolean fixedOrder() {
- return mMode == SCREENS;
- }
-
- /** Returns true if all the previews have the same identical size */
- private boolean previewsHaveIdenticalSize() {
- if (!hasPreviews()) {
- return true;
- }
-
- Iterator<RenderPreview> iterator = mPreviews.iterator();
- RenderPreview first = iterator.next();
- int width = first.getWidth();
- int height = first.getHeight();
-
- while (iterator.hasNext()) {
- RenderPreview preview = iterator.next();
- if (width != preview.getWidth() || height != preview.getHeight()) {
- return false;
- }
- }
-
- return true;
- }
-
- /** Returns true if all the previews can fully fit in the available space */
- private boolean previewsFit() {
- int scaledImageWidth = mHScale.getScaledImgSize();
- int scaledImageHeight = mVScale.getScaledImgSize();
- Rectangle clientArea = mCanvas.getClientArea();
- int availableWidth = clientArea.x + clientArea.width - getX();
- int availableHeight = clientArea.y + clientArea.height - getY();
- int bottomBorder = scaledImageHeight;
- int rightHandSide = scaledImageWidth + PREVIEW_HGAP;
-
- // First see if we can fit everything; if so, we can try to make the layouts
- // larger such that they fill up all the available space
- long availableArea = rightHandSide * bottomBorder +
- availableWidth * (Math.max(0, availableHeight - bottomBorder));
-
- long requiredArea = 0;
- for (RenderPreview preview : mPreviews) {
- // Note: This does not include individual preview scale; the layout
- // algorithm itself may be tweaking the scales to fit elements within
- // the layout
- requiredArea += preview.getArea();
- }
-
- return requiredArea * sScale < availableArea;
- }
-
- private void layoutFullFit() {
- int scaledImageWidth = mHScale.getScaledImgSize();
- int scaledImageHeight = mVScale.getScaledImgSize();
- Rectangle clientArea = mCanvas.getClientArea();
- int availableWidth = clientArea.x + clientArea.width - getX();
- int availableHeight = clientArea.y + clientArea.height - getY();
- int maxVisibleY = clientArea.y + clientArea.height;
- int bottomBorder = scaledImageHeight;
- int rightHandSide = scaledImageWidth + PREVIEW_HGAP;
-
- int minWidth = Integer.MAX_VALUE;
- int minHeight = Integer.MAX_VALUE;
- for (RenderPreview preview : mPreviews) {
- minWidth = Math.min(minWidth, preview.getWidth());
- minHeight = Math.min(minHeight, preview.getHeight());
- }
-
- BinPacker packer = new BinPacker(minWidth, minHeight);
-
- // TODO: Instead of this, just start with client area and occupy scaled image size!
-
- // Add in gap on right and bottom since we'll add that requirement on the width and
- // height rectangles too (for spacing)
- packer.addSpace(new Rect(rightHandSide, 0,
- availableWidth - rightHandSide + PREVIEW_HGAP,
- availableHeight + PREVIEW_VGAP));
- if (maxVisibleY > bottomBorder) {
- packer.addSpace(new Rect(0, bottomBorder + PREVIEW_VGAP,
- availableWidth + PREVIEW_HGAP, maxVisibleY - bottomBorder + PREVIEW_VGAP));
- }
-
- // TODO: Sort previews first before attempting to position them?
-
- ArrayList<RenderPreview> aspectOrder = new ArrayList<RenderPreview>(mPreviews);
- Collections.sort(aspectOrder, RenderPreview.INCREASING_ASPECT_RATIO);
-
- for (RenderPreview preview : aspectOrder) {
- int previewWidth = preview.getWidth();
- int previewHeight = preview.getHeight();
- previewHeight += PREVIEW_VGAP;
- if (preview.isForked()) {
- previewHeight += PREVIEW_VGAP;
- }
- previewWidth += PREVIEW_HGAP;
- // title height? how do I account for that?
- Rect position = packer.occupy(previewWidth, previewHeight);
- if (position != null) {
- preview.setPosition(position.x, position.y);
- preview.setVisible(true);
- } else {
- // Can't fit: give up and do plain row layout
- rowLayout();
- return;
- }
- }
-
- mLayoutHeight = availableHeight;
- }
- /**
- * Paints the configuration previews
- *
- * @param gc the graphics context to paint into
- */
- void paint(GC gc) {
- if (hasPreviews()) {
- // Ensure up to date at all times; consider moving if it's too expensive
- layout(mNeedLayout);
- if (mNeedRender) {
- renderPreviews();
- }
- if (mNeedZoom) {
- boolean allowZoomIn = true /*mMode == NONE*/;
- mCanvas.setFitScale(false /*onlyZoomOut*/, allowZoomIn);
- mNeedZoom = false;
- }
- int rootX = getX();
- int rootY = getY();
-
- for (RenderPreview preview : mPreviews) {
- if (preview.isVisible()) {
- int x = rootX + preview.getX();
- int y = rootY + preview.getY();
- preview.paint(gc, x, y);
- }
- }
-
- RenderPreview preview = mCanvas.getPreview();
- if (preview != null) {
- String displayName = null;
- Configuration configuration = preview.getConfiguration();
- if (configuration instanceof VaryingConfiguration) {
- // Use override flags from stashed preview, but configuration
- // data from live (not varying) configured configuration
- VaryingConfiguration cfg = (VaryingConfiguration) configuration;
- int flags = cfg.getAlternateFlags() | cfg.getOverrideFlags();
- displayName = NestedConfiguration.computeDisplayName(flags,
- getChooser().getConfiguration());
- } else if (configuration instanceof NestedConfiguration) {
- int flags = ((NestedConfiguration) configuration).getOverrideFlags();
- displayName = NestedConfiguration.computeDisplayName(flags,
- getChooser().getConfiguration());
- } else {
- displayName = configuration.getDisplayName();
- }
- if (displayName != null) {
- CanvasTransform hi = mHScale;
- CanvasTransform vi = mVScale;
-
- int destX = hi.translate(0);
- int destY = vi.translate(0);
- int destWidth = hi.getScaledImgSize();
- int destHeight = vi.getScaledImgSize();
-
- int x = destX + destWidth / 2 - preview.getWidth() / 2;
- int y = destY + destHeight;
-
- preview.paintTitle(gc, x, y, false /*showFile*/, displayName);
- }
- }
-
- // Zoom overlay
- int x = getZoomX();
- if (x > 0) {
- int y = getZoomY();
- int oldAlpha = gc.getAlpha();
-
- // Paint background oval rectangle behind the zoom and close icons
- gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY));
- gc.setAlpha(128);
- int padding = 3;
- int arc = 5;
- gc.fillRoundRectangle(x - padding, y - padding,
- ZOOM_ICON_WIDTH + 2 * padding,
- 4 * ZOOM_ICON_HEIGHT + 2 * padding, arc, arc);
-
- gc.setAlpha(255);
- IconFactory iconFactory = IconFactory.getInstance();
- Image zoomOut = iconFactory.getIcon("zoomminus"); //$NON-NLS-1$);
- Image zoomIn = iconFactory.getIcon("zoomplus"); //$NON-NLS-1$);
- Image zoom100 = iconFactory.getIcon("zoom100"); //$NON-NLS-1$);
- Image close = iconFactory.getIcon("close"); //$NON-NLS-1$);
-
- gc.drawImage(zoomIn, x, y);
- y += ZOOM_ICON_HEIGHT;
- gc.drawImage(zoomOut, x, y);
- y += ZOOM_ICON_HEIGHT;
- gc.drawImage(zoom100, x, y);
- y += ZOOM_ICON_HEIGHT;
- gc.drawImage(close, x, y);
- y += ZOOM_ICON_HEIGHT;
- gc.setAlpha(oldAlpha);
- }
- } else if (mMode == CUSTOM) {
- int rootX = getX();
- rootX += mHScale.getScaledImgSize();
- rootX += 2 * PREVIEW_HGAP;
- int rootY = getY();
- rootY += 20;
- gc.setFont(mCanvas.getFont());
- gc.setForeground(mCanvas.getDisplay().getSystemColor(SWT.COLOR_BLACK));
- gc.drawText("Add previews with \"Add as Thumbnail\"\nin the configuration menu",
- rootX, rootY, true);
- }
-
- if (mAnimation != null) {
- mAnimation.tick(gc);
- }
- }
-
- private void addPreview(@NonNull RenderPreview preview) {
- if (mPreviews == null) {
- mPreviews = Lists.newArrayList();
- }
- mPreviews.add(preview);
- }
-
- /** Adds the current configuration as a new configuration preview */
- public void addAsThumbnail() {
- ConfigurationChooser chooser = getChooser();
- String name = chooser.getConfiguration().getDisplayName();
- if (name == null || name.isEmpty()) {
- name = getUniqueName();
- }
- InputDialog d = new InputDialog(
- AdtPlugin.getShell(),
- "Add as Thumbnail Preview", // title
- "Name of thumbnail:",
- name,
- null);
- if (d.open() == Window.OK) {
- selectMode(CUSTOM);
-
- String newName = d.getValue();
- // Create a new configuration from the current settings in the composite
- Configuration configuration = Configuration.copy(chooser.getConfiguration());
- configuration.setDisplayName(newName);
-
- RenderPreview preview = RenderPreview.create(this, configuration);
- addPreview(preview);
-
- layout(true);
- beginRenderScheduling();
- scheduleRender(preview);
- mCanvas.setFitScale(true /* onlyZoomOut */, false /*allowZoomIn*/);
-
- if (mManualList == null) {
- loadList();
- }
- if (mManualList != null) {
- mManualList.add(preview);
- saveList();
- }
- }
- }
-
- /**
- * Computes a unique new name for a configuration preview that represents
- * the current, default configuration
- *
- * @return a unique name
- */
- private String getUniqueName() {
- if (mPreviews == null || mPreviews.isEmpty()) {
- // NO, not for the first preview!
- return "Config1";
- }
-
- Set<String> names = new HashSet<String>(mPreviews.size());
- for (RenderPreview preview : mPreviews) {
- names.add(preview.getDisplayName());
- }
-
- int index = 2;
- while (true) {
- String name = String.format("Config%1$d", index);
- if (!names.contains(name)) {
- return name;
- }
- index++;
- }
- }
-
- /** Generates a bunch of default configuration preview thumbnails */
- public void addDefaultPreviews() {
- ConfigurationChooser chooser = getChooser();
- Configuration parent = chooser.getConfiguration();
- if (parent instanceof NestedConfiguration) {
- parent = ((NestedConfiguration) parent).getParent();
- }
- if (mCanvas.getImageOverlay().getImage() != null) {
- // Create Language variation
- createLocaleVariation(chooser, parent);
-
- // Vary screen size
- // TODO: Be smarter here: Pick a screen that is both as differently as possible
- // from the current screen as well as also supported. So consider
- // things like supported screens, targetSdk etc.
- createScreenVariations(parent);
-
- // Vary orientation
- createStateVariation(chooser, parent);
-
- // Vary render target
- createRenderTargetVariation(chooser, parent);
- }
-
- // Also add in include-context previews, if any
- addIncludedInPreviews();
-
- // Make a placeholder preview for the current screen, in case we switch from it
- RenderPreview preview = RenderPreview.create(this, parent);
- mCanvas.setPreview(preview);
-
- sortPreviewsByOrientation();
- }
-
- private void createRenderTargetVariation(ConfigurationChooser chooser, Configuration parent) {
- /* This is disabled for now: need to load multiple versions of layoutlib.
- When I did this, there seemed to be some drug interactions between
- them, and I would end up with NPEs in layoutlib code which normally works.
- VaryingConfiguration configuration =
- VaryingConfiguration.create(chooser, parent);
- configuration.setAlternatingTarget(true);
- configuration.syncFolderConfig();
- addPreview(RenderPreview.create(this, configuration));
- */
- }
-
- private void createStateVariation(ConfigurationChooser chooser, Configuration parent) {
- State currentState = parent.getDeviceState();
- State nextState = parent.getNextDeviceState(currentState);
- if (nextState != currentState) {
- VaryingConfiguration configuration =
- VaryingConfiguration.create(chooser, parent);
- configuration.setAlternateDeviceState(true);
- configuration.syncFolderConfig();
- addPreview(RenderPreview.create(this, configuration));
- }
- }
-
- private void createLocaleVariation(ConfigurationChooser chooser, Configuration parent) {
- LocaleQualifier currentLanguage = parent.getLocale().qualifier;
- for (Locale locale : chooser.getLocaleList()) {
- LocaleQualifier qualifier = locale.qualifier;
- if (!qualifier.getLanguage().equals(currentLanguage.getLanguage())) {
- VaryingConfiguration configuration =
- VaryingConfiguration.create(chooser, parent);
- configuration.setAlternateLocale(true);
- configuration.syncFolderConfig();
- addPreview(RenderPreview.create(this, configuration));
- break;
- }
- }
- }
-
- private void createScreenVariations(Configuration parent) {
- ConfigurationChooser chooser = getChooser();
- VaryingConfiguration configuration;
-
- configuration = VaryingConfiguration.create(chooser, parent);
- configuration.setVariation(0);
- configuration.setAlternateDevice(true);
- configuration.syncFolderConfig();
- addPreview(RenderPreview.create(this, configuration));
-
- configuration = VaryingConfiguration.create(chooser, parent);
- configuration.setVariation(1);
- configuration.setAlternateDevice(true);
- configuration.syncFolderConfig();
- addPreview(RenderPreview.create(this, configuration));
- }
-
- /**
- * Returns the current mode as seen by this {@link RenderPreviewManager}.
- * Note that it may not yet have been synced with the global mode kept in
- * {@link AdtPrefs#getRenderPreviewMode()}.
- *
- * @return the current preview mode
- */
- @NonNull
- public RenderPreviewMode getMode() {
- return mMode;
- }
-
- /**
- * Update the set of previews for the current mode
- *
- * @param force force a refresh even if the preview type has not changed
- * @return true if the views were recomputed, false if the previews were
- * already showing and the mode not changed
- */
- public boolean recomputePreviews(boolean force) {
- RenderPreviewMode newMode = AdtPrefs.getPrefs().getRenderPreviewMode();
- if (newMode == mMode && !force
- && (mRevision == sRevision
- || mMode == NONE
- || mMode == CUSTOM)) {
- return false;
- }
-
- RenderPreviewMode oldMode = mMode;
- mMode = newMode;
- mRevision = sRevision;
-
- sScale = 1.0;
- disposePreviews();
-
- switch (mMode) {
- case DEFAULT:
- addDefaultPreviews();
- break;
- case INCLUDES:
- addIncludedInPreviews();
- break;
- case LOCALES:
- addLocalePreviews();
- break;
- case SCREENS:
- addScreenSizePreviews();
- break;
- case VARIATIONS:
- addVariationPreviews();
- break;
- case CUSTOM:
- addManualPreviews();
- break;
- case NONE:
- // Can't just set mNeedZoom because with no previews, the paint
- // method does nothing
- mCanvas.setFitScale(false /*onlyZoomOut*/, true /*allowZoomIn*/);
- break;
- default:
- assert false : mMode;
- }
-
- // We schedule layout for the next redraw rather than process it here immediately;
- // not only does this let us avoid doing work for windows where the tab is in the
- // background, but when a file is opened we may not know the size of the canvas
- // yet, and the layout methods need it in order to do a good job. By the time
- // the canvas is painted, we have accurate bounds.
- mNeedLayout = mNeedRender = true;
- mCanvas.redraw();
-
- if (oldMode != mMode && (oldMode == NONE || mMode == NONE)) {
- // If entering or exiting preview mode: updating padding which is compressed
- // only in preview mode.
- mCanvas.getHorizontalTransform().refresh();
- mCanvas.getVerticalTransform().refresh();
- }
-
- return true;
- }
-
- /**
- * Sets the new render preview mode to use
- *
- * @param mode the new mode
- */
- public void selectMode(@NonNull RenderPreviewMode mode) {
- if (mode != mMode) {
- AdtPrefs.getPrefs().setPreviewMode(mode);
- recomputePreviews(false);
- }
- }
-
- /** Similar to {@link #addDefaultPreviews()} but for locales */
- public void addLocalePreviews() {
-
- ConfigurationChooser chooser = getChooser();
- List<Locale> locales = chooser.getLocaleList();
- Configuration parent = chooser.getConfiguration();
-
- for (Locale locale : locales) {
- if (!locale.hasLanguage() && !locale.hasRegion()) {
- continue;
- }
- NestedConfiguration configuration = NestedConfiguration.create(chooser, parent);
- configuration.setOverrideLocale(true);
- configuration.setLocale(locale, false);
-
- String displayName = ConfigurationChooser.getLocaleLabel(chooser, locale, false);
- assert displayName != null; // it's never non null when locale is non null
- configuration.setDisplayName(displayName);
-
- addPreview(RenderPreview.create(this, configuration));
- }
-
- // Make a placeholder preview for the current screen, in case we switch from it
- Configuration configuration = parent;
- Locale locale = configuration.getLocale();
- String label = ConfigurationChooser.getLocaleLabel(chooser, locale, false);
- if (label == null) {
- label = "default";
- }
- configuration.setDisplayName(label);
- RenderPreview preview = RenderPreview.create(this, parent);
- if (preview != null) {
- mCanvas.setPreview(preview);
- }
-
- // No need to sort: they should all be identical
- }
-
- /** Similar to {@link #addDefaultPreviews()} but for screen sizes */
- public void addScreenSizePreviews() {
- ConfigurationChooser chooser = getChooser();
- Collection<Device> devices = chooser.getDevices();
- Configuration configuration = chooser.getConfiguration();
- boolean canScaleNinePatch = configuration.supports(Capability.FIXED_SCALABLE_NINE_PATCH);
-
- // Rearrange the devices a bit such that the most interesting devices bubble
- // to the front
- // 10" tablet, 7" tablet, reference phones, tiny phone, and in general the first
- // version of each seen screen size
- List<Device> sorted = new ArrayList<Device>(devices);
- Set<ScreenSize> seenSizes = new HashSet<ScreenSize>();
- State currentState = configuration.getDeviceState();
- String currentStateName = currentState != null ? currentState.getName() : "";
-
- for (int i = 0, n = sorted.size(); i < n; i++) {
- Device device = sorted.get(i);
- boolean interesting = false;
-
- State state = device.getState(currentStateName);
- if (state == null) {
- state = device.getAllStates().get(0);
- }
-
- if (device.getName().startsWith("Nexus ") //$NON-NLS-1$
- || device.getName().endsWith(" Nexus")) { //$NON-NLS-1$
- // Not String#contains("Nexus") because that would also pick up all the generic
- // entries ("3.7in WVGA (Nexus One)") so we'd have them duplicated
- interesting = true;
- }
-
- FolderConfiguration c = DeviceConfigHelper.getFolderConfig(state);
- if (c != null) {
- ScreenSizeQualifier sizeQualifier = c.getScreenSizeQualifier();
- if (sizeQualifier != null) {
- ScreenSize size = sizeQualifier.getValue();
- if (!seenSizes.contains(size)) {
- seenSizes.add(size);
- interesting = true;
- }
- }
-
- // Omit LDPI, not really used anymore
- DensityQualifier density = c.getDensityQualifier();
- if (density != null) {
- Density d = density.getValue();
- if (d == Density.LOW) {
- interesting = false;
- }
-
- if (!canScaleNinePatch && d == Density.TV) {
- interesting = false;
- }
- }
- }
-
- if (interesting) {
- NestedConfiguration screenConfig = NestedConfiguration.create(chooser,
- configuration);
- screenConfig.setOverrideDevice(true);
- screenConfig.setDevice(device, true);
- screenConfig.syncFolderConfig();
- screenConfig.setDisplayName(ConfigurationChooser.getDeviceLabel(device, true));
- addPreview(RenderPreview.create(this, screenConfig));
- }
- }
-
- // Sorted by screen size, in decreasing order
- sortPreviewsByScreenSize();
- }
-
- /**
- * Previews this layout as included in other layouts
- */
- public void addIncludedInPreviews() {
- ConfigurationChooser chooser = getChooser();
- IProject project = chooser.getProject();
- if (project == null) {
- return;
- }
- IncludeFinder finder = IncludeFinder.get(project);
-
- final List<Reference> includedBy = finder.getIncludedBy(chooser.getEditedFile());
-
- if (includedBy == null || includedBy.isEmpty()) {
- // TODO: Generate some useful defaults, such as including it in a ListView
- // as the list item layout?
- return;
- }
-
- for (final Reference reference : includedBy) {
- String title = reference.getDisplayName();
- Configuration config = Configuration.create(chooser.getConfiguration(),
- reference.getFile());
- RenderPreview preview = RenderPreview.create(this, config);
- preview.setDisplayName(title);
- preview.setIncludedWithin(reference);
-
- addPreview(preview);
- }
-
- sortPreviewsByOrientation();
- }
-
- /**
- * Previews this layout as included in other layouts
- */
- public void addVariationPreviews() {
- ConfigurationChooser chooser = getChooser();
-
- IFile file = chooser.getEditedFile();
- List<IFile> variations = AdtUtils.getResourceVariations(file, false /*includeSelf*/);
-
- // Sort by parent folder
- Collections.sort(variations, new Comparator<IFile>() {
- @Override
- public int compare(IFile file1, IFile file2) {
- return file1.getParent().getName().compareTo(file2.getParent().getName());
- }
- });
-
- Configuration currentConfig = chooser.getConfiguration();
-
- for (IFile variation : variations) {
- String title = variation.getParent().getName();
- Configuration config = Configuration.create(chooser.getConfiguration(), variation);
- config.setTheme(currentConfig.getTheme());
- config.setActivity(currentConfig.getActivity());
- RenderPreview preview = RenderPreview.create(this, config);
- preview.setDisplayName(title);
- preview.setAlternateInput(variation);
-
- addPreview(preview);
- }
-
- sortPreviewsByOrientation();
- }
-
- /**
- * Previews this layout using a custom configured set of layouts
- */
- public void addManualPreviews() {
- if (mManualList == null) {
- loadList();
- } else {
- mPreviews = mManualList.createPreviews(mCanvas);
- }
- }
-
- private void loadList() {
- IProject project = getChooser().getProject();
- if (project == null) {
- return;
- }
-
- if (mManualList == null) {
- mManualList = RenderPreviewList.get(project);
- }
-
- try {
- mManualList.load(getChooser().getDevices());
- mPreviews = mManualList.createPreviews(mCanvas);
- } catch (IOException e) {
- AdtPlugin.log(e, null);
- }
- }
-
- private void saveList() {
- if (mManualList != null) {
- try {
- mManualList.save();
- } catch (IOException e) {
- AdtPlugin.log(e, null);
- }
- }
- }
-
- void rename(ConfigurationDescription description, String newName) {
- IProject project = getChooser().getProject();
- if (project == null) {
- return;
- }
-
- if (mManualList == null) {
- mManualList = RenderPreviewList.get(project);
- }
- description.displayName = newName;
- saveList();
- }
-
-
- /**
- * Notifies that the main configuration has changed.
- *
- * @param flags the change flags, a bitmask corresponding to the
- * {@code CHANGE_} constants in {@link ConfigurationClient}
- */
- public void configurationChanged(int flags) {
- // Similar to renderPreviews, but only acts on incomplete previews
- if (hasPreviews()) {
- // Do zoomed images first
- beginRenderScheduling();
- for (RenderPreview preview : mPreviews) {
- if (preview.getScale() > 1.2) {
- preview.configurationChanged(flags);
- }
- }
- for (RenderPreview preview : mPreviews) {
- if (preview.getScale() <= 1.2) {
- preview.configurationChanged(flags);
- }
- }
- RenderPreview preview = mCanvas.getPreview();
- if (preview != null) {
- preview.configurationChanged(flags);
- preview.dispose();
- }
- mNeedLayout = true;
- mCanvas.redraw();
- }
- }
-
- /** Updates the configuration preview thumbnails */
- public void renderPreviews() {
- if (hasPreviews()) {
- beginRenderScheduling();
-
- // Process in visual order
- ArrayList<RenderPreview> visualOrder = new ArrayList<RenderPreview>(mPreviews);
- Collections.sort(visualOrder, RenderPreview.VISUAL_ORDER);
-
- // Do zoomed images first
- for (RenderPreview preview : visualOrder) {
- if (preview.getScale() > 1.2 && preview.isVisible()) {
- scheduleRender(preview);
- }
- }
- // Non-zoomed images
- for (RenderPreview preview : visualOrder) {
- if (preview.getScale() <= 1.2 && preview.isVisible()) {
- scheduleRender(preview);
- }
- }
- }
-
- mNeedRender = false;
- }
-
- private int mPendingRenderCount;
-
- /**
- * Reset rendering scheduling. The next render request will be scheduled
- * after a single delay unit.
- */
- public void beginRenderScheduling() {
- mPendingRenderCount = 0;
- }
-
- /**
- * Schedule rendering the given preview. Each successive call will add an additional
- * delay unit to the schedule from the previous {@link #scheduleRender(RenderPreview)}
- * call, until {@link #beginRenderScheduling()} is called again.
- *
- * @param preview the preview to render
- */
- public void scheduleRender(@NonNull RenderPreview preview) {
- mPendingRenderCount++;
- preview.render(mPendingRenderCount * RENDER_DELAY);
- }
-
- /**
- * Switch to the given configuration preview
- *
- * @param preview the preview to switch to
- */
- public void switchTo(@NonNull RenderPreview preview) {
- IFile input = preview.getAlternateInput();
- if (input != null) {
- IWorkbenchPartSite site = mCanvas.getEditorDelegate().getEditor().getSite();
- try {
- // This switches to the given file, but the file might not have
- // an identical configuration to what was shown in the preview.
- // For example, while viewing a 10" layout-xlarge file, it might
- // show a preview for a 5" version tied to the default layout. If
- // you click on it, it will open the default layout file, but it might
- // be using a different screen size; any of those that match the
- // default layout, say a 3.8".
- //
- // Thus, we need to also perform a screen size sync first
- Configuration configuration = preview.getConfiguration();
- boolean setSize = false;
- if (configuration instanceof NestedConfiguration) {
- NestedConfiguration nestedConfig = (NestedConfiguration) configuration;
- setSize = nestedConfig.isOverridingDevice();
- if (configuration instanceof VaryingConfiguration) {
- VaryingConfiguration c = (VaryingConfiguration) configuration;
- setSize |= c.isAlternatingDevice();
- }
-
- if (setSize) {
- ConfigurationChooser chooser = getChooser();
- IFile editedFile = chooser.getEditedFile();
- if (editedFile != null) {
- chooser.syncToVariations(CFG_DEVICE|CFG_DEVICE_STATE,
- editedFile, configuration, false, false);
- }
- }
- }
-
- IDE.openEditor(site.getWorkbenchWindow().getActivePage(), input,
- CommonXmlEditor.ID);
- } catch (PartInitException e) {
- AdtPlugin.log(e, null);
- }
- return;
- }
-
- GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor();
- ConfigurationChooser chooser = editor.getConfigurationChooser();
-
- Configuration originalConfiguration = chooser.getConfiguration();
-
- // The new configuration is the configuration which will become the configuration
- // in the layout editor's chooser
- Configuration previewConfiguration = preview.getConfiguration();
- Configuration newConfiguration = previewConfiguration;
- if (newConfiguration instanceof NestedConfiguration) {
- // Should never use a complementing configuration for the main
- // rendering's configuration; instead, create a new configuration
- // with a snapshot of the configuration's current values
- newConfiguration = Configuration.copy(previewConfiguration);
-
- // Remap all the previews to be parented to this new copy instead
- // of the old one (which is no longer controlled by the chooser)
- for (RenderPreview p : mPreviews) {
- Configuration configuration = p.getConfiguration();
- if (configuration instanceof NestedConfiguration) {
- NestedConfiguration nested = (NestedConfiguration) configuration;
- nested.setParent(newConfiguration);
- }
- }
- }
-
- // Make a preview for the configuration which *was* showing in the
- // chooser up until this point:
- RenderPreview newPreview = mCanvas.getPreview();
- if (newPreview == null) {
- newPreview = RenderPreview.create(this, originalConfiguration);
- }
-
- // Update its configuration such that it is complementing or inheriting
- // from the new chosen configuration
- if (previewConfiguration instanceof VaryingConfiguration) {
- VaryingConfiguration varying = VaryingConfiguration.create(
- (VaryingConfiguration) previewConfiguration,
- newConfiguration);
- varying.updateDisplayName();
- originalConfiguration = varying;
- newPreview.setConfiguration(originalConfiguration);
- } else if (previewConfiguration instanceof NestedConfiguration) {
- NestedConfiguration nested = NestedConfiguration.create(
- (NestedConfiguration) previewConfiguration,
- originalConfiguration,
- newConfiguration);
- nested.setDisplayName(nested.computeDisplayName());
- originalConfiguration = nested;
- newPreview.setConfiguration(originalConfiguration);
- }
-
- // Replace clicked preview with preview of the formerly edited main configuration
- // This doesn't work yet because the image overlay has had its image
- // replaced by the configuration previews! I should make a list of them
- //newPreview.setFullImage(mImageOverlay.getAwtImage());
- for (int i = 0, n = mPreviews.size(); i < n; i++) {
- if (preview == mPreviews.get(i)) {
- mPreviews.set(i, newPreview);
- break;
- }
- }
-
- // Stash the corresponding preview (not active) on the canvas so we can
- // retrieve it if clicking to some other preview later
- mCanvas.setPreview(preview);
- preview.setVisible(false);
-
- // Switch to the configuration from the clicked preview (though it's
- // most likely a copy, see above)
- chooser.setConfiguration(newConfiguration);
- editor.changed(MASK_ALL);
-
- // Scroll to the top again, if necessary
- mCanvas.getVerticalBar().setSelection(mCanvas.getVerticalBar().getMinimum());
-
- mNeedLayout = mNeedZoom = true;
- mCanvas.redraw();
- mAnimation = new SwapAnimation(preview, newPreview);
- }
-
- /**
- * Gets the preview at the given location, or null if none. This is
- * currently deeply tied to where things are painted in onPaint().
- */
- RenderPreview getPreview(ControlPoint mousePos) {
- if (hasPreviews()) {
- int rootX = getX();
- if (mousePos.x < rootX) {
- return null;
- }
- int rootY = getY();
-
- for (RenderPreview preview : mPreviews) {
- int x = rootX + preview.getX();
- int y = rootY + preview.getY();
- if (mousePos.x >= x && mousePos.x <= x + preview.getWidth()) {
- if (mousePos.y >= y && mousePos.y <= y + preview.getHeight()) {
- return preview;
- }
- }
- }
- }
-
- return null;
- }
-
- private int getX() {
- return mHScale.translate(0);
- }
-
- private int getY() {
- return mVScale.translate(0);
- }
-
- private int getZoomX() {
- Rectangle clientArea = mCanvas.getClientArea();
- int x = clientArea.x + clientArea.width - ZOOM_ICON_WIDTH;
- if (x < mHScale.getScaledImgSize() + PREVIEW_HGAP) {
- // No visible previews because the main image is zoomed too far
- return -1;
- }
-
- return x - 6;
- }
-
- private int getZoomY() {
- Rectangle clientArea = mCanvas.getClientArea();
- return clientArea.y + 5;
- }
-
- /**
- * Returns the height of the layout
- *
- * @return the height
- */
- public int getHeight() {
- return mLayoutHeight;
- }
-
- /**
- * Notifies that preview manager that the mouse cursor has moved to the
- * given control position within the layout canvas
- *
- * @param mousePos the mouse position, relative to the layout canvas
- */
- public void moved(ControlPoint mousePos) {
- RenderPreview hovered = getPreview(mousePos);
- if (hovered != mActivePreview) {
- if (mActivePreview != null) {
- mActivePreview.setActive(false);
- }
- mActivePreview = hovered;
- if (mActivePreview != null) {
- mActivePreview.setActive(true);
- }
- mCanvas.redraw();
- }
- }
-
- /**
- * Notifies that preview manager that the mouse cursor has entered the layout canvas
- *
- * @param mousePos the mouse position, relative to the layout canvas
- */
- public void enter(ControlPoint mousePos) {
- moved(mousePos);
- }
-
- /**
- * Notifies that preview manager that the mouse cursor has exited the layout canvas
- *
- * @param mousePos the mouse position, relative to the layout canvas
- */
- public void exit(ControlPoint mousePos) {
- if (mActivePreview != null) {
- mActivePreview.setActive(false);
- }
- mActivePreview = null;
- mCanvas.redraw();
- }
-
- /**
- * Process a mouse click, and return true if it was handled by this manager
- * (e.g. the click was on a preview)
- *
- * @param mousePos the mouse position where the click occurred
- * @return true if the click occurred over a preview and was handled, false otherwise
- */
- public boolean click(ControlPoint mousePos) {
- // Clicked zoom?
- int x = getZoomX();
- if (x > 0) {
- if (mousePos.x >= x && mousePos.x <= x + ZOOM_ICON_WIDTH) {
- int y = getZoomY();
- if (mousePos.y >= y && mousePos.y <= y + 4 * ZOOM_ICON_HEIGHT) {
- if (mousePos.y < y + ZOOM_ICON_HEIGHT) {
- zoomIn();
- } else if (mousePos.y < y + 2 * ZOOM_ICON_HEIGHT) {
- zoomOut();
- } else if (mousePos.y < y + 3 * ZOOM_ICON_HEIGHT) {
- zoomReset();
- } else {
- selectMode(NONE);
- }
- return true;
- }
- }
- }
-
- RenderPreview preview = getPreview(mousePos);
- if (preview != null) {
- boolean handled = preview.click(mousePos.x - getX() - preview.getX(),
- mousePos.y - getY() - preview.getY());
- if (handled) {
- // In case layout was performed, there could be a new preview
- // under this coordinate now, so make sure it's hover etc
- // shows up
- moved(mousePos);
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns true if there are thumbnail previews
- *
- * @return true if thumbnails are being shown
- */
- public boolean hasPreviews() {
- return mPreviews != null && !mPreviews.isEmpty();
- }
-
-
- private void sortPreviewsByScreenSize() {
- if (mPreviews != null) {
- Collections.sort(mPreviews, new Comparator<RenderPreview>() {
- @Override
- public int compare(RenderPreview preview1, RenderPreview preview2) {
- Configuration config1 = preview1.getConfiguration();
- Configuration config2 = preview2.getConfiguration();
- Device device1 = config1.getDevice();
- Device device2 = config1.getDevice();
- if (device1 != null && device2 != null) {
- Screen screen1 = device1.getDefaultHardware().getScreen();
- Screen screen2 = device2.getDefaultHardware().getScreen();
- if (screen1 != null && screen2 != null) {
- double delta = screen1.getDiagonalLength()
- - screen2.getDiagonalLength();
- if (delta != 0.0) {
- return (int) Math.signum(delta);
- } else {
- if (screen1.getPixelDensity() != screen2.getPixelDensity()) {
- return screen1.getPixelDensity().compareTo(
- screen2.getPixelDensity());
- }
- }
- }
-
- }
- State state1 = config1.getDeviceState();
- State state2 = config2.getDeviceState();
- if (state1 != state2 && state1 != null && state2 != null) {
- return state1.getName().compareTo(state2.getName());
- }
-
- return preview1.getDisplayName().compareTo(preview2.getDisplayName());
- }
- });
- }
- }
-
- private void sortPreviewsByOrientation() {
- if (mPreviews != null) {
- Collections.sort(mPreviews, new Comparator<RenderPreview>() {
- @Override
- public int compare(RenderPreview preview1, RenderPreview preview2) {
- Configuration config1 = preview1.getConfiguration();
- Configuration config2 = preview2.getConfiguration();
- State state1 = config1.getDeviceState();
- State state2 = config2.getDeviceState();
- if (state1 != state2 && state1 != null && state2 != null) {
- return state1.getName().compareTo(state2.getName());
- }
-
- return preview1.getDisplayName().compareTo(preview2.getDisplayName());
- }
- });
- }
- }
-
- /**
- * Vertical scrollbar listener which updates render previews which are not
- * visible and triggers a redraw
- */
- private class ScrollBarListener implements SelectionListener {
- @Override
- public void widgetSelected(SelectionEvent e) {
- if (mPreviews == null) {
- return;
- }
-
- ScrollBar bar = mCanvas.getVerticalBar();
- int selection = bar.getSelection();
- int thumb = bar.getThumb();
- int maxY = selection + thumb;
- beginRenderScheduling();
- for (RenderPreview preview : mPreviews) {
- if (!preview.isVisible() && preview.getY() <= maxY) {
- preview.setVisible(true);
- }
- }
- }
-
- @Override
- public void widgetDefaultSelected(SelectionEvent e) {
- }
- }
-
- /** Animation overlay shown briefly after swapping two previews */
- private class SwapAnimation implements Runnable {
- private long begin;
- private long end;
- private static final long DURATION = 400; // ms
- private Rect initialRect1;
- private Rect targetRect1;
- private Rect initialRect2;
- private Rect targetRect2;
- private RenderPreview preview;
-
- SwapAnimation(RenderPreview preview1, RenderPreview preview2) {
- begin = System.currentTimeMillis();
- end = begin + DURATION;
-
- initialRect1 = new Rect(preview1.getX(), preview1.getY(),
- preview1.getWidth(), preview1.getHeight());
-
- CanvasTransform hi = mCanvas.getHorizontalTransform();
- CanvasTransform vi = mCanvas.getVerticalTransform();
- initialRect2 = new Rect(hi.translate(0), vi.translate(0),
- hi.getScaledImgSize(), vi.getScaledImgSize());
- preview = preview2;
- }
-
- void tick(GC gc) {
- long now = System.currentTimeMillis();
- if (now > end || mCanvas.isDisposed()) {
- mAnimation = null;
- return;
- }
-
- CanvasTransform hi = mCanvas.getHorizontalTransform();
- CanvasTransform vi = mCanvas.getVerticalTransform();
- if (targetRect1 == null) {
- targetRect1 = new Rect(hi.translate(0), vi.translate(0),
- hi.getScaledImgSize(), vi.getScaledImgSize());
- }
- double portion = (now - begin) / (double) DURATION;
- Rect rect1 = new Rect(
- (int) (portion * (targetRect1.x - initialRect1.x) + initialRect1.x),
- (int) (portion * (targetRect1.y - initialRect1.y) + initialRect1.y),
- (int) (portion * (targetRect1.w - initialRect1.w) + initialRect1.w),
- (int) (portion * (targetRect1.h - initialRect1.h) + initialRect1.h));
-
- if (targetRect2 == null) {
- targetRect2 = new Rect(preview.getX(), preview.getY(),
- preview.getWidth(), preview.getHeight());
- }
- portion = (now - begin) / (double) DURATION;
- Rect rect2 = new Rect(
- (int) (portion * (targetRect2.x - initialRect2.x) + initialRect2.x),
- (int) (portion * (targetRect2.y - initialRect2.y) + initialRect2.y),
- (int) (portion * (targetRect2.w - initialRect2.w) + initialRect2.w),
- (int) (portion * (targetRect2.h - initialRect2.h) + initialRect2.h));
-
- gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY));
- gc.drawRectangle(rect1.x, rect1.y, rect1.w, rect1.h);
- gc.drawRectangle(rect2.x, rect2.y, rect2.w, rect2.h);
-
- mCanvas.getDisplay().timerExec(5, this);
- }
-
- @Override
- public void run() {
- mCanvas.redraw();
- }
- }
-
- /**
- * Notifies the {@linkplain RenderPreviewManager} that the configuration used
- * in the main chooser has been changed. This may require updating parent references
- * in the preview configurations inheriting from it.
- *
- * @param oldConfiguration the previous configuration
- * @param newConfiguration the new configuration in the chooser
- */
- public void updateChooserConfig(
- @NonNull Configuration oldConfiguration,
- @NonNull Configuration newConfiguration) {
- if (hasPreviews()) {
- for (RenderPreview preview : mPreviews) {
- Configuration configuration = preview.getConfiguration();
- if (configuration instanceof NestedConfiguration) {
- NestedConfiguration nestedConfig = (NestedConfiguration) configuration;
- if (nestedConfig.getParent() == oldConfiguration) {
- nestedConfig.setParent(newConfiguration);
- }
- }
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewMode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewMode.java
deleted file mode 100644
index 0f06d7f8a..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewMode.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-/**
- * The {@linkplain RenderPreviewMode} records what type of configurations to
- * render in the layout editor
- */
-public enum RenderPreviewMode {
- /** Generate a set of default previews with maximum variation */
- DEFAULT,
-
- /** Preview all the locales */
- LOCALES,
-
- /** Preview all the screen sizes */
- SCREENS,
-
- /** Preview layout as included in other layouts */
- INCLUDES,
-
- /** Preview all the variations of this layout */
- VARIATIONS,
-
- /** Show a manually configured set of previews */
- CUSTOM,
-
- /** No previews */
- NONE;
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderService.java
deleted file mode 100644
index 3b9e2fc0f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderService.java
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
- * Copyright (C) 2011 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.LAYOUT_RESOURCE_PREFIX;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.api.IClientRulesEngine;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.rendering.HardwareConfigHelper;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.RenderSecurityManager;
-import com.android.ide.common.rendering.api.AssetRepository;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.DrawableParams;
-import com.android.ide.common.rendering.api.HardwareConfig;
-import com.android.ide.common.rendering.api.IImageFactory;
-import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.AdtUtils;
-import com.android.ide.eclipse.adt.internal.editors.layout.ContextPullParser;
-import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback;
-import com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-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.manifest.ManifestInfo.ActivityAttributes;
-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.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.devices.Device;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.eclipse.core.resources.IProject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.awt.Toolkit;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The {@link RenderService} provides rendering and layout information for
- * Android layouts. This is a wrapper around the layout library.
- */
-public class RenderService {
- private static final Object RENDERING_LOCK = new Object();
-
- /** Reference to the file being edited. Can also be used to access the {@link IProject}. */
- private final GraphicalEditorPart mEditor;
-
- // The following fields are inferred from the editor and not customizable by the
- // client of the render service:
-
- private final IProject mProject;
- private final ProjectCallback mProjectCallback;
- private final ResourceResolver mResourceResolver;
- private final int mMinSdkVersion;
- private final int mTargetSdkVersion;
- private final LayoutLibrary mLayoutLib;
- private final IImageFactory mImageFactory;
- private final HardwareConfigHelper mHardwareConfigHelper;
- private final Locale mLocale;
-
- // The following fields are optional or configurable using the various chained
- // setters:
-
- private UiDocumentNode mModel;
- private Reference mIncludedWithin;
- private RenderingMode mRenderingMode = RenderingMode.NORMAL;
- private LayoutLog mLogger;
- private Integer mOverrideBgColor;
- private boolean mShowDecorations = true;
- private Set<UiElementNode> mExpandNodes = Collections.<UiElementNode>emptySet();
- private final Object mCredential;
-
- /** Use the {@link #create} factory instead */
- private RenderService(GraphicalEditorPart editor, Object credential) {
- mEditor = editor;
- mCredential = credential;
-
- mProject = editor.getProject();
- LayoutCanvas canvas = editor.getCanvasControl();
- mImageFactory = canvas.getImageOverlay();
- ConfigurationChooser chooser = editor.getConfigurationChooser();
- Configuration config = chooser.getConfiguration();
- FolderConfiguration folderConfig = config.getFullConfig();
-
- Device device = config.getDevice();
- assert device != null; // Should only attempt render with configuration that has device
- mHardwareConfigHelper = new HardwareConfigHelper(device);
- mHardwareConfigHelper.setOrientation(
- folderConfig.getScreenOrientationQualifier().getValue());
-
- mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/);
- mResourceResolver = editor.getResourceResolver();
- mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib);
- mMinSdkVersion = editor.getMinSdkVersion();
- mTargetSdkVersion = editor.getTargetSdkVersion();
- mLocale = config.getLocale();
- }
-
- private RenderService(GraphicalEditorPart editor,
- Configuration configuration, ResourceResolver resourceResolver,
- Object credential) {
- mEditor = editor;
- mCredential = credential;
-
- mProject = editor.getProject();
- LayoutCanvas canvas = editor.getCanvasControl();
- mImageFactory = canvas.getImageOverlay();
- FolderConfiguration folderConfig = configuration.getFullConfig();
-
- Device device = configuration.getDevice();
- assert device != null;
- mHardwareConfigHelper = new HardwareConfigHelper(device);
- mHardwareConfigHelper.setOrientation(
- folderConfig.getScreenOrientationQualifier().getValue());
-
- mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/);
- mResourceResolver = resourceResolver != null ? resourceResolver : editor.getResourceResolver();
- mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib);
- mMinSdkVersion = editor.getMinSdkVersion();
- mTargetSdkVersion = editor.getTargetSdkVersion();
- mLocale = configuration.getLocale();
- }
-
- private RenderSecurityManager createSecurityManager() {
- String projectPath = null;
- String sdkPath = null;
- if (RenderSecurityManager.RESTRICT_READS) {
- projectPath = AdtUtils.getAbsolutePath(mProject).toFile().getPath();
- Sdk sdk = Sdk.getCurrent();
- sdkPath = sdk != null ? sdk.getSdkOsLocation() : null;
- }
- RenderSecurityManager securityManager = new RenderSecurityManager(sdkPath, projectPath);
- securityManager.setLogger(AdtPlugin.getDefault());
-
- // Make sure this is initialized before we attempt to use it from layoutlib
- Toolkit.getDefaultToolkit();
-
- return securityManager;
- }
-
- /**
- * Returns true if this configuration supports the given rendering
- * capability
- *
- * @param target the target to look up the layout library for
- * @param capability the capability to check
- * @return true if the capability is supported
- */
- public static boolean supports(
- @NonNull IAndroidTarget target,
- @NonNull Capability capability) {
- Sdk sdk = Sdk.getCurrent();
- if (sdk != null) {
- AndroidTargetData targetData = sdk.getTargetData(target);
- if (targetData != null) {
- LayoutLibrary layoutLib = targetData.getLayoutLibrary();
- if (layoutLib != null) {
- return layoutLib.supports(capability);
- }
- }
- }
-
- return false;
- }
-
- /**
- * Creates a new {@link RenderService} associated with the given editor.
- *
- * @param editor the editor to provide configuration data such as the render target
- * @return a {@link RenderService} which can perform rendering services
- */
- public static RenderService create(GraphicalEditorPart editor) {
- // Delegate to editor such that it can pass its credential to the service
- return editor.createRenderService();
- }
-
- /**
- * Creates a new {@link RenderService} associated with the given editor.
- *
- * @param editor the editor to provide configuration data such as the render target
- * @param credential the sandbox credential
- * @return a {@link RenderService} which can perform rendering services
- */
- @NonNull
- public static RenderService create(GraphicalEditorPart editor, Object credential) {
- return new RenderService(editor, credential);
- }
-
- /**
- * Creates a new {@link RenderService} associated with the given editor.
- *
- * @param editor the editor to provide configuration data such as the render target
- * @param configuration the configuration to use (and fallback to editor for the rest)
- * @param resolver a resource resolver to use to look up resources
- * @return a {@link RenderService} which can perform rendering services
- */
- public static RenderService create(GraphicalEditorPart editor,
- Configuration configuration, ResourceResolver resolver) {
- // Delegate to editor such that it can pass its credential to the service
- return editor.createRenderService(configuration, resolver);
- }
-
- /**
- * Creates a new {@link RenderService} associated with the given editor.
- *
- * @param editor the editor to provide configuration data such as the render target
- * @param configuration the configuration to use (and fallback to editor for the rest)
- * @param resolver a resource resolver to use to look up resources
- * @param credential the sandbox credential
- * @return a {@link RenderService} which can perform rendering services
- */
- public static RenderService create(GraphicalEditorPart editor,
- Configuration configuration, ResourceResolver resolver, Object credential) {
- return new RenderService(editor, configuration, resolver, credential);
- }
-
- /**
- * Renders the given model, using this editor's theme and screen settings, and returns
- * the result as a {@link RenderSession}.
- *
- * @param model the model to be rendered, which can be different than the editor's own
- * {@link #getModel()}.
- * @param width the width to use for the layout, or -1 to use the width of the screen
- * associated with this editor
- * @param height the height to use for the layout, or -1 to use the height of the screen
- * associated with this editor
- * @param explodeNodes a set of nodes to explode, or null for none
- * @param overrideBgColor If non-null, use the given color as a background to render over
- * rather than the normal background requested by the theme
- * @param noDecor If true, don't draw window decorations like the system bar
- * @param logger a logger where rendering errors are reported
- * @param renderingMode the {@link RenderingMode} to use for rendering
- * @return the resulting rendered image wrapped in an {@link RenderSession}
- */
-
- /**
- * Sets the {@link LayoutLog} to be used during rendering. If none is specified, a
- * silent logger will be used.
- *
- * @param logger the log to be used
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setLog(LayoutLog logger) {
- mLogger = logger;
- return this;
- }
-
- /**
- * Sets the model to be rendered, which can be different than the editor's own
- * {@link GraphicalEditorPart#getModel()}.
- *
- * @param model the model to be rendered
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setModel(UiDocumentNode model) {
- mModel = model;
- return this;
- }
-
- /**
- * Overrides the width and height to be used during rendering (which might be adjusted if
- * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}.
- *
- * A value of -1 will make the rendering use the normal width and height coming from the
- * {@link Configuration#getDevice()} object.
- *
- * @param overrideRenderWidth the width in pixels of the layout to be rendered
- * @param overrideRenderHeight the height in pixels of the layout to be rendered
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setOverrideRenderSize(int overrideRenderWidth, int overrideRenderHeight) {
- mHardwareConfigHelper.setOverrideRenderSize(overrideRenderWidth, overrideRenderHeight);
- return this;
- }
-
- /**
- * Sets the max width and height to be used during rendering (which might be adjusted if
- * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}.
- *
- * A value of -1 will make the rendering use the normal width and height coming from the
- * {@link Configuration#getDevice()} object.
- *
- * @param maxRenderWidth the max width in pixels of the layout to be rendered
- * @param maxRenderHeight the max height in pixels of the layout to be rendered
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setMaxRenderSize(int maxRenderWidth, int maxRenderHeight) {
- mHardwareConfigHelper.setMaxRenderSize(maxRenderWidth, maxRenderHeight);
- return this;
- }
-
- /**
- * Sets the {@link RenderingMode} to be used during rendering. If none is specified,
- * the default is {@link RenderingMode#NORMAL}.
- *
- * @param renderingMode the rendering mode to be used
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setRenderingMode(RenderingMode renderingMode) {
- mRenderingMode = renderingMode;
- return this;
- }
-
- /**
- * Sets the overriding background color to be used, if any. The color should be a
- * bitmask of AARRGGBB. The default is null.
- *
- * @param overrideBgColor the overriding background color to be used in the rendering,
- * in the form of a AARRGGBB bitmask, or null to use no custom background.
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setOverrideBgColor(Integer overrideBgColor) {
- mOverrideBgColor = overrideBgColor;
- return this;
- }
-
- /**
- * Sets whether the rendering should include decorations such as a system bar, an
- * application bar etc depending on the SDK target and theme. The default is true.
- *
- * @param showDecorations true if the rendering should include system bars etc.
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setDecorations(boolean showDecorations) {
- mShowDecorations = showDecorations;
- return this;
- }
-
- /**
- * Sets the nodes to expand during rendering. These will be padded with approximately
- * 20 pixels and also highlighted by the {@link EmptyViewsOverlay}. The default is an
- * empty collection.
- *
- * @param nodesToExpand the nodes to be expanded
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setNodesToExpand(Set<UiElementNode> nodesToExpand) {
- mExpandNodes = nodesToExpand;
- return this;
- }
-
- /**
- * Sets the {@link Reference} to an outer layout that this layout should be rendered
- * within. The outer layout <b>must</b> contain an include tag which points to this
- * layout. The default is null.
- *
- * @param includedWithin a reference to an outer layout to render this layout within
- * @return this (such that chains of setters can be stringed together)
- */
- public RenderService setIncludedWithin(Reference includedWithin) {
- mIncludedWithin = includedWithin;
- return this;
- }
-
- /** Initializes any remaining optional fields after all setters have been called */
- private void finishConfiguration() {
- if (mLogger == null) {
- // Silent logging
- mLogger = new LayoutLog();
- }
- }
-
- /**
- * Renders the model and returns the result as a {@link RenderSession}.
- * @return the {@link RenderSession} resulting from rendering the current model
- */
- public RenderSession createRenderSession() {
- assert mModel != null : "Incomplete service config";
- finishConfiguration();
-
- if (mResourceResolver == null) {
- // Abort the rendering if the resources are not found.
- return null;
- }
-
- HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
-
- UiElementPullParser modelParser = new UiElementPullParser(mModel,
- false, mExpandNodes, hardwareConfig.getDensity(), mProject);
- ILayoutPullParser topParser = modelParser;
-
- // Code to support editing included layout
- // first reset the layout parser just in case.
- mProjectCallback.setLayoutParser(null, null);
-
- if (mIncludedWithin != null) {
- // Outer layout name:
- String contextLayoutName = mIncludedWithin.getName();
-
- // Find the layout file.
- ResourceValue contextLayout = mResourceResolver.findResValue(
- LAYOUT_RESOURCE_PREFIX + contextLayoutName, false /* forceFrameworkOnly*/);
- if (contextLayout != null) {
- File layoutFile = new File(contextLayout.getValue());
- if (layoutFile.isFile()) {
- try {
- // Get the name of the layout actually being edited, without the extension
- // as it's what IXmlPullParser.getParser(String) will receive.
- String queryLayoutName = mEditor.getLayoutResourceName();
- mProjectCallback.setLayoutParser(queryLayoutName, modelParser);
- topParser = new ContextPullParser(mProjectCallback, layoutFile);
- topParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- String xmlText = Files.toString(layoutFile, Charsets.UTF_8);
- topParser.setInput(new StringReader(xmlText));
- } catch (IOException e) {
- AdtPlugin.log(e, null);
- } catch (XmlPullParserException e) {
- AdtPlugin.log(e, null);
- }
- }
- }
- }
-
- SessionParams params = new SessionParams(
- topParser,
- mRenderingMode,
- mProject /* projectKey */,
- hardwareConfig,
- mResourceResolver,
- mProjectCallback,
- mMinSdkVersion,
- mTargetSdkVersion,
- mLogger);
-
- // Request margin and baseline information.
- // TODO: Be smarter about setting this; start without it, and on the first request
- // for an extended view info, re-render in the same session, and then set a flag
- // which will cause this to create extended view info each time from then on in the
- // same session
- params.setExtendedViewInfoMode(true);
-
- params.setLocale(mLocale.toLocaleId());
- params.setAssetRepository(new AssetRepository());
-
- ManifestInfo manifestInfo = ManifestInfo.get(mProject);
- try {
- params.setRtlSupport(manifestInfo.isRtlSupported());
- } catch (Exception e) {
- // ignore.
- }
- if (!mShowDecorations) {
- params.setForceNoDecor();
- } else {
- try {
- params.setAppLabel(manifestInfo.getApplicationLabel());
- params.setAppIcon(manifestInfo.getApplicationIcon());
- String activity = mEditor.getConfigurationChooser().getConfiguration().getActivity();
- if (activity != null) {
- ActivityAttributes info = manifestInfo.getActivityAttributes(activity);
- if (info != null) {
- if (info.getLabel() != null) {
- params.setAppLabel(info.getLabel());
- }
- if (info.getIcon() != null) {
- params.setAppIcon(info.getIcon());
- }
- }
- }
- } catch (Exception e) {
- // ignore.
- }
- }
-
- if (mOverrideBgColor != null) {
- params.setOverrideBgColor(mOverrideBgColor.intValue());
- }
-
- // set the Image Overlay as the image factory.
- params.setImageFactory(mImageFactory);
-
- mProjectCallback.setLogger(mLogger);
- mProjectCallback.setResourceResolver(mResourceResolver);
- RenderSecurityManager securityManager = createSecurityManager();
- try {
- securityManager.setActive(true, mCredential);
- synchronized (RENDERING_LOCK) {
- return mLayoutLib.createSession(params);
- }
- } catch (RuntimeException t) {
- // Exceptions from the bridge
- mLogger.error(null, t.getLocalizedMessage(), t, null);
- throw t;
- } finally {
- securityManager.dispose(mCredential);
- mProjectCallback.setLogger(null);
- mProjectCallback.setResourceResolver(null);
- }
- }
-
- /**
- * Renders the given resource value (which should refer to a drawable) and returns it
- * as an image
- *
- * @param drawableResourceValue the drawable resource value to be rendered, or null
- * @return the image, or null if something went wrong
- */
- public BufferedImage renderDrawable(ResourceValue drawableResourceValue) {
- if (drawableResourceValue == null) {
- return null;
- }
-
- finishConfiguration();
-
- HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
-
- DrawableParams params = new DrawableParams(drawableResourceValue, mProject, hardwareConfig,
- mResourceResolver, mProjectCallback, mMinSdkVersion,
- mTargetSdkVersion, mLogger);
- params.setAssetRepository(new AssetRepository());
- params.setForceNoDecor();
- Result result = mLayoutLib.renderDrawable(params);
- if (result != null && result.isSuccess()) {
- Object data = result.getData();
- if (data instanceof BufferedImage) {
- return (BufferedImage) data;
- }
- }
-
- return null;
- }
-
- /**
- * Measure the children of the given parent node, applying the given filter to the
- * pull parser's attribute values.
- *
- * @param parent the parent node to measure children for
- * @param filter the filter to apply to the attribute values
- * @return a map from node children of the parent to new bounds of the nodes
- */
- public Map<INode, Rect> measureChildren(INode parent,
- final IClientRulesEngine.AttributeFilter filter) {
- finishConfiguration();
- HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig();
-
- final NodeFactory mNodeFactory = mEditor.getCanvasControl().getNodeFactory();
- UiElementNode parentNode = ((NodeProxy) parent).getNode();
- UiElementPullParser topParser = new UiElementPullParser(parentNode,
- false, Collections.<UiElementNode>emptySet(), hardwareConfig.getDensity(),
- mProject) {
- @Override
- public String getAttributeValue(String namespace, String localName) {
- if (filter != null) {
- Object cookie = getViewCookie();
- if (cookie instanceof UiViewElementNode) {
- NodeProxy node = mNodeFactory.create((UiViewElementNode) cookie);
- if (node != null) {
- String value = filter.getAttribute(node, namespace, localName);
- if (value != null) {
- return value;
- }
- // null means no preference, not "unset".
- }
- }
- }
-
- return super.getAttributeValue(namespace, localName);
- }
-
- /**
- * The parser usually assumes that the top level node is a document node that
- * should be skipped, and that's not the case when we render in the middle of
- * the tree, so override {@link UiElementPullParser#onNextFromStartDocument}
- * to change this behavior
- */
- @Override
- public void onNextFromStartDocument() {
- mParsingState = START_TAG;
- }
- };
-
- SessionParams params = new SessionParams(
- topParser,
- RenderingMode.FULL_EXPAND,
- mProject /* projectKey */,
- hardwareConfig,
- mResourceResolver,
- mProjectCallback,
- mMinSdkVersion,
- mTargetSdkVersion,
- mLogger);
- params.setLayoutOnly();
- params.setForceNoDecor();
- params.setAssetRepository(new AssetRepository());
-
- RenderSession session = null;
- mProjectCallback.setLogger(mLogger);
- mProjectCallback.setResourceResolver(mResourceResolver);
- RenderSecurityManager securityManager = createSecurityManager();
- try {
- securityManager.setActive(true, mCredential);
- synchronized (RENDERING_LOCK) {
- session = mLayoutLib.createSession(params);
- }
- if (session.getResult().isSuccess()) {
- assert session.getRootViews().size() == 1;
- ViewInfo root = session.getRootViews().get(0);
- List<ViewInfo> children = root.getChildren();
- Map<INode, Rect> map = new HashMap<INode, Rect>(children.size());
- for (ViewInfo info : children) {
- if (info.getCookie() instanceof UiViewElementNode) {
- UiViewElementNode uiNode = (UiViewElementNode) info.getCookie();
- NodeProxy node = mNodeFactory.create(uiNode);
- map.put(node, new Rect(info.getLeft(), info.getTop(),
- info.getRight() - info.getLeft(),
- info.getBottom() - info.getTop()));
- }
- }
-
- return map;
- }
- } catch (RuntimeException t) {
- // Exceptions from the bridge
- mLogger.error(null, t.getLocalizedMessage(), t, null);
- throw t;
- } finally {
- securityManager.dispose(mCredential);
- mProjectCallback.setLogger(null);
- mProjectCallback.setResourceResolver(null);
- if (session != null) {
- session.dispose();
- }
- }
-
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java
deleted file mode 100644
index 4d51c07de..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.ResizePolicy;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.Position;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.utils.Pair;
-
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.graphics.GC;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A {@link ResizeGesture} is a gesture for resizing a selected widget. It is initiated
- * by a drag of a {@link SelectionHandle}.
- */
-public class ResizeGesture extends Gesture {
- /** The {@link Overlay} drawn for the gesture feedback. */
- private ResizeOverlay mOverlay;
-
- /** The canvas associated with this gesture. */
- private LayoutCanvas mCanvas;
-
- /** The selection handle we're dragging to perform this resize */
- private SelectionHandle mHandle;
-
- private NodeProxy mParentNode;
- private NodeProxy mChildNode;
- private DropFeedback mFeedback;
- private ResizePolicy mResizePolicy;
- private SegmentType mHorizontalEdge;
- private SegmentType mVerticalEdge;
-
- /**
- * Creates a new marquee selection (selection swiping).
- *
- * @param canvas The canvas where selection is performed.
- * @param item The selected item the handle corresponds to
- * @param handle The handle being dragged to perform the resize
- */
- public ResizeGesture(LayoutCanvas canvas, SelectionItem item, SelectionHandle handle) {
- mCanvas = canvas;
- mHandle = handle;
-
- mChildNode = item.getNode();
- mParentNode = (NodeProxy) mChildNode.getParent();
- mResizePolicy = item.getResizePolicy();
- mHorizontalEdge = getHorizontalEdgeType(mHandle);
- mVerticalEdge = getVerticalEdgeType(mHandle);
- }
-
- @Override
- public void begin(ControlPoint pos, int startMask) {
- super.begin(pos, startMask);
-
- mCanvas.getSelectionOverlay().setHidden(true);
-
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- Rect newBounds = getNewBounds(pos);
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- CanvasViewInfo childInfo = viewHierarchy.findViewInfoFor(mChildNode);
- CanvasViewInfo parentInfo = viewHierarchy.findViewInfoFor(mParentNode);
- Object childView = childInfo != null ? childInfo.getViewObject() : null;
- Object parentView = parentInfo != null ? parentInfo.getViewObject() : null;
- mFeedback = rulesEngine.callOnResizeBegin(mChildNode, mParentNode, newBounds,
- mHorizontalEdge, mVerticalEdge, childView, parentView);
- update(pos);
- mCanvas.getGestureManager().updateMessage(mFeedback);
- }
-
- @Override
- public boolean keyPressed(KeyEvent event) {
- update(mCanvas.getGestureManager().getCurrentControlPoint());
- mCanvas.redraw();
- return true;
- }
-
- @Override
- public boolean keyReleased(KeyEvent event) {
- update(mCanvas.getGestureManager().getCurrentControlPoint());
- mCanvas.redraw();
- return true;
- }
-
- @Override
- public void update(ControlPoint pos) {
- super.update(pos);
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- Rect newBounds = getNewBounds(pos);
- int modifierMask = mCanvas.getGestureManager().getRuleModifierMask();
- rulesEngine.callOnResizeUpdate(mFeedback, mChildNode, mParentNode, newBounds,
- modifierMask);
- mCanvas.getGestureManager().updateMessage(mFeedback);
- }
-
- @Override
- public void end(ControlPoint pos, boolean canceled) {
- super.end(pos, canceled);
-
- if (!canceled) {
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- Rect newBounds = getNewBounds(pos);
- rulesEngine.callOnResizeEnd(mFeedback, mChildNode, mParentNode, newBounds);
- }
-
- mCanvas.getSelectionOverlay().setHidden(false);
- }
-
- @Override
- public Pair<Boolean, Boolean> getTooltipPosition() {
- return Pair.of(mHorizontalEdge != SegmentType.TOP, mVerticalEdge != SegmentType.LEFT);
- }
-
- /**
- * For the new mouse position, compute the resized bounds (the bounding rectangle that
- * the view should be resized to). This is not just a width or height, since in some
- * cases resizing will change the x/y position of the view as well (for example, in
- * RelativeLayout or in AbsoluteLayout).
- */
- private Rect getNewBounds(ControlPoint pos) {
- LayoutPoint p = pos.toLayout();
- LayoutPoint start = mStart.toLayout();
- Rect b = mChildNode.getBounds();
- Position direction = mHandle.getPosition();
-
- int x = b.x;
- int y = b.y;
- int w = b.w;
- int h = b.h;
- int deltaX = p.x - start.x;
- int deltaY = p.y - start.y;
-
- if (deltaX == 0 && deltaY == 0) {
- // No move - just use the existing bounds
- return b;
- }
-
- if (mResizePolicy.isAspectPreserving() && w != 0 && h != 0) {
- double aspectRatio = w / (double) h;
- int newW = Math.abs(b.w + (direction.isLeft() ? -deltaX : deltaX));
- int newH = Math.abs(b.h + (direction.isTop() ? -deltaY : deltaY));
- double newAspectRatio = newW / (double) newH;
- if (newH == 0 || newAspectRatio > aspectRatio) {
- deltaY = (int) (deltaX / aspectRatio);
- } else {
- deltaX = (int) (deltaY * aspectRatio);
- }
- }
- if (direction.isLeft()) {
- // The user is dragging the left edge, so the position is anchored on the
- // right.
- int x2 = b.x + b.w;
- int nx1 = b.x + deltaX;
- if (nx1 <= x2) {
- x = nx1;
- w = x2 - x;
- } else {
- w = 0;
- x = x2;
- }
- } else if (direction.isRight()) {
- // The user is dragging the right edge, so the position is anchored on the
- // left.
- int nx2 = b.x + b.w + deltaX;
- if (nx2 >= b.x) {
- w = nx2 - b.x;
- } else {
- w = 0;
- }
- } else {
- assert direction == Position.BOTTOM_MIDDLE || direction == Position.TOP_MIDDLE;
- }
-
- if (direction.isTop()) {
- // The user is dragging the top edge, so the position is anchored on the
- // bottom.
- int y2 = b.y + b.h;
- int ny1 = b.y + deltaY;
- if (ny1 < y2) {
- y = ny1;
- h = y2 - y;
- } else {
- h = 0;
- y = y2;
- }
- } else if (direction.isBottom()) {
- // The user is dragging the bottom edge, so the position is anchored on the
- // top.
- int ny2 = b.y + b.h + deltaY;
- if (ny2 >= b.y) {
- h = ny2 - b.y;
- } else {
- h = 0;
- }
- } else {
- assert direction == Position.LEFT_MIDDLE || direction == Position.RIGHT_MIDDLE;
- }
-
- return new Rect(x, y, w, h);
- }
-
- private static SegmentType getHorizontalEdgeType(SelectionHandle handle) {
- switch (handle.getPosition()) {
- case BOTTOM_LEFT:
- case BOTTOM_RIGHT:
- case BOTTOM_MIDDLE:
- return SegmentType.BOTTOM;
- case LEFT_MIDDLE:
- case RIGHT_MIDDLE:
- return null;
- case TOP_LEFT:
- case TOP_MIDDLE:
- case TOP_RIGHT:
- return SegmentType.TOP;
- default: assert false : handle.getPosition();
- }
- return null;
- }
-
- private static SegmentType getVerticalEdgeType(SelectionHandle handle) {
- switch (handle.getPosition()) {
- case TOP_LEFT:
- case LEFT_MIDDLE:
- case BOTTOM_LEFT:
- return SegmentType.LEFT;
- case BOTTOM_MIDDLE:
- case TOP_MIDDLE:
- return null;
- case TOP_RIGHT:
- case RIGHT_MIDDLE:
- case BOTTOM_RIGHT:
- return SegmentType.RIGHT;
- default: assert false : handle.getPosition();
- }
- return null;
- }
-
-
- @Override
- public List<Overlay> createOverlays() {
- mOverlay = new ResizeOverlay();
- return Collections.<Overlay> singletonList(mOverlay);
- }
-
- /**
- * An {@link Overlay} to paint the resize feedback. This just delegates to the
- * layout rule for the parent which is handling the resizing.
- */
- private class ResizeOverlay extends Overlay {
- @Override
- public void paint(GC gc) {
- if (mChildNode != null && mFeedback != null) {
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- rulesEngine.callDropFeedbackPaint(mCanvas.getGcWrapper(), mChildNode, mFeedback);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandle.java
deleted file mode 100644
index c2db2431c..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandle.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2011 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 org.eclipse.swt.SWT;
-
-/**
- * A selection handle is a small rectangle on the border of a selected view which lets you
- * change the size of the view by dragging it.
- */
-public class SelectionHandle {
- /**
- * Size of the selection handle radius, in control coordinates. Note that this isn't
- * necessarily a <b>circular</b> radius; in the case of a rectangular handle, the
- * width and the height are both equal to this radius.
- * Note also that this radius is in <b>control</b> coordinates, whereas the rest
- * of the class operates in layout coordinates. This is because we do not want the
- * selection handles to grow or shrink along with the screen zoom; they are always
- * at the given pixel size in the control.
- */
- public final static int PIXEL_RADIUS = 3;
-
- /**
- * Extra number of pixels to look beyond the actual radius of the selection handle
- * when matching mouse positions to handles
- */
- public final static int PIXEL_MARGIN = 2;
-
- /** The position of the handle in the selection rectangle */
- enum Position {
- TOP_MIDDLE(SWT.CURSOR_SIZEN),
- TOP_RIGHT(SWT.CURSOR_SIZENE),
- RIGHT_MIDDLE(SWT.CURSOR_SIZEE),
- BOTTOM_RIGHT(SWT.CURSOR_SIZESE),
- BOTTOM_MIDDLE(SWT.CURSOR_SIZES),
- BOTTOM_LEFT(SWT.CURSOR_SIZESW),
- LEFT_MIDDLE(SWT.CURSOR_SIZEW),
- TOP_LEFT(SWT.CURSOR_SIZENW);
-
- /** Corresponding SWT cursor value */
- private int mSwtCursor;
-
- private Position(int swtCursor) {
- mSwtCursor = swtCursor;
- }
-
- private int getCursorType() {
- return mSwtCursor;
- }
-
- /** Is the {@link SelectionHandle} somewhere on the left edge? */
- boolean isLeft() {
- return this == TOP_LEFT || this == LEFT_MIDDLE || this == BOTTOM_LEFT;
- }
-
- /** Is the {@link SelectionHandle} somewhere on the right edge? */
- boolean isRight() {
- return this == TOP_RIGHT || this == RIGHT_MIDDLE || this == BOTTOM_RIGHT;
- }
-
- /** Is the {@link SelectionHandle} somewhere on the top edge? */
- boolean isTop() {
- return this == TOP_LEFT || this == TOP_MIDDLE || this == TOP_RIGHT;
- }
-
- /** Is the {@link SelectionHandle} somewhere on the bottom edge? */
- boolean isBottom() {
- return this == BOTTOM_LEFT || this == BOTTOM_MIDDLE || this == BOTTOM_RIGHT;
- }
- };
-
- /** The x coordinate of the center of the selection handle */
- public final int centerX;
- /** The y coordinate of the center of the selection handle */
- public final int centerY;
- /** The position of the handle in the selection rectangle */
- private final Position mPosition;
-
- /**
- * Constructs a new {@link SelectionHandle} at the given layout coordinate
- * corresponding to a handle at the given {@link Position}.
- *
- * @param centerX the x coordinate of the center of the selection handle
- * @param centerY y coordinate of the center of the selection handle
- * @param position the position of the handle in the selection rectangle
- */
- public SelectionHandle(int centerX, int centerY, Position position) {
- mPosition = position;
- this.centerX = centerX;
- this.centerY = centerY;
- }
-
- /**
- * Determines whether the given {@link LayoutPoint} is within the given distance in
- * layout coordinates. The distance should incorporate at least the equivalent
- * distance to the control coordinate space {@link #PIXEL_RADIUS}, but usually with a
- * few extra pixels added in to make the corners easier to target.
- *
- * @param point the mouse position in layout coordinates
- * @param distance the distance from the center of the handle to check whether the
- * point fits within
- * @return true if the given point is within the given distance of this handle
- */
- public boolean contains(LayoutPoint point, int distance) {
- return (point.x >= centerX - distance
- && point.x <= centerX + distance
- && point.y >= centerY - distance
- && point.y <= centerY + distance);
- }
-
- /**
- * Returns the position of the handle in the selection rectangle
- *
- * @return the position of the handle in the selection rectangle
- */
- public Position getPosition() {
- return mPosition;
- }
-
- /**
- * Returns the SWT cursor type to use for this selection handle
- *
- * @return the position of the handle in the selection rectangle
- */
- public int getSwtCursorType() {
- return mPosition.getCursorType();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandles.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandles.java
deleted file mode 100644
index 6d7f34a66..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionHandles.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.ResizePolicy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.Position;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * The {@link SelectionHandles} of a {@link SelectionItem} are the set of
- * {@link SelectionHandle} objects (possibly empty, for non-resizable objects) the user
- * can manipulate to resize a widget.
- */
-public class SelectionHandles implements Iterable<SelectionHandle> {
- private final SelectionItem mItem;
- private List<SelectionHandle> mHandles;
-
- /**
- * Constructs a new {@link SelectionHandles} object for the given {link
- * {@link SelectionItem}
- * @param item the item to create {@link SelectionHandles} for
- */
- public SelectionHandles(SelectionItem item) {
- mItem = item;
-
- createHandles(item.getCanvas());
- }
-
- /**
- * Find a specific {@link SelectionHandle} from this set of {@link SelectionHandles},
- * which is within the given distance (in layout coordinates) from the center of the
- * {@link SelectionHandle}.
- *
- * @param point the mouse position (in layout coordinates) to test
- * @param distance the maximum distance from the handle center to accept
- * @return a {@link SelectionHandle} under the point, or null if not found
- */
- public SelectionHandle findHandle(LayoutPoint point, int distance) {
- for (SelectionHandle handle : mHandles) {
- if (handle.contains(point, distance)) {
- return handle;
- }
- }
-
- return null;
- }
-
- /**
- * Create the {@link SelectionHandle} objects for the selection item, according to its
- * {@link ResizePolicy}.
- */
- private void createHandles(LayoutCanvas canvas) {
- NodeProxy selectedNode = mItem.getNode();
- Rect r = selectedNode.getBounds();
- if (!r.isValid()) {
- mHandles = Collections.emptyList();
- return;
- }
-
- ResizePolicy resizability = mItem.getResizePolicy();
- if (resizability.isResizable()) {
- mHandles = new ArrayList<SelectionHandle>(8);
- boolean left = resizability.leftAllowed();
- boolean right = resizability.rightAllowed();
- boolean top = resizability.topAllowed();
- boolean bottom = resizability.bottomAllowed();
- int x1 = r.x;
- int y1 = r.y;
- int w = r.w;
- int h = r.h;
- int x2 = x1 + w;
- int y2 = y1 + h;
-
- Margins insets = canvas.getInsets(mItem.getNode().getFqcn());
- if (insets != null) {
- x1 += insets.left;
- x2 -= insets.right;
- y1 += insets.top;
- y2 -= insets.bottom;
- }
-
- int mx = (x1 + x2) / 2;
- int my = (y1 + y2) / 2;
-
- if (left) {
- mHandles.add(new SelectionHandle(x1, my, Position.LEFT_MIDDLE));
- if (top) {
- mHandles.add(new SelectionHandle(x1, y1, Position.TOP_LEFT));
- }
- if (bottom) {
- mHandles.add(new SelectionHandle(x1, y2, Position.BOTTOM_LEFT));
- }
- }
- if (right) {
- mHandles.add(new SelectionHandle(x2, my, Position.RIGHT_MIDDLE));
- if (top) {
- mHandles.add(new SelectionHandle(x2, y1, Position.TOP_RIGHT));
- }
- if (bottom) {
- mHandles.add(new SelectionHandle(x2, y2, Position.BOTTOM_RIGHT));
- }
- }
- if (top) {
- mHandles.add(new SelectionHandle(mx, y1, Position.TOP_MIDDLE));
- }
- if (bottom) {
- mHandles.add(new SelectionHandle(mx, y2, Position.BOTTOM_MIDDLE));
- }
- } else {
- mHandles = Collections.emptyList();
- }
- }
-
- // Implements Iterable<SelectionHandle>
- @Override
- public Iterator<SelectionHandle> iterator() {
- return mHandles.iterator();
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
deleted file mode 100644
index d104e379e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.ResizePolicy;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-
-import org.eclipse.swt.graphics.Rectangle;
-import org.w3c.dom.Node;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents one selection in {@link LayoutCanvas}.
- */
-class SelectionItem {
-
- /** The associated {@link LayoutCanvas} */
- private LayoutCanvas mCanvas;
-
- /** Current selected view info. Can be null. */
- private final CanvasViewInfo mCanvasViewInfo;
-
- /** Current selection border rectangle. Null when mCanvasViewInfo is null . */
- private final Rectangle mRect;
-
- /** The node proxy for drawing the selection. Null when mCanvasViewInfo is null. */
- private final NodeProxy mNodeProxy;
-
- /** The resize policy for this selection item */
- private ResizePolicy mResizePolicy;
-
- /** The selection handles for this item */
- private SelectionHandles mHandles;
-
- /**
- * Creates a new {@link SelectionItem} object.
- * @param canvas the associated canvas
- * @param canvasViewInfo The view info being selected. Must not be null.
- */
- public SelectionItem(LayoutCanvas canvas, CanvasViewInfo canvasViewInfo) {
- assert canvasViewInfo != null;
-
- mCanvas = canvas;
- mCanvasViewInfo = canvasViewInfo;
-
- if (canvasViewInfo == null) {
- mRect = null;
- mNodeProxy = null;
- } else {
- Rectangle r = canvasViewInfo.getSelectionRect();
- mRect = new Rectangle(r.x, r.y, r.width, r.height);
- mNodeProxy = mCanvas.getNodeFactory().create(canvasViewInfo);
- }
- }
-
- /**
- * Returns true when this selection item represents the root, the top level
- * layout element in the editor.
- *
- * @return True if and only if this element is at the root of the hierarchy
- */
- public boolean isRoot() {
- return mCanvasViewInfo.isRoot();
- }
-
- /**
- * Returns true if this item represents a widget that should not be manipulated by the
- * user.
- *
- * @return True if this widget should not be manipulated directly by the user
- */
- public boolean isHidden() {
- return mCanvasViewInfo.isHidden();
- }
-
- /**
- * Returns the selected view info. Cannot be null.
- *
- * @return the selected view info. Cannot be null.
- */
- @NonNull
- public CanvasViewInfo getViewInfo() {
- return mCanvasViewInfo;
- }
-
- /**
- * Returns the selected node.
- *
- * @return the selected node, or null
- */
- @Nullable
- public UiViewElementNode getUiNode() {
- return mCanvasViewInfo.getUiViewNode();
- }
-
- /**
- * Returns the selection border rectangle. Cannot be null.
- *
- * @return the selection border rectangle, never null
- */
- public Rectangle getRect() {
- return mRect;
- }
-
- /** Returns the node associated with this selection (may be null) */
- @Nullable
- NodeProxy getNode() {
- return mNodeProxy;
- }
-
- /** Returns the canvas associated with this selection (never null) */
- @NonNull
- LayoutCanvas getCanvas() {
- return mCanvas;
- }
-
- //----
-
- /**
- * Gets the XML text from the given selection for a text transfer.
- * The returned string can be empty but not null.
- */
- @NonNull
- static String getAsText(LayoutCanvas canvas, List<SelectionItem> selection) {
- StringBuilder sb = new StringBuilder();
-
- LayoutEditorDelegate layoutEditorDelegate = canvas.getEditorDelegate();
- for (SelectionItem cs : selection) {
- CanvasViewInfo vi = cs.getViewInfo();
- UiViewElementNode key = vi.getUiViewNode();
- Node node = key.getXmlNode();
- String t = layoutEditorDelegate.getEditor().getXmlText(node);
- if (t != null) {
- if (sb.length() > 0) {
- sb.append('\n');
- }
- sb.append(t);
- }
- }
-
- return sb.toString();
- }
-
- /**
- * Returns elements representing the given selection of canvas items.
- *
- * @param items Items to wrap in elements
- * @return An array of wrapper elements. Never null.
- */
- @NonNull
- static SimpleElement[] getAsElements(@NonNull List<SelectionItem> items) {
- return getAsElements(items, null);
- }
-
- /**
- * Returns elements representing the given selection of canvas items.
- *
- * @param items Items to wrap in elements
- * @param primary The primary selected item which should be listed first
- * @return An array of wrapper elements. Never null.
- */
- @NonNull
- static SimpleElement[] getAsElements(
- @NonNull List<SelectionItem> items,
- @Nullable SelectionItem primary) {
- List<SimpleElement> elements = new ArrayList<SimpleElement>();
-
- if (primary != null) {
- CanvasViewInfo vi = primary.getViewInfo();
- SimpleElement e = vi.toSimpleElement();
- e.setSelectionItem(primary);
- elements.add(e);
- }
-
- for (SelectionItem cs : items) {
- if (cs == primary) {
- // Already handled
- continue;
- }
-
- CanvasViewInfo vi = cs.getViewInfo();
- SimpleElement e = vi.toSimpleElement();
- e.setSelectionItem(cs);
- elements.add(e);
- }
-
- return elements.toArray(new SimpleElement[elements.size()]);
- }
-
- /**
- * Returns true if this selection item is a layout
- *
- * @return true if this selection item is a layout
- */
- public boolean isLayout() {
- UiViewElementNode node = mCanvasViewInfo.getUiViewNode();
- if (node != null) {
- return node.getDescriptor().hasChildren();
- } else {
- return false;
- }
- }
-
- /**
- * Returns the {@link SelectionHandles} for this {@link SelectionItem}. Never null.
- *
- * @return the {@link SelectionHandles} for this {@link SelectionItem}, never null
- */
- @NonNull
- public SelectionHandles getSelectionHandles() {
- if (mHandles == null) {
- mHandles = new SelectionHandles(this);
- }
-
- return mHandles;
- }
-
- /**
- * Returns the {@link ResizePolicy} for this item
- *
- * @return the {@link ResizePolicy} for this item, never null
- */
- @NonNull
- public ResizePolicy getResizePolicy() {
- if (mResizePolicy == null && mNodeProxy != null) {
- mResizePolicy = ViewMetadataRepository.get().getResizePolicy(mNodeProxy.getFqcn());
- }
-
- return mResizePolicy;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
deleted file mode 100644
index eb3d6f290..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
+++ /dev/null
@@ -1,1262 +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_ID;
-import static com.android.SdkConstants.FQCN_SPACE;
-import static com.android.SdkConstants.FQCN_SPACE_V7;
-import static com.android.SdkConstants.NEW_ID_PREFIX;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_MARGIN;
-import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_RADIUS;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.layout.BaseViewRule;
-import com.android.ide.common.layout.GridLayoutRule;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResourceWizard;
-import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResult;
-import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
-import com.android.resources.ResourceType;
-import com.android.utils.Pair;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.ListenerList;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ActionContributionItem;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.util.SafeRunnable;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.ISelectionProvider;
-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.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MenuDetectEvent;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.ui.IWorkbenchPartSite;
-import org.w3c.dom.Node;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-
-/**
- * The {@link SelectionManager} manages the selection in the canvas editor.
- * It holds (and can be asked about) the set of selected items, and it also has
- * operations for manipulating the selection - such as toggling items, copying
- * the selection to the clipboard, etc.
- * <p/>
- * This class implements {@link ISelectionProvider} so that it can delegate
- * the selection provider from the {@link LayoutCanvasViewer}.
- * <p/>
- * Note that {@link LayoutCanvasViewer} sets a selection change listener on this
- * manager so that it can invoke its own fireSelectionChanged when the canvas'
- * selection changes.
- */
-public class SelectionManager implements ISelectionProvider {
-
- private LayoutCanvas mCanvas;
-
- /** The current selection list. The list is never null, however it can be empty. */
- private final LinkedList<SelectionItem> mSelections = new LinkedList<SelectionItem>();
-
- /** An unmodifiable view of {@link #mSelections}. */
- private final List<SelectionItem> mUnmodifiableSelection =
- Collections.unmodifiableList(mSelections);
-
- /** Barrier set when updating the selection to prevent from recursively
- * invoking ourselves. */
- private boolean mInsideUpdateSelection;
-
- /**
- * The <em>current</em> alternate selection, if any, which changes when the Alt key is
- * used during a selection. Can be null.
- */
- private CanvasAlternateSelection mAltSelection;
-
- /** List of clients listening to selection changes. */
- private final ListenerList mSelectionListeners = new ListenerList();
-
- /**
- * Constructs a new {@link SelectionManager} associated with the given layout canvas.
- *
- * @param layoutCanvas The layout canvas to create a {@link SelectionManager} for.
- */
- public SelectionManager(LayoutCanvas layoutCanvas) {
- mCanvas = layoutCanvas;
- }
-
- @Override
- public void addSelectionChangedListener(ISelectionChangedListener listener) {
- mSelectionListeners.add(listener);
- }
-
- @Override
- public void removeSelectionChangedListener(ISelectionChangedListener listener) {
- mSelectionListeners.remove(listener);
- }
-
- /**
- * Returns the native {@link SelectionItem} list.
- *
- * @return An immutable list of {@link SelectionItem}. Can be empty but not null.
- */
- @NonNull
- List<SelectionItem> getSelections() {
- return mUnmodifiableSelection;
- }
-
- /**
- * Return a snapshot/copy of the selection. Useful for clipboards etc where we
- * don't want the returned copy to be affected by future edits to the selection.
- *
- * @return A copy of the current selection. Never null.
- */
- @NonNull
- public List<SelectionItem> getSnapshot() {
- if (mSelectionListeners.isEmpty()) {
- return Collections.emptyList();
- }
-
- return new ArrayList<SelectionItem>(mSelections);
- }
-
- /**
- * Returns a {@link TreeSelection} where each {@link TreePath} item is
- * actually a {@link CanvasViewInfo}.
- */
- @Override
- public ISelection getSelection() {
- if (mSelections.isEmpty()) {
- return TreeSelection.EMPTY;
- }
-
- ArrayList<TreePath> paths = new ArrayList<TreePath>();
-
- for (SelectionItem cs : mSelections) {
- CanvasViewInfo vi = cs.getViewInfo();
- if (vi != null) {
- paths.add(getTreePath(vi));
- }
- }
-
- return new TreeSelection(paths.toArray(new TreePath[paths.size()]));
- }
-
- /**
- * Create a {@link TreePath} from the given view info
- *
- * @param viewInfo the view info to look up a tree path for
- * @return a {@link TreePath} for the given view info
- */
- public static TreePath getTreePath(CanvasViewInfo viewInfo) {
- ArrayList<Object> segments = new ArrayList<Object>();
- while (viewInfo != null) {
- segments.add(0, viewInfo);
- viewInfo = viewInfo.getParent();
- }
-
- return new TreePath(segments.toArray());
- }
-
- /**
- * Sets the selection. It must be an {@link ITreeSelection} where each segment
- * of the tree path is a {@link CanvasViewInfo}. A null selection is considered
- * as an empty selection.
- * <p/>
- * This method is invoked by {@link LayoutCanvasViewer#setSelection(ISelection)}
- * in response to an <em>outside</em> selection (compatible with ours) that has
- * changed. Typically it means the outline selection has changed and we're
- * synchronizing ours to match.
- */
- @Override
- public void setSelection(ISelection selection) {
- if (mInsideUpdateSelection) {
- return;
- }
-
- boolean changed = false;
- try {
- mInsideUpdateSelection = true;
-
- if (selection == null) {
- selection = TreeSelection.EMPTY;
- }
-
- if (selection instanceof ITreeSelection) {
- ITreeSelection treeSel = (ITreeSelection) selection;
-
- if (treeSel.isEmpty()) {
- // Clear existing selection, if any
- if (!mSelections.isEmpty()) {
- mSelections.clear();
- mAltSelection = null;
- updateActionsFromSelection();
- redraw();
- }
- return;
- }
-
- boolean redoLayout = false;
-
- // Create a list of all currently selected view infos
- Set<CanvasViewInfo> oldSelected = new HashSet<CanvasViewInfo>();
- for (SelectionItem cs : mSelections) {
- oldSelected.add(cs.getViewInfo());
- }
-
- // Go thru new selection and take care of selecting new items
- // or marking those which are the same as in the current selection
- for (TreePath path : treeSel.getPaths()) {
- Object seg = path.getLastSegment();
- if (seg instanceof CanvasViewInfo) {
- CanvasViewInfo newVi = (CanvasViewInfo) seg;
- if (oldSelected.contains(newVi)) {
- // This view info is already selected. Remove it from the
- // oldSelected list so that we don't deselect it later.
- oldSelected.remove(newVi);
- } else {
- // This view info is not already selected. Select it now.
-
- // reset alternate selection if any
- mAltSelection = null;
- // otherwise add it.
- mSelections.add(createSelection(newVi));
- changed = true;
- }
- if (newVi.isInvisible()) {
- redoLayout = true;
- }
- } else {
- // Unrelated selection (e.g. user clicked in the Project Explorer
- // or something) -- just ignore these
- return;
- }
- }
-
- // Deselect old selected items that are not in the new one
- for (CanvasViewInfo vi : oldSelected) {
- if (vi.isExploded()) {
- redoLayout = true;
- }
- deselect(vi);
- changed = true;
- }
-
- if (redoLayout) {
- mCanvas.getEditorDelegate().recomputeLayout();
- }
- }
- } finally {
- mInsideUpdateSelection = false;
- }
-
- if (changed) {
- redraw();
- fireSelectionChanged();
- updateActionsFromSelection();
- }
- }
-
- /**
- * The menu has been activated; ensure that the menu click is over the existing
- * selection, and if not, update the selection.
- *
- * @param e the {@link MenuDetectEvent} which triggered the menu
- */
- public void menuClick(MenuDetectEvent e) {
- LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout();
-
- // Right click button is used to display a context menu.
- // If there's an existing selection and the click is anywhere in this selection
- // and there are no modifiers being used, we don't want to change the selection.
- // Otherwise we select the item under the cursor.
-
- for (SelectionItem cs : mSelections) {
- if (cs.isRoot()) {
- continue;
- }
- if (cs.getRect().contains(p.x, p.y)) {
- // The cursor is inside the selection. Don't change anything.
- return;
- }
- }
-
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
- selectSingle(vi);
- }
-
- /**
- * Performs selection for a mouse event.
- * <p/>
- * Shift key (or Command on the Mac) is used to toggle in multi-selection.
- * Alt key is used to cycle selection through objects at the same level than
- * the one pointed at (i.e. click on an object then alt-click to cycle).
- *
- * @param e The mouse event which triggered the selection. Cannot be null.
- * The modifier key mask will be used to determine whether this
- * is a plain select or a toggle, etc.
- */
- public void select(MouseEvent e) {
- boolean isMultiClick = (e.stateMask & SWT.SHIFT) != 0 ||
- // On Mac, the Command key is the normal toggle accelerator
- ((SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) &&
- (e.stateMask & SWT.COMMAND) != 0);
- boolean isCycleClick = (e.stateMask & SWT.ALT) != 0;
-
- LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout();
-
- if (e.button == 3) {
- // Right click button is used to display a context menu.
- // If there's an existing selection and the click is anywhere in this selection
- // and there are no modifiers being used, we don't want to change the selection.
- // Otherwise we select the item under the cursor.
-
- if (!isCycleClick && !isMultiClick) {
- for (SelectionItem cs : mSelections) {
- if (cs.getRect().contains(p.x, p.y)) {
- // The cursor is inside the selection. Don't change anything.
- return;
- }
- }
- }
-
- } else if (e.button != 1) {
- // Click was done with something else than the left button for normal selection
- // or the right button for context menu.
- // We don't use mouse button 2 yet (middle mouse, or scroll wheel?) for
- // anything, so let's not change the selection.
- return;
- }
-
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
-
- if (vi != null && vi.isHidden()) {
- vi = vi.getParent();
- }
-
- if (isMultiClick && !isCycleClick) {
- // Case where shift is pressed: pointed object is toggled.
-
- // reset alternate selection if any
- mAltSelection = null;
-
- // If nothing has been found at the cursor, assume it might be a user error
- // and avoid clearing the existing selection.
-
- if (vi != null) {
- // toggle this selection on-off: remove it if already selected
- if (deselect(vi)) {
- if (vi.isExploded()) {
- mCanvas.getEditorDelegate().recomputeLayout();
- }
-
- redraw();
- return;
- }
-
- // otherwise add it.
- mSelections.add(createSelection(vi));
- fireSelectionChanged();
- redraw();
- }
-
- } else if (isCycleClick) {
- // Case where alt is pressed: select or cycle the object pointed at.
-
- // Note: if shift and alt are pressed, shift is ignored. The alternate selection
- // mechanism does not reset the current multiple selection unless they intersect.
-
- // We need to remember the "origin" of the alternate selection, to be
- // able to continue cycling through it later. If there's no alternate selection,
- // create one. If there's one but not for the same origin object, create a new
- // one too.
- if (mAltSelection == null || mAltSelection.getOriginatingView() != vi) {
- mAltSelection = new CanvasAlternateSelection(
- vi, mCanvas.getViewHierarchy().findAltViewInfoAt(p));
-
- // deselect them all, in case they were partially selected
- deselectAll(mAltSelection.getAltViews());
-
- // select the current one
- CanvasViewInfo vi2 = mAltSelection.getCurrent();
- if (vi2 != null) {
- mSelections.addFirst(createSelection(vi2));
- fireSelectionChanged();
- }
- } else {
- // We're trying to cycle through the current alternate selection.
- // First remove the current object.
- CanvasViewInfo vi2 = mAltSelection.getCurrent();
- deselect(vi2);
-
- // Now select the next one.
- vi2 = mAltSelection.getNext();
- if (vi2 != null) {
- mSelections.addFirst(createSelection(vi2));
- fireSelectionChanged();
- }
- }
- redraw();
-
- } else {
- // Case where no modifier is pressed: either select or reset the selection.
- selectSingle(vi);
- }
- }
-
- /**
- * Removes all the currently selected item and only select the given item.
- * Issues a redraw() if the selection changes.
- *
- * @param vi The new selected item if non-null. Selection becomes empty if null.
- * @return the item selected, or null if the selection was cleared (e.g. vi was null)
- */
- @Nullable
- SelectionItem selectSingle(CanvasViewInfo vi) {
- SelectionItem item = null;
-
- // reset alternate selection if any
- mAltSelection = null;
-
- if (vi == null) {
- // The user clicked outside the bounds of the root element; in that case, just
- // select the root element.
- vi = mCanvas.getViewHierarchy().getRoot();
- }
-
- boolean redoLayout = hasExplodedItems();
-
- // reset (multi)selection if any
- if (!mSelections.isEmpty()) {
- if (mSelections.size() == 1 && mSelections.getFirst().getViewInfo() == vi) {
- // CanvasSelection remains the same, don't touch it.
- return mSelections.getFirst();
- }
- mSelections.clear();
- }
-
- if (vi != null) {
- item = createSelection(vi);
- mSelections.add(item);
- if (vi.isInvisible()) {
- redoLayout = true;
- }
- }
- fireSelectionChanged();
-
- if (redoLayout) {
- mCanvas.getEditorDelegate().recomputeLayout();
- }
-
- redraw();
-
- return item;
- }
-
- /** Returns true if the view hierarchy is showing exploded items. */
- private boolean hasExplodedItems() {
- for (SelectionItem item : mSelections) {
- if (item.getViewInfo().isExploded()) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Selects the given set of {@link CanvasViewInfo}s. This is similar to
- * {@link #selectSingle} but allows you to make a multi-selection. Issues a
- * {@link #redraw()}.
- *
- * @param viewInfos A collection of {@link CanvasViewInfo} objects to be
- * selected, or null or empty to clear the selection.
- */
- /* package */ void selectMultiple(Collection<CanvasViewInfo> viewInfos) {
- // reset alternate selection if any
- mAltSelection = null;
-
- boolean redoLayout = hasExplodedItems();
-
- mSelections.clear();
- if (viewInfos != null) {
- for (CanvasViewInfo viewInfo : viewInfos) {
- mSelections.add(createSelection(viewInfo));
- if (viewInfo.isInvisible()) {
- redoLayout = true;
- }
- }
- }
-
- fireSelectionChanged();
-
- if (redoLayout) {
- mCanvas.getEditorDelegate().recomputeLayout();
- }
-
- redraw();
- }
-
- public void select(Collection<INode> nodes) {
- List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>(nodes.size());
- for (INode node : nodes) {
- CanvasViewInfo info = mCanvas.getViewHierarchy().findViewInfoFor(node);
- if (info != null) {
- infos.add(info);
- }
- }
- selectMultiple(infos);
- }
-
- /**
- * Selects the visual element corresponding to the given XML node
- * @param xmlNode The Node whose element we want to select.
- */
- /* package */ void select(Node xmlNode) {
- if (xmlNode == null) {
- return;
- } else if (xmlNode.getNodeType() == Node.TEXT_NODE) {
- xmlNode = xmlNode.getParentNode();
- }
-
- CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoFor(xmlNode);
- if (vi != null && !vi.isRoot()) {
- selectSingle(vi);
- }
- }
-
- /**
- * Selects any views that overlap the given selection rectangle.
- *
- * @param topLeft The top left corner defining the selection rectangle.
- * @param bottomRight The bottom right corner defining the selection
- * rectangle.
- * @param toggled A set of {@link CanvasViewInfo}s that should be toggled
- * rather than just added.
- */
- public void selectWithin(LayoutPoint topLeft, LayoutPoint bottomRight,
- Collection<CanvasViewInfo> toggled) {
- // reset alternate selection if any
- mAltSelection = null;
-
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- Collection<CanvasViewInfo> viewInfos = viewHierarchy.findWithin(topLeft, bottomRight);
-
- if (toggled.size() > 0) {
- // Copy; we're not allowed to touch the passed in collection
- Set<CanvasViewInfo> result = new HashSet<CanvasViewInfo>(toggled);
- for (CanvasViewInfo viewInfo : viewInfos) {
- if (toggled.contains(viewInfo)) {
- result.remove(viewInfo);
- } else {
- result.add(viewInfo);
- }
- }
- viewInfos = result;
- }
-
- mSelections.clear();
- for (CanvasViewInfo viewInfo : viewInfos) {
- if (viewInfo.isHidden()) {
- continue;
- }
- mSelections.add(createSelection(viewInfo));
- }
-
- fireSelectionChanged();
- redraw();
- }
-
- /**
- * Clears the selection and then selects everything (all views and all their
- * children).
- */
- public void selectAll() {
- // First clear the current selection, if any.
- mSelections.clear();
- mAltSelection = null;
-
- // Now select everything if there's a valid layout
- for (CanvasViewInfo vi : mCanvas.getViewHierarchy().findAllViewInfos(false)) {
- mSelections.add(createSelection(vi));
- }
-
- fireSelectionChanged();
- redraw();
- }
-
- /** Clears the selection */
- public void selectNone() {
- mSelections.clear();
- mAltSelection = null;
- fireSelectionChanged();
- redraw();
- }
-
- /** Selects the parent of the current selection */
- public void selectParent() {
- if (mSelections.size() == 1) {
- CanvasViewInfo parent = mSelections.get(0).getViewInfo().getParent();
- if (parent != null) {
- selectSingle(parent);
- }
- }
- }
-
- /** Finds all widgets in the layout that have the same type as the primary */
- public void selectSameType() {
- // Find all
- if (mSelections.size() == 1) {
- CanvasViewInfo viewInfo = mSelections.get(0).getViewInfo();
- ElementDescriptor descriptor = viewInfo.getUiViewNode().getDescriptor();
- mSelections.clear();
- mAltSelection = null;
- addSameType(mCanvas.getViewHierarchy().getRoot(), descriptor);
- fireSelectionChanged();
- redraw();
- }
- }
-
- /** Helper for {@link #selectSameType} */
- private void addSameType(CanvasViewInfo root, ElementDescriptor descriptor) {
- if (root.getUiViewNode().getDescriptor() == descriptor) {
- mSelections.add(createSelection(root));
- }
-
- for (CanvasViewInfo child : root.getChildren()) {
- addSameType(child, descriptor);
- }
- }
-
- /** Selects the siblings of the primary */
- public void selectSiblings() {
- // Find all
- if (mSelections.size() == 1) {
- CanvasViewInfo vi = mSelections.get(0).getViewInfo();
- mSelections.clear();
- mAltSelection = null;
- CanvasViewInfo parent = vi.getParent();
- if (parent == null) {
- selectNone();
- } else {
- for (CanvasViewInfo child : parent.getChildren()) {
- mSelections.add(createSelection(child));
- }
- fireSelectionChanged();
- redraw();
- }
- }
- }
-
- /**
- * Returns true if and only if there is currently more than one selected
- * item.
- *
- * @return True if more than one item is selected
- */
- public boolean hasMultiSelection() {
- return mSelections.size() > 1;
- }
-
- /**
- * Deselects a view info. Returns true if the object was actually selected.
- * Callers are responsible for calling redraw() and updateOulineSelection()
- * after.
- * @param canvasViewInfo The item to deselect.
- * @return True if the object was successfully removed from the selection.
- */
- public boolean deselect(CanvasViewInfo canvasViewInfo) {
- if (canvasViewInfo == null) {
- return false;
- }
-
- for (ListIterator<SelectionItem> it = mSelections.listIterator(); it.hasNext(); ) {
- SelectionItem s = it.next();
- if (canvasViewInfo == s.getViewInfo()) {
- it.remove();
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Deselects multiple view infos.
- * Callers are responsible for calling redraw() and updateOulineSelection() after.
- */
- private void deselectAll(List<CanvasViewInfo> canvasViewInfos) {
- for (ListIterator<SelectionItem> it = mSelections.listIterator(); it.hasNext(); ) {
- SelectionItem s = it.next();
- if (canvasViewInfos.contains(s.getViewInfo())) {
- it.remove();
- }
- }
- }
-
- /** Sync the selection with an updated view info tree */
- void sync() {
- // Check if the selection is still the same (based on the object keys)
- // and eventually recompute their bounds.
- for (ListIterator<SelectionItem> it = mSelections.listIterator(); it.hasNext(); ) {
- SelectionItem s = it.next();
-
- // Check if the selected object still exists
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- UiViewElementNode key = s.getViewInfo().getUiViewNode();
- CanvasViewInfo vi = viewHierarchy.findViewInfoFor(key);
-
- // Remove the previous selection -- if the selected object still exists
- // we need to recompute its bounds in case it moved so we'll insert a new one
- // at the same place.
- it.remove();
- if (vi == null) {
- vi = findCorresponding(s.getViewInfo(), viewHierarchy.getRoot());
- }
- if (vi != null) {
- it.add(createSelection(vi));
- }
- }
- fireSelectionChanged();
-
- // remove the current alternate selection views
- mAltSelection = null;
- }
-
- /** Finds the corresponding {@link CanvasViewInfo} in the new hierarchy */
- private CanvasViewInfo findCorresponding(CanvasViewInfo old, CanvasViewInfo newRoot) {
- CanvasViewInfo oldParent = old.getParent();
- if (oldParent != null) {
- CanvasViewInfo newParent = findCorresponding(oldParent, newRoot);
- if (newParent == null) {
- return null;
- }
-
- List<CanvasViewInfo> oldSiblings = oldParent.getChildren();
- List<CanvasViewInfo> newSiblings = newParent.getChildren();
- Iterator<CanvasViewInfo> oldIterator = oldSiblings.iterator();
- Iterator<CanvasViewInfo> newIterator = newSiblings.iterator();
- while (oldIterator.hasNext() && newIterator.hasNext()) {
- CanvasViewInfo oldSibling = oldIterator.next();
- CanvasViewInfo newSibling = newIterator.next();
-
- if (oldSibling.getName().equals(newSibling.getName())) {
- // Structure has changed: can't do a proper search
- return null;
- }
-
- if (oldSibling == old) {
- return newSibling;
- }
- }
- } else {
- return newRoot;
- }
-
- return null;
- }
-
- /**
- * Notifies listeners that the selection has changed.
- */
- private void fireSelectionChanged() {
- if (mInsideUpdateSelection) {
- return;
- }
- try {
- mInsideUpdateSelection = true;
-
- final SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
-
- SafeRunnable.run(new SafeRunnable() {
- @Override
- public void run() {
- for (Object listener : mSelectionListeners.getListeners()) {
- ((ISelectionChangedListener) listener).selectionChanged(event);
- }
- }
- });
-
- updateActionsFromSelection();
- } finally {
- mInsideUpdateSelection = false;
- }
- }
-
- /**
- * Updates menu actions and the layout action bar after a selection change - these are
- * actions that depend on the selection
- */
- private void updateActionsFromSelection() {
- LayoutEditorDelegate editor = mCanvas.getEditorDelegate();
- if (editor != null) {
- // Update menu actions that depend on the selection
- mCanvas.updateMenuActionState();
-
- // Update the layout actions bar
- LayoutActionBar layoutActionBar = editor.getGraphicalEditor().getLayoutActionBar();
- layoutActionBar.updateSelection();
- }
- }
-
- /**
- * Sanitizes the selection for a copy/cut or drag operation.
- * <p/>
- * Sanitizes the list to make sure all elements have a valid XML attached to it,
- * that is remove element that have no XML to avoid having to make repeated such
- * checks in various places after.
- * <p/>
- * In case of multiple selection, we also need to remove all children when their
- * parent is already selected since parents will always be added with all their
- * children.
- * <p/>
- *
- * @param selection The selection list to be sanitized <b>in-place</b>.
- * The <code>selection</code> argument should not be {@link #mSelections} -- the
- * given list is going to be altered and we should never alter the user-made selection.
- * Instead the caller should provide its own copy.
- */
- /* package */ static void sanitize(List<SelectionItem> selection) {
- if (selection.isEmpty()) {
- return;
- }
-
- for (Iterator<SelectionItem> it = selection.iterator(); it.hasNext(); ) {
- SelectionItem cs = it.next();
- CanvasViewInfo vi = cs.getViewInfo();
- UiViewElementNode key = vi == null ? null : vi.getUiViewNode();
- Node node = key == null ? null : key.getXmlNode();
- if (node == null) {
- // Missing ViewInfo or view key or XML, discard this.
- it.remove();
- continue;
- }
-
- if (vi != null) {
- for (Iterator<SelectionItem> it2 = selection.iterator();
- it2.hasNext(); ) {
- SelectionItem cs2 = it2.next();
- if (cs != cs2) {
- CanvasViewInfo vi2 = cs2.getViewInfo();
- if (vi.isParent(vi2)) {
- // vi2 is a parent for vi. Remove vi.
- it.remove();
- break;
- }
- }
- }
- }
- }
- }
-
- /**
- * Selects the given list of nodes in the canvas, and returns true iff the
- * attempt to select was successful.
- *
- * @param nodes The collection of nodes to be selected
- * @param indices A list of indices within the parent for each node, or null
- * @return True if and only if all nodes were successfully selected
- */
- public boolean selectDropped(List<INode> nodes, List<Integer> indices) {
- assert indices == null || nodes.size() == indices.size();
-
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
-
- // Look up a list of view infos which correspond to the nodes.
- final Collection<CanvasViewInfo> newChildren = new ArrayList<CanvasViewInfo>();
- for (int i = 0, n = nodes.size(); i < n; i++) {
- INode node = nodes.get(i);
-
- CanvasViewInfo viewInfo = viewHierarchy.findViewInfoFor(node);
-
- // There are two scenarios where looking up a view info fails.
- // The first one is that the node was just added and the render has not yet
- // happened, so the ViewHierarchy has no record of the node. In this case
- // there is nothing we can do, and the method will return false (which the
- // caller will use to schedule a second attempt later).
- // The second scenario is where the nodes *change identity*. This isn't
- // common, but when a drop handler makes a lot of changes to its children,
- // for example when dropping into a GridLayout where attributes are adjusted
- // on nearly all the other children to update row or column attributes
- // etc, then in some cases Eclipse's DOM model changes the identities of
- // the nodes when applying all the edits, so the new Node we created (as
- // well as possibly other nodes) are no longer the children we observe
- // after the edit, and there are new copies there instead. In this case
- // the UiViewModel also fails to map the nodes. To work around this,
- // we track the *indices* (within the parent) during a drop, such that we
- // know which children (according to their positions) the given nodes
- // are supposed to map to, and then we use these view infos instead.
- if (viewInfo == null && node instanceof NodeProxy && indices != null) {
- INode parent = node.getParent();
- CanvasViewInfo parentViewInfo = viewHierarchy.findViewInfoFor(parent);
- if (parentViewInfo != null) {
- UiViewElementNode parentUiNode = parentViewInfo.getUiViewNode();
- if (parentUiNode != null) {
- List<UiElementNode> children = parentUiNode.getUiChildren();
- int index = indices.get(i);
- if (index >= 0 && index < children.size()) {
- UiElementNode replacedNode = children.get(index);
- viewInfo = viewHierarchy.findViewInfoFor(replacedNode);
- }
- }
- }
- }
-
- if (viewInfo != null) {
- if (nodes.size() > 1 && viewInfo.isHidden()) {
- // Skip spacers - unless you're dropping just one
- continue;
- }
- if (GridLayoutRule.sDebugGridLayout && (viewInfo.getName().equals(FQCN_SPACE)
- || viewInfo.getName().equals(FQCN_SPACE_V7))) {
- // In debug mode they might not be marked as hidden but we never never
- // want to select these guys
- continue;
- }
- newChildren.add(viewInfo);
- }
- }
- boolean found = nodes.size() == newChildren.size();
-
- if (found || newChildren.size() > 0) {
- mCanvas.getSelectionManager().selectMultiple(newChildren);
- }
-
- return found;
- }
-
- /**
- * Update the outline selection to select the given nodes, asynchronously.
- * @param nodes The nodes to be selected
- */
- public void setOutlineSelection(final List<INode> nodes) {
- Display.getDefault().asyncExec(new Runnable() {
- @Override
- public void run() {
- selectDropped(nodes, null /* indices */);
- syncOutlineSelection();
- }
- });
- }
-
- /**
- * Syncs the current selection to the outline, synchronously.
- */
- public void syncOutlineSelection() {
- OutlinePage outlinePage = mCanvas.getOutlinePage();
- IWorkbenchPartSite site = outlinePage.getEditor().getSite();
- ISelectionProvider selectionProvider = site.getSelectionProvider();
- ISelection selection = selectionProvider.getSelection();
- if (selection != null) {
- outlinePage.setSelection(selection);
- }
- }
-
- private void redraw() {
- mCanvas.redraw();
- }
-
- SelectionItem createSelection(CanvasViewInfo vi) {
- return new SelectionItem(mCanvas, vi);
- }
-
- /**
- * Returns true if there is nothing selected
- *
- * @return true if there is nothing selected
- */
- public boolean isEmpty() {
- return mSelections.size() == 0;
- }
-
- /**
- * "Select" context menu which lists various menu options related to selection:
- * <ul>
- * <li> Select All
- * <li> Select Parent
- * <li> Select None
- * <li> Select Siblings
- * <li> Select Same Type
- * </ul>
- * etc.
- */
- public static class SelectionMenu extends SubmenuAction {
- private final GraphicalEditorPart mEditor;
-
- public SelectionMenu(GraphicalEditorPart editor) {
- super("Select");
- mEditor = editor;
- }
-
- @Override
- public String getId() {
- return "-selectionmenu"; //$NON-NLS-1$
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- LayoutCanvas canvas = mEditor.getCanvasControl();
- SelectionManager selectionManager = canvas.getSelectionManager();
- List<SelectionItem> selections = selectionManager.getSelections();
- boolean selectedOne = selections.size() == 1;
- boolean notRoot = selectedOne && !selections.get(0).isRoot();
- boolean haveSelection = selections.size() > 0;
-
- Action a;
- a = selectionManager.new SelectAction("Select Parent\tEsc", SELECT_PARENT);
- new ActionContributionItem(a).fill(menu, -1);
- a.setEnabled(notRoot);
- a.setAccelerator(SWT.ESC);
-
- a = selectionManager.new SelectAction("Select Siblings", SELECT_SIBLINGS);
- new ActionContributionItem(a).fill(menu, -1);
- a.setEnabled(notRoot);
-
- a = selectionManager.new SelectAction("Select Same Type", SELECT_SAME_TYPE);
- new ActionContributionItem(a).fill(menu, -1);
- a.setEnabled(selectedOne);
-
- new Separator().fill(menu, -1);
-
- // Special case for Select All: Use global action
- a = canvas.getSelectAllAction();
- new ActionContributionItem(a).fill(menu, -1);
- a.setEnabled(true);
-
- a = selectionManager.new SelectAction("Deselect All", SELECT_NONE);
- new ActionContributionItem(a).fill(menu, -1);
- a.setEnabled(haveSelection);
- }
- }
-
- private static final int SELECT_PARENT = 1;
- private static final int SELECT_SIBLINGS = 2;
- private static final int SELECT_SAME_TYPE = 3;
- private static final int SELECT_NONE = 4; // SELECT_ALL is handled separately
-
- private class SelectAction extends Action {
- private final int mType;
-
- public SelectAction(String title, int type) {
- super(title, IAction.AS_PUSH_BUTTON);
- mType = type;
- }
-
- @Override
- public void run() {
- switch (mType) {
- case SELECT_NONE:
- selectNone();
- break;
- case SELECT_PARENT:
- selectParent();
- break;
- case SELECT_SAME_TYPE:
- selectSameType();
- break;
- case SELECT_SIBLINGS:
- selectSiblings();
- break;
- }
-
- List<INode> nodes = new ArrayList<INode>();
- for (SelectionItem item : getSelections()) {
- nodes.add(item.getNode());
- }
- setOutlineSelection(nodes);
- }
- }
-
- public Pair<SelectionItem, SelectionHandle> findHandle(ControlPoint controlPoint) {
- if (!isEmpty()) {
- LayoutPoint layoutPoint = controlPoint.toLayout();
- int distance = (int) ((PIXEL_MARGIN + PIXEL_RADIUS) / mCanvas.getScale());
-
- for (SelectionItem item : getSelections()) {
- SelectionHandles handles = item.getSelectionHandles();
- // See if it's over the selection handles
- SelectionHandle handle = handles.findHandle(layoutPoint, distance);
- if (handle != null) {
- return Pair.of(item, handle);
- }
- }
-
- }
- return null;
- }
-
- /** Performs the default action provided by the currently selected view */
- public void performDefaultAction() {
- final List<SelectionItem> selections = getSelections();
- if (selections.size() > 0) {
- NodeProxy primary = selections.get(0).getNode();
- if (primary != null) {
- RulesEngine rulesEngine = mCanvas.getRulesEngine();
- final String id = rulesEngine.callGetDefaultActionId(primary);
- if (id == null) {
- return;
- }
- final List<RuleAction> actions = rulesEngine.callGetContextMenu(primary);
- if (actions == null) {
- return;
- }
- RuleAction matching = null;
- for (RuleAction a : actions) {
- if (id.equals(a.getId())) {
- matching = a;
- break;
- }
- }
- if (matching == null) {
- return;
- }
- final List<INode> selectedNodes = new ArrayList<INode>();
- for (SelectionItem item : selections) {
- NodeProxy n = item.getNode();
- if (n != null) {
- selectedNodes.add(n);
- }
- }
- final RuleAction action = matching;
- mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(action.getTitle(),
- new Runnable() {
- @Override
- public void run() {
- action.getCallback().action(action, selectedNodes,
- action.getId(), null);
- LayoutCanvas canvas = mCanvas;
- CanvasViewInfo root = canvas.getViewHierarchy().getRoot();
- if (root != null) {
- UiViewElementNode uiViewNode = root.getUiViewNode();
- NodeFactory nodeFactory = canvas.getNodeFactory();
- NodeProxy rootNode = nodeFactory.create(uiViewNode);
- if (rootNode != null) {
- rootNode.applyPendingChanges();
- }
- }
- }
- });
- }
- }
- }
-
- /** Performs renaming the selected views */
- public void performRename() {
- final List<SelectionItem> selections = getSelections();
- if (selections.size() > 0) {
- NodeProxy primary = selections.get(0).getNode();
- if (primary != null) {
- performRename(primary, selections);
- }
- }
- }
-
- /**
- * Performs renaming the given node.
- *
- * @param primary the node to be renamed, or the primary node (to get the
- * current value from if more than one node should be renamed)
- * @param selections if not null, a list of nodes to apply the setting to
- * (which should include the primary)
- * @return the result of the renaming operation
- */
- @NonNull
- public RenameResult performRename(
- final @NonNull INode primary,
- final @Nullable List<SelectionItem> selections) {
- String id = primary.getStringAttr(ANDROID_URI, ATTR_ID);
- if (id != null && !id.isEmpty()) {
- RenameResult result = RenameResourceWizard.renameResource(
- mCanvas.getShell(),
- mCanvas.getEditorDelegate().getGraphicalEditor().getProject(),
- ResourceType.ID, BaseViewRule.stripIdPrefix(id), null, true /*canClear*/);
- if (result.isCanceled()) {
- return result;
- } else if (!result.isUnavailable()) {
- return result;
- }
- }
- String currentId = primary.getStringAttr(ANDROID_URI, ATTR_ID);
- currentId = BaseViewRule.stripIdPrefix(currentId);
- InputDialog d = new InputDialog(
- AdtPlugin.getDisplay().getActiveShell(),
- "Set ID",
- "New ID:",
- currentId,
- ResourceNameValidator.create(false, (IProject) null, ResourceType.ID));
- if (d.open() == Window.OK) {
- final String s = d.getValue();
- mCanvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel("Set ID",
- new Runnable() {
- @Override
- public void run() {
- String newId = s;
- newId = NEW_ID_PREFIX + BaseViewRule.stripIdPrefix(s);
- if (selections != null) {
- for (SelectionItem item : selections) {
- NodeProxy node = item.getNode();
- if (node != null) {
- node.setAttribute(ANDROID_URI, ATTR_ID, newId);
- }
- }
- } else {
- primary.setAttribute(ANDROID_URI, ATTR_ID, newId);
- }
-
- LayoutCanvas canvas = mCanvas;
- CanvasViewInfo root = canvas.getViewHierarchy().getRoot();
- if (root != null) {
- UiViewElementNode uiViewNode = root.getUiViewNode();
- NodeFactory nodeFactory = canvas.getNodeFactory();
- NodeProxy rootNode = nodeFactory.create(uiViewNode);
- if (rootNode != null) {
- rootNode.applyPendingChanges();
- }
- }
- }
- });
- return RenameResult.name(BaseViewRule.stripIdPrefix(s));
- } else {
- return RenameResult.canceled();
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java
deleted file mode 100644
index 97d048108..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java
+++ /dev/null
@@ -1,247 +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 com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Margins;
-import com.android.ide.common.api.Rect;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-
-import org.eclipse.swt.graphics.GC;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The {@link SelectionOverlay} paints the current selection as an overlay.
- */
-public class SelectionOverlay extends Overlay {
- private final LayoutCanvas mCanvas;
- private boolean mHidden;
-
- /**
- * Constructs a new {@link SelectionOverlay} tied to the given canvas.
- *
- * @param canvas the associated canvas
- */
- public SelectionOverlay(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- /**
- * Set whether the selection overlay should be hidden. This is done during some
- * gestures like resize where the new bounds could be confused with the current
- * selection bounds.
- *
- * @param hidden when true, hide the selection bounds, when false, unhide.
- */
- public void setHidden(boolean hidden) {
- mHidden = hidden;
- }
-
- /**
- * Paints the selection.
- *
- * @param selectionManager The {@link SelectionManager} holding the
- * selection.
- * @param gcWrapper The graphics context wrapper for the layout rules to use.
- * @param gc The SWT graphics object
- * @param rulesEngine The {@link RulesEngine} holding the rules.
- */
- public void paint(SelectionManager selectionManager, GCWrapper gcWrapper,
- GC gc, RulesEngine rulesEngine) {
- if (mHidden) {
- return;
- }
-
- List<SelectionItem> selections = selectionManager.getSelections();
- int n = selections.size();
- if (n > 0) {
- List<NodeProxy> selectedNodes = new ArrayList<NodeProxy>();
- boolean isMultipleSelection = n > 1;
- for (SelectionItem s : selections) {
- if (s.isRoot()) {
- // The root selection is never painted
- continue;
- }
-
- NodeProxy node = s.getNode();
- if (node != null) {
- paintSelection(gcWrapper, gc, s, isMultipleSelection);
- selectedNodes.add(node);
- }
- }
-
- if (selectedNodes.size() > 0) {
- paintSelectionFeedback(gcWrapper, selectedNodes, rulesEngine);
- } else {
- CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot();
- if (root != null) {
- NodeProxy parent = mCanvas.getNodeFactory().create(root);
- rulesEngine.callPaintSelectionFeedback(gcWrapper,
- parent, Collections.<INode>emptyList(), root.getViewObject());
- }
- }
-
- if (n == 1) {
- NodeProxy node = selections.get(0).getNode();
- if (node != null) {
- paintHints(gcWrapper, node, rulesEngine);
- }
- }
- } else {
- CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot();
- if (root != null) {
- NodeProxy parent = mCanvas.getNodeFactory().create(root);
- rulesEngine.callPaintSelectionFeedback(gcWrapper,
- parent, Collections.<INode>emptyList(), root.getViewObject());
- }
- }
- }
-
- /** Paint hint for current selection */
- private void paintHints(GCWrapper gcWrapper, NodeProxy node, RulesEngine rulesEngine) {
- INode parent = node.getParent();
- if (parent instanceof NodeProxy) {
- NodeProxy parentNode = (NodeProxy) parent;
- List<String> infos = rulesEngine.callGetSelectionHint(parentNode, node);
- if (infos != null && infos.size() > 0) {
- gcWrapper.useStyle(DrawingStyle.HELP);
-
- Rect b = mCanvas.getImageOverlay().getImageBounds();
- if (b == null) {
- return;
- }
-
- // Compute the location to display the help. This is done in
- // layout coordinates, so we need to apply the scale in reverse
- // when making pixel margins
- // TODO: We could take the Canvas dimensions into account to see
- // where there is more room.
- // TODO: The scrollbars should take the presence of hint text
- // into account.
- double scale = mCanvas.getScale();
- int x, y;
- if (b.w > b.h) {
- x = (int) (b.x + 3 / scale);
- y = (int) (b.y + b.h + 6 / scale);
- } else {
- x = (int) (b.x + b.w + 6 / scale);
- y = (int) (b.y + 3 / scale);
- }
- gcWrapper.drawBoxedStrings(x, y, infos);
- }
- }
- }
-
- private void paintSelectionFeedback(GCWrapper gcWrapper, List<NodeProxy> nodes,
- RulesEngine rulesEngine) {
- // Add fastpath for n=1
-
- // Group nodes into parent/child groups
- Set<INode> parents = new HashSet<INode>();
- for (INode node : nodes) {
- INode parent = node.getParent();
- if (/*parent == null || */parent instanceof NodeProxy) {
- NodeProxy parentNode = (NodeProxy) parent;
- parents.add(parentNode);
- }
- }
- ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
- for (INode parent : parents) {
- List<INode> children = new ArrayList<INode>();
- for (INode node : nodes) {
- INode nodeParent = node.getParent();
- if (nodeParent == parent) {
- children.add(node);
- }
- }
- CanvasViewInfo viewInfo = viewHierarchy.findViewInfoFor((NodeProxy) parent);
- Object view = viewInfo != null ? viewInfo.getViewObject() : null;
-
- rulesEngine.callPaintSelectionFeedback(gcWrapper,
- (NodeProxy) parent, children, view);
- }
- }
-
- /** Called by the canvas when a view is being selected. */
- private void paintSelection(IGraphics gc, GC swtGc, SelectionItem item,
- boolean isMultipleSelection) {
- CanvasViewInfo view = item.getViewInfo();
- if (view.isHidden()) {
- return;
- }
-
- NodeProxy selectedNode = item.getNode();
- Rect r = selectedNode.getBounds();
- if (!r.isValid()) {
- return;
- }
-
- gc.useStyle(DrawingStyle.SELECTION);
-
- Margins insets = mCanvas.getInsets(selectedNode.getFqcn());
- int x1 = r.x;
- int y1 = r.y;
- int x2 = r.x2() + 1;
- int y2 = r.y2() + 1;
-
- if (insets != null) {
- x1 += insets.left;
- x2 -= insets.right;
- y1 += insets.top;
- y2 -= insets.bottom;
- }
-
- gc.drawRect(x1, y1, x2, y2);
-
- // Paint sibling rectangles, if applicable
- List<CanvasViewInfo> siblings = view.getNodeSiblings();
- if (siblings != null) {
- for (CanvasViewInfo sibling : siblings) {
- if (sibling != view) {
- r = SwtUtils.toRect(sibling.getSelectionRect());
- gc.fillRect(r);
- gc.drawRect(r);
- }
- }
- }
-
- // Paint selection handles. These are painted in control coordinates on the
- // real SWT GC object rather than in layout coordinates on the GCWrapper,
- // since we want them to have a fixed size that is independent of the
- // screen zoom.
- CanvasTransform horizontalTransform = mCanvas.getHorizontalTransform();
- CanvasTransform verticalTransform = mCanvas.getVerticalTransform();
- int radius = SelectionHandle.PIXEL_RADIUS;
- int doubleRadius = 2 * radius;
- for (SelectionHandle handle : item.getSelectionHandles()) {
- int cx = horizontalTransform.translate(handle.centerX);
- int cy = verticalTransform.translate(handle.centerY);
-
- SwtDrawingStyle style = SwtDrawingStyle.of(DrawingStyle.SELECTION);
- gc.setAlpha(style.getStrokeAlpha());
- swtGc.fillRectangle(cx - radius, cy - radius, doubleRadius, doubleRadius);
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ShowWithinMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ShowWithinMenu.java
deleted file mode 100644
index d1d529e5a..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ShowWithinMenu.java
+++ /dev/null
@@ -1,82 +0,0 @@
-
-package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
-
-import org.eclipse.core.resources.IFile;
-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.Separator;
-import org.eclipse.swt.widgets.Menu;
-
-import java.util.List;
-
-/**
- * Action which creates a submenu for the "Show Included In" action
- */
-class ShowWithinMenu extends SubmenuAction {
- private LayoutEditorDelegate mEditorDelegate;
-
- ShowWithinMenu(LayoutEditorDelegate editorDelegate) {
- super("Show Included In");
- mEditorDelegate = editorDelegate;
- }
-
- @Override
- protected void addMenuItems(Menu menu) {
- GraphicalEditorPart graphicalEditor = mEditorDelegate.getGraphicalEditor();
- IFile file = graphicalEditor.getEditedFile();
- if (graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) {
- IProject project = file.getProject();
- IncludeFinder finder = IncludeFinder.get(project);
- final List<Reference> includedBy = finder.getIncludedBy(file);
-
- if (includedBy != null && includedBy.size() > 0) {
- for (final Reference reference : includedBy) {
- String title = reference.getDisplayName();
- IAction action = new ShowWithinAction(title, reference);
- new ActionContributionItem(action).fill(menu, -1);
- }
- new Separator().fill(menu, -1);
- }
- IAction action = new ShowWithinAction("Nothing", null);
- if (includedBy == null || includedBy.size() == 0) {
- action.setEnabled(false);
- }
- new ActionContributionItem(action).fill(menu, -1);
- } else {
- addDisabledMessageItem("Not supported on platform");
- }
- }
-
- /** Action to select one particular include-context */
- private class ShowWithinAction extends Action {
- private Reference mReference;
-
- public ShowWithinAction(String title, Reference reference) {
- super(title, IAction.AS_RADIO_BUTTON);
- mReference = reference;
- }
-
- @Override
- public boolean isChecked() {
- Reference within = mEditorDelegate.getGraphicalEditor().getIncludedWithin();
- if (within == null) {
- return mReference == null;
- } else {
- return within.equals(mReference);
- }
- }
-
- @Override
- public void run() {
- if (!isChecked()) {
- mEditorDelegate.getGraphicalEditor().showIn(mReference);
- }
- }
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java
deleted file mode 100644
index 198c16484..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttribute.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.annotations.NonNull;
-import com.android.ide.common.api.INode.IAttribute;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Represents one XML attribute in a {@link SimpleElement}.
- * <p/>
- * The attribute is always represented by a namespace URI, a name and a value.
- * The name cannot be empty.
- * The namespace URI can be empty for an attribute without a namespace but is never null.
- * The value can be empty but cannot be null.
- * <p/>
- * For a more detailed explanation of the purpose of this class,
- * please see {@link SimpleXmlTransfer}.
- */
-public class SimpleAttribute implements IAttribute {
- private final String mName;
- private final String mValue;
- private final String mUri;
-
- /**
- * Creates a new {@link SimpleAttribute}.
- * <p/>
- * Any null value will be converted to an empty non-null string.
- * However it is a semantic error to use an empty name -- no assertion is done though.
- *
- * @param uri The URI of the attribute.
- * @param name The XML local name of the attribute.
- * @param value The value of the attribute.
- */
- public SimpleAttribute(String uri, String name, String value) {
- mUri = uri == null ? "" : uri;
- mName = name == null ? "" : name;
- mValue = value == null ? "" : value;
- }
-
- /**
- * Returns the namespace URI of the attribute.
- * Can be empty for an attribute without a namespace but is never null.
- */
- @Override
- public @NonNull String getUri() {
- return mUri;
- }
-
- /** Returns the XML local name of the attribute. Cannot be null nor empty. */
- @Override
- public @NonNull String getName() {
- return mName;
- }
-
- /** Returns the value of the attribute. Cannot be null. Can be empty. */
- @Override
- public @NonNull String getValue() {
- return mValue;
- }
-
- // reader and writer methods
-
- @Override
- public String toString() {
- return String.format("@%s:%s=%s\n", //$NON-NLS-1$
- mName,
- mUri,
- mValue);
- }
-
- private static final Pattern REGEXP =
- Pattern.compile("[^@]*@([^:]+):([^=]*)=([^\n]*)\n*"); //$NON-NLS-1$
-
- static SimpleAttribute parseString(String value) {
- Matcher m = REGEXP.matcher(value);
- if (m.matches()) {
- return new SimpleAttribute(m.group(2), m.group(1), m.group(3));
- }
-
- return null;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof SimpleAttribute) {
- SimpleAttribute sa = (SimpleAttribute) obj;
-
- return mName.equals(sa.mName) &&
- mUri.equals(sa.mUri) &&
- mValue.equals(sa.mValue);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- long c = mName.hashCode();
- // uses the formula defined in java.util.List.hashCode()
- c = 31*c + mUri.hashCode();
- c = 31*c + mValue.hashCode();
- if (c > 0x0FFFFFFFFL) {
- // wrap any overflow
- c = c ^ (c >> 32);
- }
- return (int)(c & 0x0FFFFFFFFL);
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
deleted file mode 100644
index 9acc8c25e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.Rect;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents an XML element with a name, attributes and inner elements.
- * <p/>
- * The semantic of the element name is to be a fully qualified class name of a View to inflate.
- * The element name is not expected to have a name space.
- * <p/>
- * For a more detailed explanation of the purpose of this class,
- * please see {@link SimpleXmlTransfer}.
- */
-public class SimpleElement implements IDragElement {
-
- /** Version number of the internal serialized string format. */
- private static final String FORMAT_VERSION = "3";
-
- private final String mFqcn;
- private final String mParentFqcn;
- private final Rect mBounds;
- private final Rect mParentBounds;
- private final List<IDragAttribute> mAttributes = new ArrayList<IDragAttribute>();
- private final List<IDragElement> mElements = new ArrayList<IDragElement>();
-
- private IDragAttribute[] mCachedAttributes = null;
- private IDragElement[] mCachedElements = null;
- private SelectionItem mSelectionItem;
-
- /**
- * Creates a new {@link SimpleElement} with the specified element name.
- *
- * @param fqcn A fully qualified class name of a View to inflate, e.g.
- * "android.view.Button". Must not be null nor empty.
- * @param parentFqcn The fully qualified class name of the parent of this element.
- * Can be null but not empty.
- * @param bounds The canvas bounds of the originating canvas node of the element.
- * If null, a non-null invalid rectangle will be assigned.
- * @param parentBounds The canvas bounds of the parent of this element. Can be null.
- */
- public SimpleElement(String fqcn, String parentFqcn, Rect bounds, Rect parentBounds) {
- mFqcn = fqcn;
- mParentFqcn = parentFqcn;
- mBounds = bounds == null ? new Rect() : bounds.copy();
- mParentBounds = parentBounds == null ? new Rect() : parentBounds.copy();
- }
-
- /**
- * Returns the element name, which must match a fully qualified class name of
- * a View to inflate.
- */
- @Override
- public @NonNull String getFqcn() {
- return mFqcn;
- }
-
- /**
- * Returns the bounds of the element's node, if it originated from an existing
- * canvas. The rectangle is invalid and non-null when the element originated
- * from the object palette (unless it successfully rendered a preview)
- */
- @Override
- public @NonNull Rect getBounds() {
- return mBounds;
- }
-
- /**
- * Returns the fully qualified class name of the parent, if the element originated
- * from an existing canvas. Returns null if the element has no parent, such as a top
- * level element or an element originating from the object palette.
- */
- @Override
- public String getParentFqcn() {
- return mParentFqcn;
- }
-
- /**
- * Returns the bounds of the element's parent, absolute for the canvas, or null if there
- * is no suitable parent. This is null when {@link #getParentFqcn()} is null.
- */
- @Override
- public @NonNull Rect getParentBounds() {
- return mParentBounds;
- }
-
- @Override
- public @NonNull IDragAttribute[] getAttributes() {
- if (mCachedAttributes == null) {
- mCachedAttributes = mAttributes.toArray(new IDragAttribute[mAttributes.size()]);
- }
- return mCachedAttributes;
- }
-
- @Override
- public IDragAttribute getAttribute(@Nullable String uri, @NonNull String localName) {
- for (IDragAttribute attr : mAttributes) {
- if (attr.getUri().equals(uri) && attr.getName().equals(localName)) {
- return attr;
- }
- }
-
- return null;
- }
-
- @Override
- public @NonNull IDragElement[] getInnerElements() {
- if (mCachedElements == null) {
- mCachedElements = mElements.toArray(new IDragElement[mElements.size()]);
- }
- return mCachedElements;
- }
-
- public void addAttribute(SimpleAttribute attr) {
- mCachedAttributes = null;
- mAttributes.add(attr);
- }
-
- public void addInnerElement(SimpleElement e) {
- mCachedElements = null;
- mElements.add(e);
- }
-
- @Override
- public boolean isSame(@NonNull INode node) {
- if (mSelectionItem != null) {
- return node == mSelectionItem.getNode();
- } else {
- return node.getBounds().equals(mBounds);
- }
- }
-
- void setSelectionItem(@Nullable SelectionItem selectionItem) {
- mSelectionItem = selectionItem;
- }
-
- @Nullable
- SelectionItem getSelectionItem() {
- return mSelectionItem;
- }
-
- @Nullable
- static SimpleElement findPrimary(SimpleElement[] elements, SelectionItem primary) {
- if (elements == null || elements.length == 0) {
- return null;
- }
-
- if (elements.length == 1 || primary == null) {
- return elements[0];
- }
-
- for (SimpleElement element : elements) {
- if (element.getSelectionItem() == primary) {
- return element;
- }
- }
-
- return elements[0];
- }
-
- // reader and writer methods
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("{V=").append(FORMAT_VERSION);
- sb.append(",N=").append(mFqcn);
- if (mParentFqcn != null) {
- sb.append(",P=").append(mParentFqcn);
- }
- if (mBounds != null && mBounds.isValid()) {
- sb.append(String.format(",R=%d %d %d %d", mBounds.x, mBounds.y, mBounds.w, mBounds.h));
- }
- if (mParentBounds != null && mParentBounds.isValid()) {
- sb.append(String.format(",Q=%d %d %d %d",
- mParentBounds.x, mParentBounds.y, mParentBounds.w, mParentBounds.h));
- }
- sb.append('\n');
- for (IDragAttribute a : mAttributes) {
- sb.append(a.toString());
- }
- for (IDragElement e : mElements) {
- sb.append(e.toString());
- }
- sb.append("}\n"); //$NON-NLS-1$
- return sb.toString();
- }
-
- /** Parses a string containing one or more elements. */
- static SimpleElement[] parseString(String value) {
- ArrayList<SimpleElement> elements = new ArrayList<SimpleElement>();
- String[] lines = value.split("\n");
- int[] index = new int[] { 0 };
- SimpleElement element = null;
- while ((element = parseLines(lines, index)) != null) {
- elements.add(element);
- }
- return elements.toArray(new SimpleElement[elements.size()]);
- }
-
- /**
- * Parses one element from the input lines array, starting at the inOutIndex
- * and updating the inOutIndex to match the next unread line on output.
- */
- private static SimpleElement parseLines(String[] lines, int[] inOutIndex) {
- SimpleElement e = null;
- int index = inOutIndex[0];
- while (index < lines.length) {
- String line = lines[index++];
- String s = line.trim();
- if (s.startsWith("{")) { //$NON-NLS-1$
- if (e == null) {
- // This is the element's header, it should have
- // the format "key=value,key=value,..."
- String version = null;
- String fqcn = null;
- String parent = null;
- Rect bounds = null;
- Rect pbounds = null;
-
- for (String s2 : s.substring(1).split(",")) { //$NON-NLS-1$
- int pos = s2.indexOf('=');
- if (pos <= 0 || pos == s2.length() - 1) {
- continue;
- }
- String key = s2.substring(0, pos).trim();
- String value = s2.substring(pos + 1).trim();
-
- if (key.equals("V")) { //$NON-NLS-1$
- version = value;
- if (!value.equals(FORMAT_VERSION)) {
- // Wrong format version. Don't even try to process anything
- // else and just give up everything.
- inOutIndex[0] = index;
- return null;
- }
-
- } else if (key.equals("N")) { //$NON-NLS-1$
- fqcn = value;
-
- } else if (key.equals("P")) { //$NON-NLS-1$
- parent = value;
-
- } else if (key.equals("R") || key.equals("Q")) { //$NON-NLS-1$ //$NON-NLS-2$
- // Parse the canvas bounds
- String[] sb = value.split(" +"); //$NON-NLS-1$
- if (sb != null && sb.length == 4) {
- Rect r = null;
- try {
- r = new Rect();
- r.x = Integer.parseInt(sb[0]);
- r.y = Integer.parseInt(sb[1]);
- r.w = Integer.parseInt(sb[2]);
- r.h = Integer.parseInt(sb[3]);
-
- if (key.equals("R")) {
- bounds = r;
- } else {
- pbounds = r;
- }
- } catch (NumberFormatException ignore) {
- }
- }
- }
- }
-
- // We need at least a valid name to recreate an element
- if (version != null && fqcn != null && fqcn.length() > 0) {
- e = new SimpleElement(fqcn, parent, bounds, pbounds);
- }
- } else {
- // This is an inner element... need to parse the { line again.
- inOutIndex[0] = index - 1;
- SimpleElement e2 = SimpleElement.parseLines(lines, inOutIndex);
- if (e2 != null) {
- e.addInnerElement(e2);
- }
- index = inOutIndex[0];
- }
-
- } else if (e != null && s.startsWith("@")) { //$NON-NLS-1$
- SimpleAttribute a = SimpleAttribute.parseString(line);
- if (a != null) {
- e.addAttribute(a);
- }
-
- } else if (e != null && s.startsWith("}")) { //$NON-NLS-1$
- // We're done with this element
- inOutIndex[0] = index;
- return e;
- }
- }
- inOutIndex[0] = index;
- return null;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof SimpleElement) {
- SimpleElement se = (SimpleElement) obj;
-
- // Bounds and parentFqcn must be null on both sides or equal.
- if ((mBounds == null && se.mBounds != null) ||
- (mBounds != null && !mBounds.equals(se.mBounds))) {
- return false;
- }
- if ((mParentFqcn == null && se.mParentFqcn != null) ||
- (mParentFqcn != null && !mParentFqcn.equals(se.mParentFqcn))) {
- return false;
- }
- if ((mParentBounds == null && se.mParentBounds != null) ||
- (mParentBounds != null && !mParentBounds.equals(se.mParentBounds))) {
- return false;
- }
-
- return mFqcn.equals(se.mFqcn) &&
- mAttributes.size() == se.mAttributes.size() &&
- mElements.size() == se.mElements.size() &&
- mAttributes.equals(se.mAttributes) &&
- mElements.equals(se.mElements);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- long c = mFqcn.hashCode();
- // uses the formula defined in java.util.List.hashCode()
- c = 31*c + mAttributes.hashCode();
- c = 31*c + mElements.hashCode();
- if (mParentFqcn != null) {
- c = 31*c + mParentFqcn.hashCode();
- }
- if (mBounds != null && mBounds.isValid()) {
- c = 31*c + mBounds.hashCode();
- }
- if (mParentBounds != null && mParentBounds.isValid()) {
- c = 31*c + mParentBounds.hashCode();
- }
-
- if (c > 0x0FFFFFFFFL) {
- // wrap any overflow
- c = c ^ (c >> 32);
- }
- return (int)(c & 0x0FFFFFFFFL);
- }
-}
-
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleXmlTransfer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleXmlTransfer.java
deleted file mode 100644
index 20ac2033e..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleXmlTransfer.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2009 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 com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
-
-import org.eclipse.swt.dnd.ByteArrayTransfer;
-import org.eclipse.swt.dnd.TransferData;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * A d'n'd {@link Transfer} class that can transfer a <em>simplified</em> XML fragment
- * to transfer elements and their attributes between {@link LayoutCanvas}.
- * <p/>
- * The implementation is based on the {@link ByteArrayTransfer} and what we transfer
- * is text with the following fixed format:
- * <p/>
- * <pre>
- * {element-name element-property ...
- * attrib_name="attrib_value"
- * attrib2="..."
- * {...inner elements...
- * }
- * }
- * {...next element...
- * }
- *
- * </pre>
- * The format has nothing to do with XML per se, except for the fact that the
- * transfered content represents XML elements and XML attributes.
- *
- * <p/>
- * The detailed syntax is:
- * <pre>
- * - ELEMENT := {NAME PROPERTY*\nATTRIB_LINE*ELEMENT*}\n
- * - PROPERTY := $[A-Z]=[^ ]*
- * - NAME := [^\n=]+
- * - ATTRIB_LINE := @URI:NAME=[^\n]*\n
- * </pre>
- *
- * Elements are represented by {@link SimpleElement}s and their attributes by
- * {@link SimpleAttribute}s, all of which have very specific properties that are
- * specifically limited to our needs for drag'n'drop.
- */
-final class SimpleXmlTransfer extends ByteArrayTransfer {
-
- // Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
-
- private static final String TYPE_NAME = "android.ADT.simple.xml.transfer.1"; //$NON-NLS-1$
- private static final int TYPE_ID = registerType(TYPE_NAME);
- private static final SimpleXmlTransfer sInstance = new SimpleXmlTransfer();
-
- /** Private constructor. Use {@link #getInstance()} to retrieve the singleton instance. */
- private SimpleXmlTransfer() {
- // pass
- }
-
- /** Returns the singleton instance. */
- public static SimpleXmlTransfer getInstance() {
- return sInstance;
- }
-
- /**
- * Helper method that returns the FQCN transfered for the given {@link ElementDescriptor}.
- * <p/>
- * If the descriptor is a {@link ViewElementDescriptor}, the transfered data is the FQCN
- * of the Android View class represented (e.g. "android.widget.Button").
- * For any other non-null descriptor, the XML name is used.
- * Otherwise it is null.
- *
- * @param desc The {@link ElementDescriptor} to transfer.
- * @return The FQCN, XML name or null.
- */
- public static String getFqcn(ElementDescriptor desc) {
- if (desc instanceof ViewElementDescriptor) {
- return ((ViewElementDescriptor) desc).getFullClassName();
- } else if (desc != null) {
- return desc.getXmlName();
- }
-
- return null;
- }
-
- @Override
- protected int[] getTypeIds() {
- return new int[] { TYPE_ID };
- }
-
- @Override
- protected String[] getTypeNames() {
- return new String[] { TYPE_NAME };
- }
-
- /** Transforms a array of {@link SimpleElement} into a native data transfer. */
- @Override
- protected void javaToNative(Object object, TransferData transferData) {
- if (object == null || !(object instanceof SimpleElement[])) {
- return;
- }
-
- if (isSupportedType(transferData)) {
- StringBuilder sb = new StringBuilder();
- for (SimpleElement e : (SimpleElement[]) object) {
- sb.append(e.toString());
- }
- String data = sb.toString();
-
- try {
- byte[] buf = data.getBytes("UTF-8"); //$NON-NLS-1$
- super.javaToNative(buf, transferData);
- } catch (UnsupportedEncodingException e) {
- // unlikely; ignore
- }
- }
- }
-
- /**
- * Recreates an array of {@link SimpleElement} from a native data transfer.
- *
- * @return An array of {@link SimpleElement} or null. The array may be empty.
- */
- @Override
- protected Object nativeToJava(TransferData transferData) {
- if (isSupportedType(transferData)) {
- byte[] buf = (byte[]) super.nativeToJava(transferData);
- if (buf != null && buf.length > 0) {
- try {
- String s = new String(buf, "UTF-8"); //$NON-NLS-1$
- return SimpleElement.parseString(s);
- } catch (UnsupportedEncodingException e) {
- // unlikely to happen, but still possible
- }
- }
- }
-
- return null;
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SubmenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SubmenuAction.java
deleted file mode 100644
index 0923dda79..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SubmenuAction.java
+++ /dev/null
@@ -1,75 +0,0 @@
-
-package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ActionContributionItem;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.IMenuCreator;
-import org.eclipse.swt.events.MenuEvent;
-import org.eclipse.swt.events.MenuListener;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.MenuItem;
-
-/**
- * Action which creates a submenu that is dynamically populated by subclasses
- */
-public abstract class SubmenuAction extends Action implements MenuListener, IMenuCreator {
- private Menu mMenu;
-
- public SubmenuAction(String title) {
- super(title, IAction.AS_DROP_DOWN_MENU);
- }
-
- @Override
- public IMenuCreator getMenuCreator() {
- return this;
- }
-
- @Override
- public void dispose() {
- if (mMenu != null) {
- mMenu.dispose();
- mMenu = null;
- }
- }
-
- @Override
- public Menu getMenu(Control parent) {
- return null;
- }
-
- @Override
- public Menu getMenu(Menu parent) {
- mMenu = new Menu(parent);
- mMenu.addMenuListener(this);
- return mMenu;
- }
-
- @Override
- public void menuHidden(MenuEvent e) {
- }
-
- protected abstract void addMenuItems(Menu menu);
-
- @Override
- public void menuShown(MenuEvent e) {
- // TODO: Replace this stuff with manager.setRemoveAllWhenShown(true);
- MenuItem[] menuItems = mMenu.getItems();
- for (int i = 0; i < menuItems.length; i++) {
- menuItems[i].dispose();
- }
- addMenuItems(mMenu);
- }
-
- protected void addDisabledMessageItem(String message) {
- IAction action = new Action(message, IAction.AS_PUSH_BUTTON) {
- @Override
- public void run() {
- }
- };
- action.setEnabled(false);
- new ActionContributionItem(action).fill(mMenu, -1);
-
- }
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java
deleted file mode 100644
index 93a33283c..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java
+++ /dev/null
@@ -1,319 +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 com.android.ide.common.api.DrawingStyle;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.RGB;
-
-/**
- * Description of the drawing styles with specific color, line style and alpha
- * definitions. This class corresponds to the more generic {@link DrawingStyle}
- * class which defines the drawing styles but does not introduce any specific
- * SWT values to the API clients.
- * <p>
- * TODO: This class should eventually be replaced by a scheme where the color
- * constants are instead coming from the theme.
- */
-public enum SwtDrawingStyle {
- /**
- * The style definition corresponding to {@link DrawingStyle#SELECTION}
- */
- SELECTION(new RGB(0x00, 0x99, 0xFF), 192, new RGB(0x00, 0x99, 0xFF), 192, 1, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#GUIDELINE}
- */
- GUIDELINE(new RGB(0x00, 0xAA, 0x00), 192, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#GUIDELINE}
- */
- GUIDELINE_SHADOW(new RGB(0x00, 0xAA, 0x00), 192, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#GUIDELINE_DASHED}
- */
- GUIDELINE_DASHED(new RGB(0x00, 0xAA, 0x00), 192, SWT.LINE_CUSTOM),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DISTANCE}
- */
- DISTANCE(new RGB(0xFF, 0x00, 0x00), 192 - 32, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#GRID}
- */
- GRID(new RGB(0xAA, 0xAA, 0xAA), 128, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#HOVER}
- */
- HOVER(null, 0, new RGB(0xFF, 0xFF, 0xFF), 40, 1, SWT.LINE_DOT),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#HOVER}
- */
- HOVER_SELECTION(null, 0, new RGB(0xFF, 0xFF, 0xFF), 10, 1, SWT.LINE_DOT),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#ANCHOR}
- */
- ANCHOR(new RGB(0x00, 0x99, 0xFF), 96, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#OUTLINE}
- */
- OUTLINE(new RGB(0x88, 0xFF, 0x88), 160, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DROP_RECIPIENT}
- */
- DROP_RECIPIENT(new RGB(0xFF, 0x99, 0x00), 255, new RGB(0xFF, 0x99, 0x00), 160, 2,
- SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DROP_ZONE}
- */
- DROP_ZONE(new RGB(0x00, 0xAA, 0x00), 220, new RGB(0x55, 0xAA, 0x00), 200, 1, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to
- * {@link DrawingStyle#DROP_ZONE_ACTIVE}
- */
- DROP_ZONE_ACTIVE(new RGB(0x00, 0xAA, 0x00), 220, new RGB(0x00, 0xAA, 0x00), 128, 2,
- SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DROP_PREVIEW}
- */
- DROP_PREVIEW(new RGB(0xFF, 0x99, 0x00), 255, null, 0, 2, SWT.LINE_CUSTOM),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#RESIZE_PREVIEW}
- */
- RESIZE_PREVIEW(new RGB(0xFF, 0x99, 0x00), 255, null, 0, 2, SWT.LINE_SOLID),
-
- /**
- * The style used to show a proposed resize bound which is being rejected (for example,
- * because there is no near edge to attach to in a RelativeLayout).
- */
- RESIZE_FAIL(new RGB(0xFF, 0x99, 0x00), 255, null, 0, 2, SWT.LINE_CUSTOM),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#HELP}
- */
- HELP(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0x00, 0x00, 0x00), 128, 1, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#INVALID}
- */
- INVALID(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0xFF, 0x00, 0x00), 64, 2, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DEPENDENCY}
- */
- DEPENDENCY(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0xFF, 0xFF, 0x00), 24, 2, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#CYCLE}
- */
- CYCLE(new RGB(0xFF, 0x00, 0x00), 192, null, 0, 1, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#DRAGGED}
- */
- DRAGGED(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0x00, 0xFF, 0x00), 16, 2, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#EMPTY}
- */
- EMPTY(new RGB(0xFF, 0xFF, 0x55), 255, new RGB(0xFF, 0xFF, 0x55), 255, 1, SWT.LINE_DASH),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#CUSTOM1}
- */
- CUSTOM1(new RGB(0xFF, 0x00, 0xFF), 255, null, 0, 1, SWT.LINE_SOLID),
-
- /**
- * The style definition corresponding to {@link DrawingStyle#CUSTOM2}
- */
- CUSTOM2(new RGB(0x00, 0xFF, 0xFF), 255, null, 0, 1, SWT.LINE_DOT);
-
- /**
- * Construct a new style value with the given foreground, background, width,
- * linestyle and transparency.
- *
- * @param stroke A color descriptor for the foreground color, or null if no
- * foreground color should be set
- * @param fill A color descriptor for the background color, or null if no
- * foreground color should be set
- * @param lineWidth The line width, in pixels, or 0 if no line width should
- * be set
- * @param lineStyle The SWT line style - such as {@link SWT#LINE_SOLID}.
- * @param strokeAlpha The alpha value of the stroke, an integer in the range 0 to 255
- * where 0 is fully transparent and 255 is fully opaque.
- * @param fillAlpha The alpha value of the fill, an integer in the range 0 to 255
- * where 0 is fully transparent and 255 is fully opaque.
- */
- private SwtDrawingStyle(RGB stroke, int strokeAlpha, RGB fill, int fillAlpha, int lineWidth,
- int lineStyle) {
- mStroke = stroke;
- mFill = fill;
- mLineWidth = lineWidth;
- mLineStyle = lineStyle;
- mStrokeAlpha = strokeAlpha;
- mFillAlpha = fillAlpha;
- }
-
- /**
- * Convenience constructor for typical drawing styles, which do not specify
- * a fill and use a standard thickness line
- *
- * @param stroke Stroke color to be used (e.g. for the border/foreground)
- * @param strokeAlpha Transparency to use for the stroke; 0 is transparent
- * and 255 is fully opaque.
- * @param lineStyle The SWT line style - such as {@link SWT#LINE_SOLID}.
- */
- private SwtDrawingStyle(RGB stroke, int strokeAlpha, int lineStyle) {
- this(stroke, strokeAlpha, null, 255, 1, lineStyle);
- }
-
- /**
- * Return the stroke/foreground/border RGB color description to be used for
- * this style, or null if none
- */
- public RGB getStrokeColor() {
- return mStroke;
- }
-
- /**
- * Return the fill/background/interior RGB color description to be used for
- * this style, or null if none
- */
- public RGB getFillColor() {
- return mFill;
- }
-
- /** Return the line width to be used for this style */
- public int getLineWidth() {
- return mLineWidth;
- }
-
- /** Return the SWT line style to be used for this style */
- public int getLineStyle() {
- return mLineStyle;
- }
-
- /**
- * Return the stroke alpha value (in the range 0,255) to be used for this
- * style
- */
- public int getStrokeAlpha() {
- return mStrokeAlpha;
- }
-
- /**
- * Return the fill alpha value (in the range 0,255) to be used for this
- * style
- */
- public int getFillAlpha() {
- return mFillAlpha;
- }
-
- /**
- * Return the corresponding SwtDrawingStyle for the given
- * {@link DrawingStyle}
- * @param style The style to convert from a {@link DrawingStyle} to a {@link SwtDrawingStyle}.
- * @return A corresponding {@link SwtDrawingStyle}.
- */
- public static SwtDrawingStyle of(DrawingStyle style) {
- switch (style) {
- case SELECTION:
- return SELECTION;
- case GUIDELINE:
- return GUIDELINE;
- case GUIDELINE_SHADOW:
- return GUIDELINE_SHADOW;
- case GUIDELINE_DASHED:
- return GUIDELINE_DASHED;
- case DISTANCE:
- return DISTANCE;
- case GRID:
- return GRID;
- case HOVER:
- return HOVER;
- case HOVER_SELECTION:
- return HOVER_SELECTION;
- case ANCHOR:
- return ANCHOR;
- case OUTLINE:
- return OUTLINE;
- case DROP_ZONE:
- return DROP_ZONE;
- case DROP_ZONE_ACTIVE:
- return DROP_ZONE_ACTIVE;
- case DROP_RECIPIENT:
- return DROP_RECIPIENT;
- case DROP_PREVIEW:
- return DROP_PREVIEW;
- case RESIZE_PREVIEW:
- return RESIZE_PREVIEW;
- case RESIZE_FAIL:
- return RESIZE_FAIL;
- case HELP:
- return HELP;
- case INVALID:
- return INVALID;
- case DEPENDENCY:
- return DEPENDENCY;
- case CYCLE:
- return CYCLE;
- case DRAGGED:
- return DRAGGED;
- case EMPTY:
- return EMPTY;
- case CUSTOM1:
- return CUSTOM1;
- case CUSTOM2:
- return CUSTOM2;
-
- // Internal error
- default:
- throw new IllegalArgumentException("Unknown style " + style);
- }
- }
-
- /** RGB description of the stroke/foreground/border color */
- private final RGB mStroke;
-
- /** RGB description of the fill/foreground/interior color */
- private final RGB mFill;
-
- /** Pixel thickness of the stroke/border */
- private final int mLineWidth;
-
- /** SWT line style of the border/stroke */
- private final int mLineStyle;
-
- /** Alpha (in the range 0-255) of the stroke/border */
- private final int mStrokeAlpha;
-
- /** Alpha (in the range 0-255) of the fill/interior */
- private final int mFillAlpha;
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
deleted file mode 100644
index 64e91bedf..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java
+++ /dev/null
@@ -1,457 +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.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE;
-
-import com.android.ide.common.api.Rect;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-
-import org.eclipse.swt.SWTException;
-import org.eclipse.swt.graphics.Device;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.FontMetrics;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.PaletteData;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-
-import java.awt.Graphics;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.DataBufferInt;
-import java.awt.image.WritableRaster;
-import java.util.List;
-
-/**
- * Various generic SWT utilities such as image conversion.
- */
-public class SwtUtils {
-
- private SwtUtils() {
- }
-
- /**
- * Returns the {@link PaletteData} describing the ARGB ordering expected from integers
- * representing pixels for AWT {@link BufferedImage}.
- *
- * @param imageType the {@link BufferedImage#getType()} type
- * @return A new {@link PaletteData} suitable for AWT images.
- */
- public static PaletteData getAwtPaletteData(int imageType) {
- switch (imageType) {
- case BufferedImage.TYPE_INT_RGB:
- case BufferedImage.TYPE_INT_ARGB:
- case BufferedImage.TYPE_INT_ARGB_PRE:
- return new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF);
-
- case BufferedImage.TYPE_3BYTE_BGR:
- case BufferedImage.TYPE_4BYTE_ABGR:
- case BufferedImage.TYPE_4BYTE_ABGR_PRE:
- return new PaletteData(0x000000FF, 0x0000FF00, 0x00FF0000);
-
- default:
- throw new UnsupportedOperationException("RGB type not supported yet.");
- }
- }
-
- /**
- * Returns true if the given type of {@link BufferedImage} is supported for
- * conversion. For unsupported formats, use
- * {@link #convertToCompatibleFormat(BufferedImage)} first.
- *
- * @param imageType the {@link BufferedImage#getType()}
- * @return true if we can convert the given buffered image format
- */
- private static boolean isSupportedPaletteType(int imageType) {
- switch (imageType) {
- case BufferedImage.TYPE_INT_RGB:
- case BufferedImage.TYPE_INT_ARGB:
- case BufferedImage.TYPE_INT_ARGB_PRE:
- case BufferedImage.TYPE_3BYTE_BGR:
- case BufferedImage.TYPE_4BYTE_ABGR:
- case BufferedImage.TYPE_4BYTE_ABGR_PRE:
- return true;
- default:
- return false;
- }
- }
-
- /** Converts the given arbitrary {@link BufferedImage} to another {@link BufferedImage}
- * in a format that is supported (see {@link #isSupportedPaletteType(int)})
- *
- * @param image the image to be converted
- * @return a new image that is in a guaranteed compatible format
- */
- private static BufferedImage convertToCompatibleFormat(BufferedImage image) {
- BufferedImage converted = new BufferedImage(image.getWidth(), image.getHeight(),
- BufferedImage.TYPE_INT_ARGB);
- Graphics graphics = converted.getGraphics();
- graphics.drawImage(image, 0, 0, null);
- graphics.dispose();
-
- return converted;
- }
-
- /**
- * Converts an AWT {@link BufferedImage} into an equivalent SWT {@link Image}. Whether
- * the transparency data is transferred is optional, and this method can also apply an
- * alpha adjustment during the conversion.
- * <p/>
- * Implementation details: on Windows, the returned {@link Image} will have an ordering
- * matching the Windows DIB (e.g. RGBA, not ARGB). Callers must make sure to use
- * <code>Image.getImageData().paletteData</code> to get the right pixels out of the image.
- *
- * @param display The display where the SWT image will be shown
- * @param awtImage The AWT {@link BufferedImage}
- * @param transferAlpha If true, copy alpha data out of the source image
- * @param globalAlpha If -1, do nothing, otherwise adjust the alpha of the final image
- * by the given amount in the range [0,255]
- * @return A new SWT {@link Image} with the same contents as the source
- * {@link BufferedImage}
- */
- public static Image convertToSwt(Device display, BufferedImage awtImage,
- boolean transferAlpha, int globalAlpha) {
- if (!isSupportedPaletteType(awtImage.getType())) {
- awtImage = convertToCompatibleFormat(awtImage);
- }
-
- int width = awtImage.getWidth();
- int height = awtImage.getHeight();
-
- WritableRaster raster = awtImage.getRaster();
- DataBuffer dataBuffer = raster.getDataBuffer();
- ImageData imageData =
- new ImageData(width, height, 32, getAwtPaletteData(awtImage.getType()));
-
- if (dataBuffer instanceof DataBufferInt) {
- int[] imageDataBuffer = ((DataBufferInt) dataBuffer).getData();
- imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
- } else if (dataBuffer instanceof DataBufferByte) {
- byte[] imageDataBuffer = ((DataBufferByte) dataBuffer).getData();
- try {
- imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
- } catch (SWTException se) {
- // Unsupported depth
- return convertToSwt(display, convertToCompatibleFormat(awtImage),
- transferAlpha, globalAlpha);
- }
- }
-
- if (transferAlpha) {
- byte[] alphaData = new byte[height * width];
- for (int y = 0; y < height; y++) {
- byte[] alphaRow = new byte[width];
- for (int x = 0; x < width; x++) {
- int alpha = awtImage.getRGB(x, y) >>> 24;
-
- // We have to multiply in the alpha now since if we
- // set ImageData.alpha, it will ignore the alphaData.
- if (globalAlpha != -1) {
- alpha = alpha * globalAlpha >> 8;
- }
-
- alphaRow[x] = (byte) alpha;
- }
- System.arraycopy(alphaRow, 0, alphaData, y * width, width);
- }
-
- imageData.alphaData = alphaData;
- } else if (globalAlpha != -1) {
- imageData.alpha = globalAlpha;
- }
-
- return new Image(display, imageData);
- }
-
- /**
- * Converts a direct-color model SWT image to an equivalent AWT image. If the image
- * does not have a supported color model, returns null. This method does <b>NOT</b>
- * preserve alpha in the source image.
- *
- * @param swtImage the SWT image to be converted to AWT
- * @return an AWT image representing the source SWT image
- */
- public static BufferedImage convertToAwt(Image swtImage) {
- ImageData swtData = swtImage.getImageData();
- BufferedImage awtImage =
- new BufferedImage(swtData.width, swtData.height, BufferedImage.TYPE_INT_ARGB);
- PaletteData swtPalette = swtData.palette;
- if (swtPalette.isDirect) {
- PaletteData awtPalette = getAwtPaletteData(awtImage.getType());
-
- if (swtPalette.equals(awtPalette)) {
- // No color conversion needed.
- for (int y = 0; y < swtData.height; y++) {
- for (int x = 0; x < swtData.width; x++) {
- int pixel = swtData.getPixel(x, y);
- awtImage.setRGB(x, y, 0xFF000000 | pixel);
- }
- }
- } else {
- // We need to remap the colors
- int sr = -awtPalette.redShift + swtPalette.redShift;
- int sg = -awtPalette.greenShift + swtPalette.greenShift;
- int sb = -awtPalette.blueShift + swtPalette.blueShift;
-
- for (int y = 0; y < swtData.height; y++) {
- for (int x = 0; x < swtData.width; x++) {
- int pixel = swtData.getPixel(x, y);
-
- int r = pixel & swtPalette.redMask;
- int g = pixel & swtPalette.greenMask;
- int b = pixel & swtPalette.blueMask;
- r = (sr < 0) ? r >>> -sr : r << sr;
- g = (sg < 0) ? g >>> -sg : g << sg;
- b = (sb < 0) ? b >>> -sb : b << sb;
-
- pixel = 0xFF000000 | r | g | b;
- awtImage.setRGB(x, y, pixel);
- }
- }
- }
- } else {
- return null;
- }
-
- return awtImage;
- }
-
- /**
- * Creates a new image from a source image where the contents from a given set of
- * bounding boxes are copied into the new image and the rest is left transparent. A
- * scale can be applied to make the resulting image larger or smaller than the source
- * image. Note that the alpha channel in the original image is ignored, and the alpha
- * values for the painted rectangles will be set to a specific value passed into this
- * function.
- *
- * @param image the source image
- * @param rectangles the set of rectangles (bounding boxes) to copy from the source
- * image
- * @param boundingBox the bounding rectangle of the rectangle list, which can be
- * computed by {@link ImageUtils#getBoundingRectangle}
- * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the
- * destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by
- * doubling the image size
- * @param alpha the alpha (in the range 0-255) that painted bits should be set to
- * @return a pair of the rendered cropped image, and the location within the source
- * image that the crop begins (multiplied by the scale). May return null if
- * there are no selected items.
- */
- public static Image drawRectangles(Image image,
- List<Rectangle> rectangles, Rectangle boundingBox, double scale, byte alpha) {
-
- if (rectangles.size() == 0 || boundingBox == null || boundingBox.isEmpty()) {
- return null;
- }
-
- ImageData srcData = image.getImageData();
- int destWidth = (int) (scale * boundingBox.width);
- int destHeight = (int) (scale * boundingBox.height);
-
- ImageData destData = new ImageData(destWidth, destHeight, srcData.depth, srcData.palette);
- byte[] alphaData = new byte[destHeight * destWidth];
- destData.alphaData = alphaData;
-
- for (Rectangle bounds : rectangles) {
- int dx1 = bounds.x - boundingBox.x;
- int dy1 = bounds.y - boundingBox.y;
- int dx2 = dx1 + bounds.width;
- int dy2 = dy1 + bounds.height;
-
- dx1 *= scale;
- dy1 *= scale;
- dx2 *= scale;
- dy2 *= scale;
-
- int sx1 = bounds.x;
- int sy1 = bounds.y;
- int sx2 = sx1 + bounds.width;
- int sy2 = sy1 + bounds.height;
-
- if (scale == 1.0d) {
- for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy++) {
- for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx++) {
- destData.setPixel(dx, dy, srcData.getPixel(sx, sy));
- alphaData[dy * destWidth + dx] = alpha;
- }
- }
- } else {
- // Scaled copy.
- int sxDelta = sx2 - sx1;
- int dxDelta = dx2 - dx1;
- int syDelta = sy2 - sy1;
- int dyDelta = dy2 - dy1;
- for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy = (dy - dy1) * syDelta / dyDelta
- + sy1) {
- for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx = (dx - dx1) * sxDelta
- / dxDelta + sx1) {
- assert sx < sx2 && sy < sy2;
- destData.setPixel(dx, dy, srcData.getPixel(sx, sy));
- alphaData[dy * destWidth + dx] = alpha;
- }
- }
- }
- }
-
- return new Image(image.getDevice(), destData);
- }
-
- /**
- * Creates a new empty/blank image of the given size
- *
- * @param display the display to associate the image with
- * @param width the width of the image
- * @param height the height of the image
- * @return a new blank image of the given size
- */
- public static Image createEmptyImage(Display display, int width, int height) {
- BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- return SwtUtils.convertToSwt(display, image, false, 0);
- }
-
- /**
- * Converts the given SWT {@link Rectangle} into an ADT {@link Rect}
- *
- * @param swtRect the SWT {@link Rectangle}
- * @return an equivalent {@link Rect}
- */
- public static Rect toRect(Rectangle swtRect) {
- return new Rect(swtRect.x, swtRect.y, swtRect.width, swtRect.height);
- }
-
- /**
- * Sets the values of the given ADT {@link Rect} to the values of the given SWT
- * {@link Rectangle}
- *
- * @param target the ADT {@link Rect} to modify
- * @param source the SWT {@link Rectangle} to read values from
- */
- public static void set(Rect target, Rectangle source) {
- target.set(source.x, source.y, source.width, source.height);
- }
-
- /**
- * Compares an ADT {@link Rect} with an SWT {@link Rectangle} and returns true if they
- * are equivalent
- *
- * @param r1 the ADT {@link Rect}
- * @param r2 the SWT {@link Rectangle}
- * @return true if the two rectangles are equivalent
- */
- public static boolean equals(Rect r1, Rectangle r2) {
- return r1.x == r2.x && r1.y == r2.y && r1.w == r2.width && r1.h == r2.height;
-
- }
-
- /**
- * Get the average width of the font used by the given control
- *
- * @param display the display associated with the font usage
- * @param font the font to look up the average character width for
- * @return the average width, in pixels, of the given font
- */
- public static final int getAverageCharWidth(Display display, Font font) {
- GC gc = new GC(display);
- gc.setFont(font);
- FontMetrics fontMetrics = gc.getFontMetrics();
- int width = fontMetrics.getAverageCharWidth();
- gc.dispose();
- return width;
- }
-
- /**
- * Get the average width of the given font
- *
- * @param control the control to look up the default font for
- * @return the average width, in pixels, of the current font in the control
- */
- public static final int getAverageCharWidth(Control control) {
- GC gc = new GC(control.getDisplay());
- int width = gc.getFontMetrics().getAverageCharWidth();
- gc.dispose();
- return width;
- }
-
- /**
- * Draws a drop shadow for the given rectangle into the given context. It
- * will not draw anything if the rectangle is smaller than a minimum
- * determined by the assets used to draw the shadow graphics.
- * <p>
- * This corresponds to {@link ImageUtils#drawRectangleShadow(Graphics, int, int, int, int)},
- * but applied directly to an SWT graphics context instead, such that no image conversion
- * has to be performed.
- * <p>
- * Make sure to keep changes in the visual appearance here in sync with the
- * AWT version in {@link ImageUtils#drawRectangleShadow(Graphics, int, int, int, int)}.
- *
- * @param gc the graphics context to draw into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static final void drawRectangleShadow(GC gc, int x, int y, int width, int height) {
- if (sShadowBottomLeft == null) {
- IconFactory icons = IconFactory.getInstance();
- // See ImageUtils.drawRectangleShadow for an explanation of the assets.
- sShadowBottomLeft = icons.getIcon("shadow-bl"); //$NON-NLS-1$
- sShadowBottom = icons.getIcon("shadow-b"); //$NON-NLS-1$
- sShadowBottomRight = icons.getIcon("shadow-br"); //$NON-NLS-1$
- sShadowRight = icons.getIcon("shadow-r"); //$NON-NLS-1$
- sShadowTopRight = icons.getIcon("shadow-tr"); //$NON-NLS-1$
- assert sShadowBottomRight.getImageData().width == SHADOW_SIZE;
- assert sShadowBottomRight.getImageData().height == SHADOW_SIZE;
- }
-
- ImageData bottomLeftData = sShadowBottomLeft.getImageData();
- ImageData topRightData = sShadowTopRight.getImageData();
- ImageData bottomData = sShadowBottom.getImageData();
- ImageData rightData = sShadowRight.getImageData();
- int blWidth = bottomLeftData.width;
- int trHeight = topRightData.height;
- if (width < blWidth) {
- return;
- }
- if (height < trHeight) {
- return;
- }
-
- gc.drawImage(sShadowBottomLeft, x, y + height);
- gc.drawImage(sShadowBottomRight, x + width, y + height);
- gc.drawImage(sShadowTopRight, x + width, y);
- gc.drawImage(sShadowBottom,
- 0, 0,
- bottomData.width, bottomData.height,
- x + bottomLeftData.width, y + height,
- width - bottomLeftData.width, bottomData.height);
- gc.drawImage(sShadowRight,
- 0, 0,
- rightData.width, rightData.height,
- x + width, y + topRightData.height,
- rightData.width, height - topRightData.height);
- }
-
- private static Image sShadowBottomLeft;
- private static Image sShadowBottom;
- private static Image sShadowBottomRight;
- private static Image sShadowRight;
- private static Image sShadowTopRight;
-}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
deleted file mode 100644
index d247e28d7..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ViewHierarchy.java
+++ /dev/null
@@ -1,771 +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.ATTR_ID;
-import static com.android.SdkConstants.VIEW_MERGE;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.utils.Pair;
-
-import org.eclipse.swt.graphics.Rectangle;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.RandomAccess;
-import java.util.Set;
-
-/**
- * The view hierarchy class manages a set of view info objects and performs find
- * operations on this set.
- */
-public class ViewHierarchy {
- private static final boolean DUMP_INFO = false;
-
- private LayoutCanvas mCanvas;
-
- /**
- * Constructs a new {@link ViewHierarchy} tied to the given
- * {@link LayoutCanvas}.
- *
- * @param canvas The {@link LayoutCanvas} to create a {@link ViewHierarchy}
- * for.
- */
- public ViewHierarchy(LayoutCanvas canvas) {
- mCanvas = canvas;
- }
-
- /**
- * The CanvasViewInfo root created by the last call to {@link #setSession}
- * with a valid layout.
- * <p/>
- * This <em>can</em> be null to indicate we're dealing with an empty document with
- * no root node. Null here does not mean the result was invalid, merely that the XML
- * had no content to display -- we need to treat an empty document as valid so that
- * we can drop new items in it.
- */
- private CanvasViewInfo mLastValidViewInfoRoot;
-
- /**
- * True when the last {@link #setSession} provided a valid {@link LayoutScene}.
- * <p/>
- * When false this means the canvas is displaying an out-dated result image & bounds and some
- * features should be disabled accordingly such a drag'n'drop.
- * <p/>
- * Note that an empty document (with a null {@link #mLastValidViewInfoRoot}) is considered
- * valid since it is an acceptable drop target.
- */
- private boolean mIsResultValid;
-
- /**
- * A list of invisible parents (see {@link CanvasViewInfo#isInvisible()} for
- * details) in the current view hierarchy.
- */
- private final List<CanvasViewInfo> mInvisibleParents = new ArrayList<CanvasViewInfo>();
-
- /**
- * A read-only view of {@link #mInvisibleParents}; note that this is NOT a copy so it
- * reflects updates to the underlying {@link #mInvisibleParents} list.
- */
- private final List<CanvasViewInfo> mInvisibleParentsReadOnly =
- Collections.unmodifiableList(mInvisibleParents);
-
- /**
- * Flag which records whether or not we have any exploded parent nodes in this
- * view hierarchy. This is used to track whether or not we need to recompute the
- * layout when we exit show-all-invisible-parents mode (see
- * {@link LayoutCanvas#showInvisibleViews}).
- */
- private boolean mExplodedParents;
-
- /**
- * Bounds of included views in the current view hierarchy when rendered in other context
- */
- private List<Rectangle> mIncludedBounds;
-
- /** The render session for the current view hierarchy */
- private RenderSession mSession;
-
- /** Map from nodes to canvas view infos */
- private Map<UiViewElementNode, CanvasViewInfo> mNodeToView = Collections.emptyMap();
-
- /** Map from DOM nodes to canvas view infos */
- private Map<Node, CanvasViewInfo> mDomNodeToView = Collections.emptyMap();
-
- /**
- * Disposes the view hierarchy content.
- */
- public void dispose() {
- if (mSession != null) {
- mSession.dispose();
- mSession = null;
- }
- }
-
-
- /**
- * Sets the result of the layout rendering. The result object indicates if the layout
- * rendering succeeded. If it did, it contains a bitmap and the objects rectangles.
- *
- * Implementation detail: the bridge's computeLayout() method already returns a newly
- * allocated ILayourResult. That means we can keep this result and hold on to it
- * when it is valid.
- *
- * @param session The new session, either valid or not.
- * @param explodedNodes The set of individual nodes the layout computer was asked to
- * explode. Note that these are independent of the explode-all mode where
- * all views are exploded; this is used only for the mode (
- * {@link LayoutCanvas#showInvisibleViews}) where individual invisible
- * nodes are padded during certain interactions.
- */
- /* package */ void setSession(RenderSession session, Set<UiElementNode> explodedNodes,
- boolean layoutlib5) {
- // replace the previous scene, so the previous scene must be disposed.
- if (mSession != null) {
- mSession.dispose();
- }
-
- mSession = session;
- mIsResultValid = (session != null && session.getResult().isSuccess());
- mExplodedParents = false;
- mNodeToView = new HashMap<UiViewElementNode, CanvasViewInfo>(50);
- if (mIsResultValid && session != null) {
- List<ViewInfo> rootList = session.getRootViews();
-
- Pair<CanvasViewInfo,List<Rectangle>> infos = null;
-
- if (rootList == null || rootList.size() == 0) {
- // Special case: Look to see if this is really an empty <merge> view,
- // which shows up without any ViewInfos in the merge. In that case we
- // want to manufacture an empty view, such that we can target the view
- // via drag & drop, etc.
- if (hasMergeRoot()) {
- ViewInfo mergeRoot = createMergeInfo(session);
- infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
- } else {
- infos = null;
- }
- } else {
- if (rootList.size() > 1 && hasMergeRoot()) {
- ViewInfo mergeRoot = createMergeInfo(session);
- mergeRoot.setChildren(rootList);
- infos = CanvasViewInfo.create(mergeRoot, layoutlib5);
- } else {
- ViewInfo root = rootList.get(0);
-
- if (root != null) {
- infos = CanvasViewInfo.create(root, layoutlib5);
- if (DUMP_INFO) {
- dump(session, root, 0);
- }
- } else {
- infos = null;
- }
- }
- }
- if (infos != null) {
- mLastValidViewInfoRoot = infos.getFirst();
- mIncludedBounds = infos.getSecond();
-
- if (mLastValidViewInfoRoot.getUiViewNode() == null &&
- mLastValidViewInfoRoot.getChildren().isEmpty()) {
- GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor();
- if (editor.getIncludedWithin() != null) {
- // Somehow, this view was supposed to be rendered within another
- // view, yet this view was rendered as part of the other view.
- // In that case, abort attempting to show included in; clear the
- // include context and trigger a standalone re-render.
- editor.showIn(null);
- return;
- }
- }
-
- } else {
- mLastValidViewInfoRoot = null;
- mIncludedBounds = null;
- }
-
- updateNodeProxies(mLastValidViewInfoRoot);
-
- // Update the data structures related to tracking invisible and exploded nodes.
- // We need to find the {@link CanvasViewInfo} objects that correspond to
- // the passed in {@link UiElementNode} keys that were re-rendered, and mark
- // them as exploded and store them in a list for rendering.
- mExplodedParents = false;
- mInvisibleParents.clear();
- addInvisibleParents(mLastValidViewInfoRoot, explodedNodes);
-
- mDomNodeToView = new HashMap<Node, CanvasViewInfo>(mNodeToView.size());
- for (Map.Entry<UiViewElementNode, CanvasViewInfo> entry : mNodeToView.entrySet()) {
- mDomNodeToView.put(entry.getKey().getXmlNode(), entry.getValue());
- }
-
- // Update the selection
- mCanvas.getSelectionManager().sync();
- } else {
- mIncludedBounds = null;
- mInvisibleParents.clear();
- mDomNodeToView = Collections.emptyMap();
- }
- }
-
- private ViewInfo createMergeInfo(RenderSession session) {
- BufferedImage image = session.getImage();
- ControlPoint imageSize = ControlPoint.create(mCanvas,
- mCanvas.getHorizontalTransform().getMargin() + image.getWidth(),
- mCanvas.getVerticalTransform().getMargin() + image.getHeight());
- LayoutPoint layoutSize = imageSize.toLayout();
- UiDocumentNode model = mCanvas.getEditorDelegate().getUiRootNode();
- List<UiElementNode> children = model.getUiChildren();
- return new ViewInfo(VIEW_MERGE, children.get(0), 0, 0, layoutSize.x, layoutSize.y);
- }
-
- /**
- * Returns true if this view hierarchy corresponds to an editor that has a {@code
- * <merge>} tag at the root
- *
- * @return true if there is a {@code <merge>} at the root of this editor's document
- */
- private boolean hasMergeRoot() {
- UiDocumentNode model = mCanvas.getEditorDelegate().getUiRootNode();
- if (model != null) {
- List<UiElementNode> children = model.getUiChildren();
- if (children != null && children.size() > 0
- && VIEW_MERGE.equals(children.get(0).getDescriptor().getXmlName())) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Creates or updates the node proxy for this canvas view info.
- * <p/>
- * Since proxies are reused, this will update the bounds of an existing proxy when the
- * canvas is refreshed and a view changes position or size.
- * <p/>
- * This is a recursive call that updates the whole hierarchy starting at the given
- * view info.
- */
- private void updateNodeProxies(CanvasViewInfo vi) {
- if (vi == null) {
- return;
- }
-
- UiViewElementNode key = vi.getUiViewNode();
-
- if (key != null) {
- mCanvas.getNodeFactory().create(vi);
- mNodeToView.put(key, vi);
- }
-
- for (CanvasViewInfo child : vi.getChildren()) {
- updateNodeProxies(child);
- }
- }
-
- /**
- * Make a pass over the view hierarchy and look for two things:
- * <ol>
- * <li>Invisible parents. These are nodes that can hold children and have empty
- * bounds. These are then added to the {@link #mInvisibleParents} list.
- * <li>Exploded nodes. These are nodes that were previously marked as invisible, and
- * subsequently rendered by a recomputed layout. They now no longer have empty bounds,
- * but should be specially marked via {@link CanvasViewInfo#setExploded} such that we
- * for example in selection operations can determine if we need to recompute the
- * layout.
- * </ol>
- *
- * @param vi
- * @param invisibleNodes
- */
- private void addInvisibleParents(CanvasViewInfo vi, Set<UiElementNode> invisibleNodes) {
- if (vi == null) {
- return;
- }
-
- if (vi.isInvisible()) {
- mInvisibleParents.add(vi);
- } else if (invisibleNodes != null) {
- UiViewElementNode key = vi.getUiViewNode();
-
- if (key != null && invisibleNodes.contains(key)) {
- vi.setExploded(true);
- mExplodedParents = true;
- mInvisibleParents.add(vi);
- }
- }
-
- for (CanvasViewInfo child : vi.getChildren()) {
- addInvisibleParents(child, invisibleNodes);
- }
- }
-
- /**
- * Returns the current {@link RenderSession}.
- * @return the session or null if none have been set.
- */
- public RenderSession getSession() {
- return mSession;
- }
-
- /**
- * Returns true when the last {@link #setSession} provided a valid
- * {@link RenderSession}.
- * <p/>
- * When false this means the canvas is displaying an out-dated result image & bounds and some
- * features should be disabled accordingly such a drag'n'drop.
- * <p/>
- * Note that an empty document (with a null {@link #getRoot()}) is considered
- * valid since it is an acceptable drop target.
- * @return True when this {@link ViewHierarchy} contains a valid hierarchy of views.
- */
- public boolean isValid() {
- return mIsResultValid;
- }
-
- /**
- * Returns true if the last valid content of the canvas represents an empty document.
- * @return True if the last valid content of the canvas represents an empty document.
- */
- public boolean isEmpty() {
- return mLastValidViewInfoRoot == null;
- }
-
- /**
- * Returns true if we have parents in this hierarchy that are invisible (e.g. because
- * they have no children and zero layout bounds).
- *
- * @return True if we have invisible parents.
- */
- public boolean hasInvisibleParents() {
- return mInvisibleParents.size() > 0;
- }
-
- /**
- * Returns true if we have views that were exploded during rendering
- * @return True if we have exploded parents
- */
- public boolean hasExplodedParents() {
- return mExplodedParents;
- }
-
- /** Locates and return any views that overlap the given selection rectangle.
- * @param topLeft The top left corner of the selection rectangle.
- * @param bottomRight The bottom right corner of the selection rectangle.
- * @return A collection of {@link CanvasViewInfo} objects that overlap the
- * rectangle.
- */
- public Collection<CanvasViewInfo> findWithin(
- LayoutPoint topLeft,
- LayoutPoint bottomRight) {
- Rectangle selectionRectangle = new Rectangle(topLeft.x, topLeft.y, bottomRight.x
- - topLeft.x, bottomRight.y - topLeft.y);
- List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>();
- addWithin(mLastValidViewInfoRoot, selectionRectangle, infos);
- return infos;
- }
-
- /**
- * Recursive internal version of {@link #findViewInfoAt(int, int)}. Please don't use directly.
- * <p/>
- * Tries to find the inner most child matching the given x,y coordinates in the view
- * info sub-tree. This uses the potentially-expanded selection bounds.
- *
- * Returns null if not found.
- */
- private void addWithin(
- CanvasViewInfo canvasViewInfo,
- Rectangle canvasRectangle,
- List<CanvasViewInfo> infos) {
- if (canvasViewInfo == null) {
- return;
- }
- Rectangle r = canvasViewInfo.getSelectionRect();
- if (canvasRectangle.intersects(r)) {
-
- // try to find a matching child first
- for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
- addWithin(child, canvasRectangle, infos);
- }
-
- if (canvasViewInfo != mLastValidViewInfoRoot) {
- infos.add(canvasViewInfo);
- }
- }
- }
-
- /**
- * Locates and returns the {@link CanvasViewInfo} corresponding to the given
- * node, or null if it cannot be found.
- *
- * @param node The node we want to find a corresponding
- * {@link CanvasViewInfo} for.
- * @return The {@link CanvasViewInfo} corresponding to the given node, or
- * null if no match was found.
- */
- @Nullable
- public CanvasViewInfo findViewInfoFor(@Nullable Node node) {
- CanvasViewInfo vi = mDomNodeToView.get(node);
-
- if (vi == null) {
- if (node == null) {
- return null;
- } else if (node.getNodeType() == Node.TEXT_NODE) {
- return mDomNodeToView.get(node.getParentNode());
- } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
- return mDomNodeToView.get(((Attr) node).getOwnerElement());
- } else if (node.getNodeType() == Node.DOCUMENT_NODE) {
- return mDomNodeToView.get(((Document) node).getDocumentElement());
- }
- }
-
- return vi;
- }
-
- /**
- * Tries to find the inner most child matching the given x,y coordinates in
- * the view info sub-tree, starting at the last know view info root. This
- * uses the potentially-expanded selection bounds.
- * <p/>
- * Returns null if not found or if there's no view info root.
- *
- * @param p The point at which to look for the deepest match in the view
- * hierarchy
- * @return A {@link CanvasViewInfo} that intersects the given point, or null
- * if nothing was found.
- */
- public CanvasViewInfo findViewInfoAt(LayoutPoint p) {
- if (mLastValidViewInfoRoot == null) {
- return null;
- }
-
- return findViewInfoAt_Recursive(p, mLastValidViewInfoRoot);
- }
-
- /**
- * Recursive internal version of {@link #findViewInfoAt(int, int)}. Please don't use directly.
- * <p/>
- * Tries to find the inner most child matching the given x,y coordinates in the view
- * info sub-tree. This uses the potentially-expanded selection bounds.
- *
- * Returns null if not found.
- */
- private CanvasViewInfo findViewInfoAt_Recursive(LayoutPoint p, CanvasViewInfo canvasViewInfo) {
- if (canvasViewInfo == null) {
- return null;
- }
- Rectangle r = canvasViewInfo.getSelectionRect();
- if (r.contains(p.x, p.y)) {
-
- // try to find a matching child first
- // Iterate in REVERSE z order such that siblings on top
- // are checked before earlier siblings (this matters in layouts like
- // FrameLayout and in <merge> contexts where the views are sitting on top
- // of each other and we want to select the same view as the one drawn
- // on top of the others
- List<CanvasViewInfo> children = canvasViewInfo.getChildren();
- assert children instanceof RandomAccess;
- for (int i = children.size() - 1; i >= 0; i--) {
- CanvasViewInfo child = children.get(i);
- CanvasViewInfo v = findViewInfoAt_Recursive(p, child);
- if (v != null) {
- return v;
- }
- }
-
- // if no children matched, this is the view that we're looking for
- return canvasViewInfo;
- }
-
- return null;
- }
-
- /**
- * Returns a list of all the possible alternatives for a given view at the given
- * position. This is used to build and manage the "alternate" selection that cycles
- * around the parents or children of the currently selected element.
- */
- /* package */ List<CanvasViewInfo> findAltViewInfoAt(LayoutPoint p) {
- if (mLastValidViewInfoRoot != null) {
- return findAltViewInfoAt_Recursive(p, mLastValidViewInfoRoot, null);
- }
-
- return null;
- }
-
- /**
- * Internal recursive version of {@link #findAltViewInfoAt(int, int, CanvasViewInfo)}.
- * Please don't use directly.
- */
- private List<CanvasViewInfo> findAltViewInfoAt_Recursive(
- LayoutPoint p, CanvasViewInfo parent, List<CanvasViewInfo> outList) {
- Rectangle r;
-
- if (outList == null) {
- outList = new ArrayList<CanvasViewInfo>();
-
- if (parent != null) {
- // add the parent root only once
- r = parent.getSelectionRect();
- if (r.contains(p.x, p.y)) {
- outList.add(parent);
- }
- }
- }
-
- if (parent != null && !parent.getChildren().isEmpty()) {
- // then add all children that match the position
- for (CanvasViewInfo child : parent.getChildren()) {
- r = child.getSelectionRect();
- if (r.contains(p.x, p.y)) {
- outList.add(child);
- }
- }
-
- // finally recurse in the children
- for (CanvasViewInfo child : parent.getChildren()) {
- r = child.getSelectionRect();
- if (r.contains(p.x, p.y)) {
- findAltViewInfoAt_Recursive(p, child, outList);
- }
- }
- }
-
- return outList;
- }
-
- /**
- * Locates and returns the {@link CanvasViewInfo} corresponding to the given
- * node, or null if it cannot be found.
- *
- * @param node The node we want to find a corresponding
- * {@link CanvasViewInfo} for.
- * @return The {@link CanvasViewInfo} corresponding to the given node, or
- * null if no match was found.
- */
- public CanvasViewInfo findViewInfoFor(INode node) {
- return findViewInfoFor((NodeProxy) node);
- }
-
- /**
- * Tries to find a child with the same view key in the view info sub-tree.
- * Returns null if not found.
- *
- * @param viewKey The view key that a matching {@link CanvasViewInfo} should
- * have as its key.
- * @return A {@link CanvasViewInfo} matching the given key, or null if not
- * found.
- */
- public CanvasViewInfo findViewInfoFor(UiElementNode viewKey) {
- return mNodeToView.get(viewKey);
- }
-
- /**
- * Tries to find a child with the given node proxy as the view key.
- * Returns null if not found.
- *
- * @param proxy The view key that a matching {@link CanvasViewInfo} should
- * have as its key.
- * @return A {@link CanvasViewInfo} matching the given key, or null if not
- * found.
- */
- @Nullable
- public CanvasViewInfo findViewInfoFor(@Nullable NodeProxy proxy) {
- if (proxy == null) {
- return null;
- }
- return mNodeToView.get(proxy.getNode());
- }
-
- /**
- * Returns a list of ALL ViewInfos (possibly excluding the root, depending
- * on the parameter for that).
- *
- * @param includeRoot If true, include the root in the list, otherwise
- * exclude it (but include all its children)
- * @return A list of canvas view infos.
- */
- public List<CanvasViewInfo> findAllViewInfos(boolean includeRoot) {
- List<CanvasViewInfo> infos = new ArrayList<CanvasViewInfo>();
- if (mIsResultValid && mLastValidViewInfoRoot != null) {
- findAllViewInfos(infos, mLastValidViewInfoRoot, includeRoot);
- }
-
- return infos;
- }
-
- private void findAllViewInfos(List<CanvasViewInfo> result, CanvasViewInfo canvasViewInfo,
- boolean includeRoot) {
- if (canvasViewInfo != null) {
- if (includeRoot || !canvasViewInfo.isRoot()) {
- result.add(canvasViewInfo);
- }
- for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
- findAllViewInfos(result, child, true);
- }
- }
- }
-
- /**
- * Returns the root of the view hierarchy, if any (could be null, for example
- * on rendering failure).
- *
- * @return The current view hierarchy, or null
- */
- public CanvasViewInfo getRoot() {
- return mLastValidViewInfoRoot;
- }
-
- /**
- * Returns a collection of views that have zero bounds and that correspond to empty
- * parents. Note that the views may not actually have zero bounds; in particular, if
- * they are exploded ({@link CanvasViewInfo#isExploded()}, then they will have the
- * bounds of a shown invisible node. Therefore, this method returns the views that
- * would be invisible in a real rendering of the scene.
- *
- * @return A collection of empty parent views.
- */
- public List<CanvasViewInfo> getInvisibleViews() {
- return mInvisibleParentsReadOnly;
- }
-
- /**
- * Returns the invisible nodes (the {@link UiElementNode} objects corresponding
- * to the {@link CanvasViewInfo} objects returned from {@link #getInvisibleViews()}.
- * We are pulling out the nodes since they preserve their identity across layout
- * rendering, and in particular we return it as a set such that the layout renderer
- * can perform quick identity checks when looking up attribute values during the
- * rendering process.
- *
- * @return A set of the invisible nodes.
- */
- public Set<UiElementNode> getInvisibleNodes() {
- if (mInvisibleParents.size() == 0) {
- return Collections.emptySet();
- }
-
- Set<UiElementNode> nodes = new HashSet<UiElementNode>(mInvisibleParents.size());
- for (CanvasViewInfo info : mInvisibleParents) {
- UiViewElementNode node = info.getUiViewNode();
- if (node != null) {
- nodes.add(node);
- }
- }
-
- return nodes;
- }
-
- /**
- * Returns the list of bounds for included views in the current view hierarchy. Can be null
- * when there are no included views.
- *
- * @return a list of included view bounds, or null
- */
- public List<Rectangle> getIncludedBounds() {
- return mIncludedBounds;
- }
-
- /**
- * Returns a map of the default properties for the given view object in this session
- *
- * @param viewObject the object to look up the properties map for
- * @return the map of properties, or null if not found
- */
- @Nullable
- public Map<String, String> getDefaultProperties(@NonNull Object viewObject) {
- if (mSession != null) {
- return mSession.getDefaultProperties(viewObject);
- }
-
- return null;
- }
-
- /**
- * Dumps a {@link ViewInfo} hierarchy to stdout
- *
- * @param session the corresponding session, if any
- * @param info the {@link ViewInfo} object to dump
- * @param depth the depth to indent it to
- */
- public static void dump(RenderSession session, ViewInfo info, int depth) {
- if (DUMP_INFO) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < depth; i++) {
- sb.append(" "); //$NON-NLS-1$
- }
- sb.append(info.getClassName());
- sb.append(" ["); //$NON-NLS-1$
- sb.append(info.getLeft());
- sb.append(","); //$NON-NLS-1$
- sb.append(info.getTop());
- sb.append(","); //$NON-NLS-1$
- sb.append(info.getRight());
- sb.append(","); //$NON-NLS-1$
- sb.append(info.getBottom());
- sb.append("]"); //$NON-NLS-1$
- Object cookie = info.getCookie();
- if (cookie instanceof UiViewElementNode) {
- sb.append(" "); //$NON-NLS-1$
- UiViewElementNode node = (UiViewElementNode) cookie;
- sb.append("<"); //$NON-NLS-1$
- sb.append(node.getDescriptor().getXmlName());
- sb.append(">"); //$NON-NLS-1$
-
- String id = node.getAttributeValue(ATTR_ID);
- if (id != null && !id.isEmpty()) {
- sb.append(" ");
- sb.append(id);
- }
- } else if (cookie != null) {
- sb.append(" " + cookie); //$NON-NLS-1$
- }
- /* Display defaults?
- if (info.getViewObject() != null) {
- Map<String, String> defaults = session.getDefaultProperties(info.getCookie());
- sb.append(" - defaults: "); //$NON-NLS-1$
- sb.append(defaults);
- sb.append('\n');
- }
- */
-
- System.out.println(sb.toString());
-
- for (ViewInfo child : info.getChildren()) {
- dump(session, child, depth + 1);
- }
- }
- }
-}