aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
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/LayoutCanvas.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java1720
1 files changed, 0 insertions, 1720 deletions
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();
- }
- }
- }
-}