aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaphael <raphael@google.com>2010-01-14 16:13:26 -0800
committerRaphael <raphael@google.com>2010-01-14 16:33:37 -0800
commit1a9d30498bb489a8a7704a86eeb6effd92f35f3b (patch)
treec1b16eb02716d3b5348f38df3ea739756c700270
parent784f932f81764c275a961bf8faf286b32c209437 (diff)
downloadsdk-1a9d30498bb489a8a7704a86eeb6effd92f35f3b.zip
sdk-1a9d30498bb489a8a7704a86eeb6effd92f35f3b.tar.gz
sdk-1a9d30498bb489a8a7704a86eeb6effd92f35f3b.tar.bz2
ADT GRE: Move gscripts package.
Moving the public API from com.android.ide.eclispse.adt.gscripts to ...adt.editors.layout.gscripts. Change-Id: Idf5b979d47dbbbe2514cce8cc3c688eb273bcce6
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.view.View.groovy10
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy10
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy10
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.ListView.groovy10
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/DropZone.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/DropZone.java)2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INodeProxy.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/INodeProxy.java)2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/IViewRule.java)3
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Point.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Point.java)2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Rect.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Rect.java)2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java2382
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java1550
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java716
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java4
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java8
15 files changed, 2357 insertions, 2358 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.view.View.groovy b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.view.View.groovy
index cbaa73c..ceab48f 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.view.View.groovy
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.view.View.groovy
@@ -16,11 +16,11 @@
package com.android.adt.gscripts;
-import com.android.ide.eclipse.adt.gscripts.IViewRule;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.Rect;
-import com.android.ide.eclipse.adt.gscripts.Point;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Point;
import java.util.Map;
import java.util.ArrayList;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy
index a94005b..2884af2 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy
@@ -16,11 +16,11 @@
package com.android.adt.gscripts;
-import com.android.ide.eclipse.adt.gscripts.IViewRule;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.Rect;
-import com.android.ide.eclipse.adt.gscripts.Point;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Point;
import java.util.Map;
import java.util.ArrayList;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy
index 5df24eb..3ce2bb3 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy
@@ -16,11 +16,11 @@
package com.android.adt.gscripts;
-import com.android.ide.eclipse.adt.gscripts.IViewRule;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.Rect;
-import com.android.ide.eclipse.adt.gscripts.Point;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Point;
import java.util.Map;
import java.util.ArrayList;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.ListView.groovy b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.ListView.groovy
index 8a78824..17713aa 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.ListView.groovy
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.ListView.groovy
@@ -16,11 +16,11 @@
package com.android.adt.gscripts;
-import com.android.ide.eclipse.adt.gscripts.IViewRule;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.Rect;
-import com.android.ide.eclipse.adt.gscripts.Point;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Point;
import java.util.Map;
import java.util.ArrayList;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/DropZone.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/DropZone.java
index 1651d71..7bb8716 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/DropZone.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/DropZone.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.gscripts;
+package com.android.ide.eclipse.adt.editors.layout.gscripts;
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/INodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INodeProxy.java
index 42a1b2d..3e6cb47 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/INodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/INodeProxy.java
@@ -15,7 +15,7 @@
*/
-package com.android.ide.eclipse.adt.gscripts;
+package com.android.ide.eclipse.adt.editors.layout.gscripts;
import groovy.lang.Closure;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/IViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java
index df36ee5..0adf3c5 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/IViewRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IViewRule.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.gscripts;
+package com.android.ide.eclipse.adt.editors.layout.gscripts;
import java.util.ArrayList;
-import java.util.LinkedHashMap;
import java.util.Map;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Point.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Point.java
index 50d371d..2e57912 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Point.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Point.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.gscripts;
+package com.android.ide.eclipse.adt.editors.layout.gscripts;
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Rect.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Rect.java
index d2ca640..8d0c08c 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/gscripts/Rect.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/Rect.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.gscripts;
+package com.android.ide.eclipse.adt.editors.layout.gscripts;
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java
index 36a80fb..f070811 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import org.eclipse.swt.dnd.DND;
@@ -163,7 +163,7 @@ import java.util.ArrayList;
Point p = eventToCanvasPoint(event);
mCanvas.getRulesEngine().dropFinish(viewFqcn, mTargetNode, mCurrentZone,
- new com.android.ide.eclipse.adt.gscripts.Point(p.x, p.y));
+ new com.android.ide.eclipse.adt.editors.layout.gscripts.Point(p.x, p.y));
clearDropInfo();
}
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
index 5c16b93..c277c25 100755
--- 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
@@ -1,1191 +1,1191 @@
-/*
- * 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.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.layout.ExplodedRenderingHelper;
-import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor;
-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.LayoutReloadMonitor.ChangeFlags;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.CustomToggle;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
-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.configurations.FolderConfiguration;
-import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
-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.LoadStatus;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
-import com.android.layoutlib.api.ILayoutBridge;
-import com.android.layoutlib.api.ILayoutLog;
-import com.android.layoutlib.api.ILayoutResult;
-import com.android.layoutlib.api.IProjectCallback;
-import com.android.layoutlib.api.IResourceValue;
-import com.android.layoutlib.api.IXmlPullParser;
-import com.android.sdklib.IAndroidTarget;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-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.draw2d.geometry.Rectangle;
-import org.eclipse.gef.ui.parts.SelectionSynchronizer;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.custom.StyledText;
-import org.eclipse.swt.dnd.Clipboard;
-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.ui.IActionBars;
-import org.eclipse.ui.IEditorInput;
-import org.eclipse.ui.IEditorSite;
-import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.actions.ActionFactory;
-import org.eclipse.ui.ide.IDE;
-import org.eclipse.ui.part.EditorPart;
-import org.eclipse.ui.part.FileEditorInput;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.util.Map;
-
-/**
- * Graphical layout editor part, version 2.
- *
- * @since GLE2
- *
- * TODO List:
- * - display error icon
- * - finish palette (see palette's todo list)
- * - finish canvas (see canva's todo list)
- * - completly rethink the property panel
- * - link to the existing outline editor (prolly reuse/adapt for simplier model and will need
- * to adapt the selection synchronizer.)
- */
-public class GraphicalEditorPart extends EditorPart implements IGraphicalLayoutEditor {
-
- /*
- * Useful notes:
- * To understand Drag'n'drop:
- * http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
- */
-
- /** Reference to the layout editor */
- private final LayoutEditor mLayoutEditor;
-
- /** reference to the file being edited. Can also be used to access the {@link IProject}. */
- private IFile mEditedFile;
-
- /** The current clipboard. Must be disposed later. */
- private Clipboard mClipboard;
-
- /** The configuration composite at the top of the layout editor. */
- private ConfigurationComposite mConfigComposite;
-
- /** The sash that splits the palette from the canvas. */
- private SashForm mSashPalette;
- private SashForm mSashError;
-
- /** The palette displayed on the left of the sash. */
- private PaletteComposite mPalette;
-
- /** The layout canvas displayed to the right of the sash. */
- private LayoutCanvas mLayoutCanvas;
-
- /** The Groovy Rules Engine associated with this editor. It is project-specific. */
- private RulesEngine mRulesEngine;
-
- private StyledText mErrorLabel;
-
- private Map<String, Map<String, IResourceValue>> mConfiguredFrameworkRes;
- private Map<String, Map<String, IResourceValue>> mConfiguredProjectRes;
- private ProjectCallback mProjectCallback;
- private ILayoutLog mLogger;
-
- private boolean mNeedsXmlReload = false;
- private boolean mNeedsRecompute = false;
-
- private TargetListener mTargetListener;
-
- private ConfigListener mConfigListener;
-
- private ReloadListener mReloadListener;
-
- protected boolean mUseExplodeMode;
-
-
- public GraphicalEditorPart(LayoutEditor layoutEditor) {
- mLayoutEditor = layoutEditor;
- 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);
- }
- }
-
- 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 void createPartControl(Composite parent) {
-
- Display d = parent.getDisplay();
- mClipboard = new Clipboard(d);
-
- GridLayout gl = new GridLayout(1, false);
- parent.setLayout(gl);
- gl.marginHeight = gl.marginWidth = 0;
-
- // create the top part for the configuration control
-
- CustomToggle[] toggles = new CustomToggle[] {
- new CustomToggle(
- "Explode",
- null, //image
- "Displays extra margins in the layout."
- ) {
- @Override
- public void onSelected(boolean newState) {
- mUseExplodeMode = newState;
- recomputeLayout();
- }
- },
- new CustomToggle(
- "Outline",
- null, //image
- "Shows the of all views in the layout."
- ) {
- @Override
- public void onSelected(boolean newState) {
- mLayoutCanvas.setShowOutline(newState);
- }
- }
- };
-
- mConfigListener = new ConfigListener();
- mConfigComposite = new ConfigurationComposite(mConfigListener, toggles, parent, SWT.BORDER);
-
- mSashPalette = new SashForm(parent, SWT.HORIZONTAL);
- mSashPalette.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- mPalette = new PaletteComposite(mSashPalette);
-
- mSashError = new SashForm(mSashPalette, SWT.VERTICAL | SWT.BORDER);
- mSashError.setLayoutData(new GridData(GridData.FILL_BOTH));
-
- mLayoutCanvas = new LayoutCanvas(mRulesEngine, mSashError, SWT.NONE);
-
- mErrorLabel = new StyledText(mSashError, SWT.READ_ONLY);
- mErrorLabel.setEditable(false);
- mErrorLabel.setBackground(d.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
- mErrorLabel.setForeground(d.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
-
- mSashPalette.setWeights(new int[] { 20, 80 });
- mSashError.setWeights(new int[] { 80, 20 });
- mSashError.setMaximizedControl(mLayoutCanvas);
-
- setupEditActions();
-
- // Initialize the state
- reloadPalette();
- }
-
- private void setupEditActions() {
-
- IActionBars actionBars = getEditorSite().getActionBars();
-
- actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), new Action("Copy") {
- @Override
- public void run() {
- // TODO enable copy only when there's a selection
- mLayoutCanvas.onCopy(mClipboard);
- }
- });
-
- actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), new Action("Cut") {
- @Override
- public void run() {
- // TODO enable cut only when there's a selection
- mLayoutCanvas.onCut(mClipboard);
- }
- });
-
- actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), new Action("Paste") {
- @Override
- public void run() {
- mLayoutCanvas.onPaste(mClipboard);
- }
- });
-
- actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
- new Action("Select All") {
- @Override
- public void run() {
- mLayoutCanvas.onSelectAll();
- }
- });
- }
-
- /**
- * Switches the stack to display the error label and hide the canvas.
- * @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));
- }
- mSashError.setMaximizedControl(null);
- }
-
- /** Displays the canvas and hides the error label. */
- private void hideError() {
- mSashError.setMaximizedControl(mLayoutCanvas);
- }
-
- @Override
- public void dispose() {
- if (mTargetListener != null) {
- AdtPlugin.getDefault().removeTargetListener(mTargetListener);
- mTargetListener = null;
- }
-
- if (mReloadListener != null) {
- LayoutReloadMonitor.getMonitor().removeListener(mReloadListener);
- mReloadListener = null;
- }
-
- if (mClipboard != null) {
- mClipboard.dispose();
- mClipboard = null;
- }
-
- super.dispose();
- }
-
- /**
- * Listens to changes from the Configuration UI banner and triggers layout rendering when
- * changed. Also provide the Configuration UI with the list of resources/layout to display.
- */
- private class ConfigListener implements IConfigListener {
-
- /**
- * Looks for a file matching the new {@link FolderConfiguration} and attempts to open it.
- * <p/>If there is no match, notify the user.
- */
- public void onConfigurationChange() {
- mConfiguredFrameworkRes = mConfiguredProjectRes = null;
-
- if (mEditedFile == null || mConfigComposite.getEditedConfig() == null) {
- return;
- }
-
- // 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 (mLayoutEditor.isCreatingPages()) {
- recomputeLayout();
- } else {
- // get the resources of the file's project.
- ProjectResources resources = ResourceManager.getInstance().getProjectResources(
- mEditedFile.getProject());
-
- // from the resources, look for a matching file
- ResourceFile match = null;
- if (resources != null) {
- match = resources.getMatchingFile(mEditedFile.getName(),
- ResourceFolderType.LAYOUT,
- mConfigComposite.getCurrentConfig());
- }
-
- if (match != null) {
- if (match.getFile().equals(mEditedFile) == false) {
- try {
- // tell the editor that the next replacement file is due to a config
- // change.
- mLayoutEditor.setNewFileOnConfigChange(true);
-
- // ask the IDE to open the replacement file.
- IDE.openEditor(
- getSite().getWorkbenchWindow().getActivePage(),
- match.getFile().getIFile());
-
- // we're done!
- return;
- } catch (PartInitException e) {
- // FIXME: do something!
- }
- }
-
- // at this point, we have not opened a new file.
-
- // Even though the layout doesn't change, the config changed, and referenced
- // resources need to be updated.
- recomputeLayout();
- } else {
- // display the error.
- FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();
- displayError(
- "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
- currentConfig.toDisplayString(),
- currentConfig.getFolderName(ResourceFolderType.LAYOUT,
- Sdk.getCurrent().getTarget(mEditedFile.getProject())),
- mEditedFile.getName());
- }
- }
- }
-
- public void onThemeChange() {
- recomputeLayout();
- }
-
- public void onClippingChange() {
- recomputeLayout();
- }
-
- public void onCreate() {
- LayoutCreatorDialog dialog = new LayoutCreatorDialog(mConfigComposite.getShell(),
- mEditedFile.getName(),
- Sdk.getCurrent().getTarget(mEditedFile.getProject()),
- mConfigComposite.getCurrentConfig());
- if (dialog.open() == Dialog.OK) {
- final FolderConfiguration config = new FolderConfiguration();
- dialog.getConfiguration(config);
-
- createAlternateLayout(config);
- }
- }
-
- public Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
- if (mConfiguredFrameworkRes == null && mConfigComposite != null) {
- ProjectResources 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(
- mConfigComposite.getCurrentConfig());
- }
- }
-
- return mConfiguredFrameworkRes;
- }
-
- public Map<String, Map<String, IResourceValue>> getConfiguredProjectResources() {
- if (mConfiguredProjectRes == null && mConfigComposite != null) {
- ProjectResources project = getProjectResources();
-
- // make sure they are loaded
- project.loadAll();
-
- // get the project resource values based on the current config
- mConfiguredProjectRes = project.getConfiguredResources(
- mConfigComposite.getCurrentConfig());
- }
-
- return mConfiguredProjectRes;
- }
-
- /**
- * Returns a {@link ProjectResources} for the framework resources.
- * @return the framework resources or null if not found.
- */
- public ProjectResources getFrameworkResources() {
- if (mEditedFile != null) {
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
-
- if (target != null) {
- AndroidTargetData data = currentSdk.getTargetData(target);
-
- if (data != null) {
- return data.getFrameworkResources();
- }
- }
- }
- }
-
- return null;
- }
-
- public ProjectResources getProjectResources() {
- if (mEditedFile != null) {
- ResourceManager manager = ResourceManager.getInstance();
- return manager.getProjectResources(mEditedFile.getProject());
- }
-
- return null;
- }
-
- /**
- * Creates a new layout file from the specified {@link FolderConfiguration}.
- */
- private void createAlternateLayout(final FolderConfiguration config) {
- new Job("Create Alternate Resource") {
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- // get the folder name
- String folderName = config.getFolderName(ResourceFolderType.LAYOUT,
- Sdk.getCurrent().getTarget(mEditedFile.getProject()));
- try {
-
- // look to see if it exists.
- // get the res folder
- IFolder res = (IFolder)mEditedFile.getParent().getParent();
- String path = res.getLocation().toOSString();
-
- File newLayoutFolder = new File(path + File.separator + folderName);
- if (newLayoutFolder.isFile()) {
- // this should not happen since aapt would have complained
- // before, but if one disable the automatic build, this could
- // happen.
- String message = String.format("File 'res/%1$s' is in the way!",
- folderName);
-
- AdtPlugin.displayError("Layout Creation", message);
-
- return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
- } else if (newLayoutFolder.exists() == false) {
- // create it.
- newLayoutFolder.mkdir();
- }
-
- // now create the file
- File newLayoutFile = new File(newLayoutFolder.getAbsolutePath() +
- File.separator + mEditedFile.getName());
-
- newLayoutFile.createNewFile();
-
- InputStream input = mEditedFile.getContents();
-
- FileOutputStream fos = new FileOutputStream(newLayoutFile);
-
- byte[] data = new byte[512];
- int count;
- while ((count = input.read(data)) != -1) {
- fos.write(data, 0, count);
- }
-
- input.close();
- fos.close();
-
- // refreshes the res folder to show up the new
- // layout folder (if needed) and the file.
- // We use a progress monitor to catch the end of the refresh
- // to trigger the edit of the new file.
- res.refreshLocal(IResource.DEPTH_INFINITE, new IProgressMonitor() {
- public void done() {
- mConfigComposite.getDisplay().asyncExec(new Runnable() {
- public void run() {
- onConfigurationChange();
- }
- });
- }
-
- public void beginTask(String name, int totalWork) {
- // pass
- }
-
- public void internalWorked(double work) {
- // pass
- }
-
- public boolean isCanceled() {
- // pass
- return false;
- }
-
- public void setCanceled(boolean value) {
- // pass
- }
-
- public void setTaskName(String name) {
- // pass
- }
-
- public void subTask(String name) {
- // pass
- }
-
- public void worked(int work) {
- // pass
- }
- });
- } catch (IOException e2) {
- String message = String.format(
- "Failed to create File 'res/%1$s/%2$s' : %3$s",
- folderName, mEditedFile.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, mEditedFile.getName(), e2.getMessage());
-
- AdtPlugin.displayError("Layout Creation", message);
-
- return e2.getStatus();
- }
-
- return Status.OK_STATUS;
-
- }
- }.schedule();
- }
- }
-
- /**
- * Listens to target changed in the current project, to trigger a new layout rendering.
- */
- private class TargetListener implements ITargetChangeListener {
-
- public void onProjectTargetChange(IProject changedProject) {
- if (changedProject != null && changedProject.equals(getProject())) {
- updateEditor();
- }
- }
-
- public void onTargetLoaded(IAndroidTarget target) {
- IProject project = getProject();
- if (target != null && target.equals(Sdk.getCurrent().getTarget(project))) {
- updateEditor();
- }
- }
-
- public void onSdkLoaded() {
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
- if (target != null) {
- mConfigComposite.onSdkLoaded(target);
- mConfigListener.onConfigurationChange();
- }
- }
- }
-
- private void updateEditor() {
- mLayoutEditor.commitPages(false /* onSave */);
-
- // because the target changed we must reset the configured resources.
- mConfiguredFrameworkRes = mConfiguredProjectRes = 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
- mLayoutEditor.initUiRootNode(true /*force*/);
- }
-
- private IProject getProject() {
- return getLayoutEditor().getProject();
- }
- }
-
- // ----------------
-
- /**
- * 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 (mNeedsRecompute || mNeedsXmlReload) {
- recomputeLayout();
- }
- }
-
- /**
- * Responds to a page change that made the Graphical editor page the deactivated page
- */
- public void deactivated() {
- // nothing to be done here for now.
- }
-
- /**
- * Opens and initialize the editor with a new file.
- * @param file the file being edited.
- */
- public void openFile(IFile file) {
- mEditedFile = file;
- mConfigComposite.openFile(mEditedFile);
-
- if (mReloadListener == null) {
- mReloadListener = new ReloadListener();
- LayoutReloadMonitor.getMonitor().addListener(mEditedFile.getProject(), mReloadListener);
- }
-
- if (mRulesEngine == null) {
- mRulesEngine = new RulesEngine(mEditedFile.getProject());
- if (mLayoutCanvas != null) {
- mLayoutCanvas.setRulesEngine(mRulesEngine);
- }
- }
- }
-
- /**
- * Resets the editor with a replacement file.
- * @param file the replacement file.
- */
- public void replaceFile(IFile file) {
- mEditedFile = file;
- mConfigComposite.replaceFile(mEditedFile);
- }
-
- /**
- * 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;
- mConfigComposite.changeFileOnNewConfig(mEditedFile);
- }
-
- public void onTargetChange() {
- mConfigComposite.onTargetChange();
- mConfigListener.onConfigurationChange();
- }
-
- public void onSdkChange() {
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
- if (target != null) {
- mConfigComposite.onSdkLoaded(target);
- mConfigListener.onConfigurationChange();
- }
- }
- }
-
- public Clipboard getClipboard() {
- return mClipboard;
- }
-
- public LayoutEditor getLayoutEditor() {
- return mLayoutEditor;
- }
-
- public UiDocumentNode getModel() {
- return mLayoutEditor.getUiRootNode();
- }
-
- public SelectionSynchronizer getSelectionSynchronizer() {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * Callback for XML model changed. Only update/recompute the layout if the editor is visible
- */
- public void onXmlModelChanged() {
- if (mLayoutEditor.isGraphicalEditorActive()) {
- doXmlReload(true /* force */);
- recomputeLayout();
- } else {
- mNeedsXmlReload = true;
- }
- }
-
- /**
- * Actually performs the XML reload
- * @see #onXmlModelChanged()
- */
- private void doXmlReload(boolean force) {
- if (force || mNeedsXmlReload) {
-
- // TODO : update the mLayoutCanvas, preserving the current selection if possible.
-
-// GraphicalViewer viewer = getGraphicalViewer();
-//
-// // try to preserve the selection before changing the content
-// SelectionManager selMan = viewer.getSelectionManager();
-// ISelection selection = selMan.getSelection();
-//
-// try {
-// viewer.setContents(getModel());
-// } finally {
-// selMan.setSelection(selection);
-// }
-
- mNeedsXmlReload = false;
- }
- }
-
- public void recomputeLayout() {
- doXmlReload(false /* force */);
- try {
- // 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;
- }
-
- IProject iProject = mEditedFile.getProject();
-
- 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.
- AdtPlugin.printErrorToConsole(iProject.getName(), message);
- return;
- }
-
- Sdk currentSdk = Sdk.getCurrent();
- if (currentSdk != null) {
- IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
- if (target == null) {
- displayError("The project target is not set.");
- return;
- }
-
- AndroidTargetData data = currentSdk.getTargetData(target);
- if (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);
- switch (targetLoadStatus) {
- case LOADING:
- displayError("The project target (%1$s) is still loading.\n%2$s will refresh automatically once the process is finished.",
- target.getName(), mEditedFile.getName());
-
- break;
- case FAILED: // known failure
- case LOADED: // success but data isn't loaded?!?!
- displayError("The project target (%s) was not properly loaded.",
- target.getName());
- break;
- }
-
- return;
- }
-
- // check there is actually a model (maybe the file is empty).
- UiDocumentNode model = getModel();
-
- if (model.getUiChildren().size() == 0) {
- displayError("No Xml content. Go to the Outline view and add nodes.");
- return;
- }
-
- LayoutBridge bridge = data.getLayoutBridge();
-
- if (bridge.bridge != null) { // bridge can never be null.
- ResourceManager resManager = ResourceManager.getInstance();
-
- ProjectResources projectRes = resManager.getProjectResources(iProject);
- if (projectRes == null) {
- displayError("Missing project resources.");
- return;
- }
-
- // get the resources of the file's project.
- Map<String, Map<String, IResourceValue>> configuredProjectRes =
- mConfigListener.getConfiguredProjectResources();
-
- // get the framework resources
- Map<String, Map<String, IResourceValue>> frameworkResources =
- mConfigListener.getConfiguredFrameworkResources();
-
- if (configuredProjectRes != null && frameworkResources != null) {
- if (mProjectCallback == null) {
- mProjectCallback = new ProjectCallback(
- bridge.classLoader, projectRes, iProject);
- }
-
- if (mLogger == null) {
- mLogger = new ILayoutLog() {
- public void error(String message) {
- AdtPlugin.printErrorToConsole(mEditedFile.getName(), message);
- }
-
- public void error(Throwable error) {
- String message = error.getMessage();
- if (message == null) {
- message = error.getClass().getName();
- }
-
- PrintStream ps = new PrintStream(AdtPlugin.getErrorStream());
- error.printStackTrace(ps);
- }
-
- public void warning(String message) {
- AdtPlugin.printToConsole(mEditedFile.getName(), message);
- }
- };
- }
-
- // get the selected theme
- String theme = mConfigComposite.getTheme();
- if (theme != null) {
- // Compute the layout
- Rectangle rect = getBounds();
-
- int width = rect.width;
- int height = rect.height;
- if (mUseExplodeMode) {
- // compute how many padding in x and y will bump the screen size
- ExplodedRenderingHelper helper = new ExplodedRenderingHelper(
- getModel(), iProject);
-
- // there are 2 paddings for each view
- // left and right, or top and bottom.
- int paddingValue = ExplodedRenderingHelper.PADDING_VALUE * 2;
-
- width += helper.getWidthPadding() * paddingValue;
- height += helper.getHeightPadding() * paddingValue;
- }
-
- int density = mConfigComposite.getDensity().getDpiValue();
- float xdpi = mConfigComposite.getXDpi();
- float ydpi = mConfigComposite.getYDpi();
- boolean isProjectTheme = mConfigComposite.isProjectTheme();
-
- UiElementPullParser parser = new UiElementPullParser(getModel(),
- mUseExplodeMode, density, xdpi, iProject);
-
- ILayoutResult result = computeLayout(bridge, parser,
- iProject /* projectKey */,
- width, height, !mConfigComposite.getClipping(),
- density, xdpi, ydpi,
- theme, isProjectTheme,
- configuredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
-
- mLayoutCanvas.setResult(result);
-
- // update the UiElementNode with the layout info.
- if (result.getSuccess() == ILayoutResult.SUCCESS) {
- hideError();
- } else {
- displayError(result.getErrorMessage());
- }
-
- model.refreshUi();
- }
- }
- } else {
- // SDK is loaded but not the layout library!
-
- // check whether the bridge managed to load, or not
- if (bridge.status == 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 {
- displayError("Eclipse failed to load the framework information and the layout library!");
- }
- }
- } else {
- displayError("Eclipse is loading the SDK.\n%1$s will refresh automatically once the process is finished.",
- mEditedFile.getName());
- }
- } finally {
- // no matter the result, we are done doing the recompute based on the latest
- // resource/code change.
- mNeedsRecompute = false;
- }
- }
-
- /**
- * Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
- * the implementation API level.
- *
- * Implementation detail: the bridge's computeLayout() method already returns a newly
- * allocated ILayoutResult.
- */
- @SuppressWarnings("deprecation")
- private static ILayoutResult computeLayout(LayoutBridge bridge,
- IXmlPullParser layoutDescription, Object projectKey,
- int screenWidth, int screenHeight, boolean renderFullSize,
- int density, float xdpi, float ydpi,
- String themeName, boolean isProjectTheme,
- Map<String, Map<String, IResourceValue>> projectResources,
- Map<String, Map<String, IResourceValue>> frameworkResources,
- IProjectCallback projectCallback, ILayoutLog logger) {
-
- if (bridge.apiLevel >= ILayoutBridge.API_CURRENT) {
- // newest API with support for "render full height"
- // TODO: link boolean to UI.
- return bridge.bridge.computeLayout(layoutDescription,
- projectKey, screenWidth, screenHeight, renderFullSize,
- density, xdpi, ydpi,
- themeName, isProjectTheme,
- projectResources, frameworkResources, projectCallback,
- logger);
- } else if (bridge.apiLevel == 3) {
- // newer api with density support.
- return bridge.bridge.computeLayout(layoutDescription,
- projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
- themeName, isProjectTheme,
- projectResources, frameworkResources, projectCallback,
- logger);
- } else if (bridge.apiLevel == 2) {
- // api with boolean for separation of project/framework theme
- return bridge.bridge.computeLayout(layoutDescription,
- projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
- projectResources, frameworkResources, projectCallback,
- logger);
- } else {
- // oldest api with no density/dpi, and project theme boolean mixed
- // into the theme name.
-
- // change the string if it's a custom theme to make sure we can
- // differentiate them
- if (isProjectTheme) {
- themeName = "*" + themeName; //$NON-NLS-1$
- }
-
- return bridge.bridge.computeLayout(layoutDescription,
- projectKey, screenWidth, screenHeight, themeName,
- projectResources, frameworkResources, projectCallback,
- logger);
- }
- }
-
- public Rectangle getBounds() {
- return mConfigComposite.getScreenBounds();
- }
-
- public void reloadPalette() {
- if (mPalette != null) {
- mPalette.reloadPalette(mLayoutEditor.getTargetData());
- }
- }
-
- /**
- * Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
- * created by {@link ElementCreateCommand#execute()}.
- *
- * @param uiNodeModel The {@link UiElementNode} to select.
- */
- public void selectModel(UiElementNode uiNodeModel) {
-
- // TODO this method was useful for GLE1. We may not need it anymore now.
-
-// GraphicalViewer viewer = getGraphicalViewer();
-//
-// // Give focus to the graphical viewer (in case the outline has it)
-// viewer.getControl().forceFocus();
-//
-// Object editPart = viewer.getEditPartRegistry().get(uiNodeModel);
-//
-// if (editPart instanceof EditPart) {
-// viewer.select((EditPart)editPart);
-// }
- }
-
- private class ReloadListener implements ILayoutReloadListener {
- /*
- * Called when the file changes triggered a redraw of the layout
- */
- public void reloadLayout(ChangeFlags flags) {
- boolean recompute = false;
-
- if (flags.rClass) {
- recompute = true;
- if (mEditedFile != null) {
- ProjectResources projectRes = ResourceManager.getInstance().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.
- mLayoutCanvas.getDisplay().asyncExec(new Runnable() {
- public void run() {
- mConfigComposite.updateLocales();
- }
- });
- }
-
- 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;
-
- // clear the cache in the bridge in case a bitmap/9-patch changed.
- IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
- if (target != null) {
-
- AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
- if (data != null) {
- LayoutBridge bridge = data.getLayoutBridge();
-
- if (bridge.bridge != null) {
- bridge.bridge.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 (recompute) {
- mLayoutCanvas.getDisplay().asyncExec(new Runnable() {
- public void run() {
- if (mLayoutEditor.isGraphicalEditorActive()) {
- recomputeLayout();
- } else {
- mNeedsRecompute = true;
- }
- }
- });
- }
- }
- }
-}
+/*
+ * 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.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.ExplodedRenderingHelper;
+import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor;
+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.LayoutReloadMonitor.ChangeFlags;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.CustomToggle;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
+import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
+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.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+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.LoadStatus;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
+import com.android.layoutlib.api.ILayoutBridge;
+import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IProjectCallback;
+import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+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.draw2d.geometry.Rectangle;
+import org.eclipse.gef.ui.parts.SelectionSynchronizer;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.dnd.Clipboard;
+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.ui.IActionBars;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.EditorPart;
+import org.eclipse.ui.part.FileEditorInput;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+/**
+ * Graphical layout editor part, version 2.
+ *
+ * @since GLE2
+ *
+ * TODO List:
+ * - display error icon
+ * - finish palette (see palette's todo list)
+ * - finish canvas (see canva's todo list)
+ * - completly rethink the property panel
+ * - link to the existing outline editor (prolly reuse/adapt for simplier model and will need
+ * to adapt the selection synchronizer.)
+ */
+public class GraphicalEditorPart extends EditorPart implements IGraphicalLayoutEditor {
+
+ /*
+ * Useful notes:
+ * To understand Drag'n'drop:
+ * http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html
+ */
+
+ /** Reference to the layout editor */
+ private final LayoutEditor mLayoutEditor;
+
+ /** reference to the file being edited. Can also be used to access the {@link IProject}. */
+ private IFile mEditedFile;
+
+ /** The current clipboard. Must be disposed later. */
+ private Clipboard mClipboard;
+
+ /** The configuration composite at the top of the layout editor. */
+ private ConfigurationComposite mConfigComposite;
+
+ /** The sash that splits the palette from the canvas. */
+ private SashForm mSashPalette;
+ private SashForm mSashError;
+
+ /** The palette displayed on the left of the sash. */
+ private PaletteComposite mPalette;
+
+ /** The layout canvas displayed to the right of the sash. */
+ private LayoutCanvas mLayoutCanvas;
+
+ /** The Groovy Rules Engine associated with this editor. It is project-specific. */
+ private RulesEngine mRulesEngine;
+
+ private StyledText mErrorLabel;
+
+ private Map<String, Map<String, IResourceValue>> mConfiguredFrameworkRes;
+ private Map<String, Map<String, IResourceValue>> mConfiguredProjectRes;
+ private ProjectCallback mProjectCallback;
+ private ILayoutLog mLogger;
+
+ private boolean mNeedsXmlReload = false;
+ private boolean mNeedsRecompute = false;
+
+ private TargetListener mTargetListener;
+
+ private ConfigListener mConfigListener;
+
+ private ReloadListener mReloadListener;
+
+ protected boolean mUseExplodeMode;
+
+
+ public GraphicalEditorPart(LayoutEditor layoutEditor) {
+ mLayoutEditor = layoutEditor;
+ 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);
+ }
+ }
+
+ 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 void createPartControl(Composite parent) {
+
+ Display d = parent.getDisplay();
+ mClipboard = new Clipboard(d);
+
+ GridLayout gl = new GridLayout(1, false);
+ parent.setLayout(gl);
+ gl.marginHeight = gl.marginWidth = 0;
+
+ // create the top part for the configuration control
+
+ CustomToggle[] toggles = new CustomToggle[] {
+ new CustomToggle(
+ "Explode",
+ null, //image
+ "Displays extra margins in the layout."
+ ) {
+ @Override
+ public void onSelected(boolean newState) {
+ mUseExplodeMode = newState;
+ recomputeLayout();
+ }
+ },
+ new CustomToggle(
+ "Outline",
+ null, //image
+ "Shows the of all views in the layout."
+ ) {
+ @Override
+ public void onSelected(boolean newState) {
+ mLayoutCanvas.setShowOutline(newState);
+ }
+ }
+ };
+
+ mConfigListener = new ConfigListener();
+ mConfigComposite = new ConfigurationComposite(mConfigListener, toggles, parent, SWT.BORDER);
+
+ mSashPalette = new SashForm(parent, SWT.HORIZONTAL);
+ mSashPalette.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ mPalette = new PaletteComposite(mSashPalette);
+
+ mSashError = new SashForm(mSashPalette, SWT.VERTICAL | SWT.BORDER);
+ mSashError.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ mLayoutCanvas = new LayoutCanvas(mRulesEngine, mSashError, SWT.NONE);
+
+ mErrorLabel = new StyledText(mSashError, SWT.READ_ONLY);
+ mErrorLabel.setEditable(false);
+ mErrorLabel.setBackground(d.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
+ mErrorLabel.setForeground(d.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
+
+ mSashPalette.setWeights(new int[] { 20, 80 });
+ mSashError.setWeights(new int[] { 80, 20 });
+ mSashError.setMaximizedControl(mLayoutCanvas);
+
+ setupEditActions();
+
+ // Initialize the state
+ reloadPalette();
+ }
+
+ private void setupEditActions() {
+
+ IActionBars actionBars = getEditorSite().getActionBars();
+
+ actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), new Action("Copy") {
+ @Override
+ public void run() {
+ // TODO enable copy only when there's a selection
+ mLayoutCanvas.onCopy(mClipboard);
+ }
+ });
+
+ actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), new Action("Cut") {
+ @Override
+ public void run() {
+ // TODO enable cut only when there's a selection
+ mLayoutCanvas.onCut(mClipboard);
+ }
+ });
+
+ actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), new Action("Paste") {
+ @Override
+ public void run() {
+ mLayoutCanvas.onPaste(mClipboard);
+ }
+ });
+
+ actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
+ new Action("Select All") {
+ @Override
+ public void run() {
+ mLayoutCanvas.onSelectAll();
+ }
+ });
+ }
+
+ /**
+ * Switches the stack to display the error label and hide the canvas.
+ * @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));
+ }
+ mSashError.setMaximizedControl(null);
+ }
+
+ /** Displays the canvas and hides the error label. */
+ private void hideError() {
+ mSashError.setMaximizedControl(mLayoutCanvas);
+ }
+
+ @Override
+ public void dispose() {
+ if (mTargetListener != null) {
+ AdtPlugin.getDefault().removeTargetListener(mTargetListener);
+ mTargetListener = null;
+ }
+
+ if (mReloadListener != null) {
+ LayoutReloadMonitor.getMonitor().removeListener(mReloadListener);
+ mReloadListener = null;
+ }
+
+ if (mClipboard != null) {
+ mClipboard.dispose();
+ mClipboard = null;
+ }
+
+ super.dispose();
+ }
+
+ /**
+ * Listens to changes from the Configuration UI banner and triggers layout rendering when
+ * changed. Also provide the Configuration UI with the list of resources/layout to display.
+ */
+ private class ConfigListener implements IConfigListener {
+
+ /**
+ * Looks for a file matching the new {@link FolderConfiguration} and attempts to open it.
+ * <p/>If there is no match, notify the user.
+ */
+ public void onConfigurationChange() {
+ mConfiguredFrameworkRes = mConfiguredProjectRes = null;
+
+ if (mEditedFile == null || mConfigComposite.getEditedConfig() == null) {
+ return;
+ }
+
+ // 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 (mLayoutEditor.isCreatingPages()) {
+ recomputeLayout();
+ } else {
+ // get the resources of the file's project.
+ ProjectResources resources = ResourceManager.getInstance().getProjectResources(
+ mEditedFile.getProject());
+
+ // from the resources, look for a matching file
+ ResourceFile match = null;
+ if (resources != null) {
+ match = resources.getMatchingFile(mEditedFile.getName(),
+ ResourceFolderType.LAYOUT,
+ mConfigComposite.getCurrentConfig());
+ }
+
+ if (match != null) {
+ if (match.getFile().equals(mEditedFile) == false) {
+ try {
+ // tell the editor that the next replacement file is due to a config
+ // change.
+ mLayoutEditor.setNewFileOnConfigChange(true);
+
+ // ask the IDE to open the replacement file.
+ IDE.openEditor(
+ getSite().getWorkbenchWindow().getActivePage(),
+ match.getFile().getIFile());
+
+ // we're done!
+ return;
+ } catch (PartInitException e) {
+ // FIXME: do something!
+ }
+ }
+
+ // at this point, we have not opened a new file.
+
+ // Even though the layout doesn't change, the config changed, and referenced
+ // resources need to be updated.
+ recomputeLayout();
+ } else {
+ // display the error.
+ FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();
+ displayError(
+ "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
+ currentConfig.toDisplayString(),
+ currentConfig.getFolderName(ResourceFolderType.LAYOUT,
+ Sdk.getCurrent().getTarget(mEditedFile.getProject())),
+ mEditedFile.getName());
+ }
+ }
+ }
+
+ public void onThemeChange() {
+ recomputeLayout();
+ }
+
+ public void onClippingChange() {
+ recomputeLayout();
+ }
+
+ public void onCreate() {
+ LayoutCreatorDialog dialog = new LayoutCreatorDialog(mConfigComposite.getShell(),
+ mEditedFile.getName(),
+ Sdk.getCurrent().getTarget(mEditedFile.getProject()),
+ mConfigComposite.getCurrentConfig());
+ if (dialog.open() == Dialog.OK) {
+ final FolderConfiguration config = new FolderConfiguration();
+ dialog.getConfiguration(config);
+
+ createAlternateLayout(config);
+ }
+ }
+
+ public Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() {
+ if (mConfiguredFrameworkRes == null && mConfigComposite != null) {
+ ProjectResources 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(
+ mConfigComposite.getCurrentConfig());
+ }
+ }
+
+ return mConfiguredFrameworkRes;
+ }
+
+ public Map<String, Map<String, IResourceValue>> getConfiguredProjectResources() {
+ if (mConfiguredProjectRes == null && mConfigComposite != null) {
+ ProjectResources project = getProjectResources();
+
+ // make sure they are loaded
+ project.loadAll();
+
+ // get the project resource values based on the current config
+ mConfiguredProjectRes = project.getConfiguredResources(
+ mConfigComposite.getCurrentConfig());
+ }
+
+ return mConfiguredProjectRes;
+ }
+
+ /**
+ * Returns a {@link ProjectResources} for the framework resources.
+ * @return the framework resources or null if not found.
+ */
+ public ProjectResources getFrameworkResources() {
+ if (mEditedFile != null) {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
+
+ if (target != null) {
+ AndroidTargetData data = currentSdk.getTargetData(target);
+
+ if (data != null) {
+ return data.getFrameworkResources();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public ProjectResources getProjectResources() {
+ if (mEditedFile != null) {
+ ResourceManager manager = ResourceManager.getInstance();
+ return manager.getProjectResources(mEditedFile.getProject());
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new layout file from the specified {@link FolderConfiguration}.
+ */
+ private void createAlternateLayout(final FolderConfiguration config) {
+ new Job("Create Alternate Resource") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // get the folder name
+ String folderName = config.getFolderName(ResourceFolderType.LAYOUT,
+ Sdk.getCurrent().getTarget(mEditedFile.getProject()));
+ try {
+
+ // look to see if it exists.
+ // get the res folder
+ IFolder res = (IFolder)mEditedFile.getParent().getParent();
+ String path = res.getLocation().toOSString();
+
+ File newLayoutFolder = new File(path + File.separator + folderName);
+ if (newLayoutFolder.isFile()) {
+ // this should not happen since aapt would have complained
+ // before, but if one disable the automatic build, this could
+ // happen.
+ String message = String.format("File 'res/%1$s' is in the way!",
+ folderName);
+
+ AdtPlugin.displayError("Layout Creation", message);
+
+ return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message);
+ } else if (newLayoutFolder.exists() == false) {
+ // create it.
+ newLayoutFolder.mkdir();
+ }
+
+ // now create the file
+ File newLayoutFile = new File(newLayoutFolder.getAbsolutePath() +
+ File.separator + mEditedFile.getName());
+
+ newLayoutFile.createNewFile();
+
+ InputStream input = mEditedFile.getContents();
+
+ FileOutputStream fos = new FileOutputStream(newLayoutFile);
+
+ byte[] data = new byte[512];
+ int count;
+ while ((count = input.read(data)) != -1) {
+ fos.write(data, 0, count);
+ }
+
+ input.close();
+ fos.close();
+
+ // refreshes the res folder to show up the new
+ // layout folder (if needed) and the file.
+ // We use a progress monitor to catch the end of the refresh
+ // to trigger the edit of the new file.
+ res.refreshLocal(IResource.DEPTH_INFINITE, new IProgressMonitor() {
+ public void done() {
+ mConfigComposite.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ onConfigurationChange();
+ }
+ });
+ }
+
+ public void beginTask(String name, int totalWork) {
+ // pass
+ }
+
+ public void internalWorked(double work) {
+ // pass
+ }
+
+ public boolean isCanceled() {
+ // pass
+ return false;
+ }
+
+ public void setCanceled(boolean value) {
+ // pass
+ }
+
+ public void setTaskName(String name) {
+ // pass
+ }
+
+ public void subTask(String name) {
+ // pass
+ }
+
+ public void worked(int work) {
+ // pass
+ }
+ });
+ } catch (IOException e2) {
+ String message = String.format(
+ "Failed to create File 'res/%1$s/%2$s' : %3$s",
+ folderName, mEditedFile.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, mEditedFile.getName(), e2.getMessage());
+
+ AdtPlugin.displayError("Layout Creation", message);
+
+ return e2.getStatus();
+ }
+
+ return Status.OK_STATUS;
+
+ }
+ }.schedule();
+ }
+ }
+
+ /**
+ * Listens to target changed in the current project, to trigger a new layout rendering.
+ */
+ private class TargetListener implements ITargetChangeListener {
+
+ public void onProjectTargetChange(IProject changedProject) {
+ if (changedProject != null && changedProject.equals(getProject())) {
+ updateEditor();
+ }
+ }
+
+ public void onTargetLoaded(IAndroidTarget target) {
+ IProject project = getProject();
+ if (target != null && target.equals(Sdk.getCurrent().getTarget(project))) {
+ updateEditor();
+ }
+ }
+
+ public void onSdkLoaded() {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
+ if (target != null) {
+ mConfigComposite.onSdkLoaded(target);
+ mConfigListener.onConfigurationChange();
+ }
+ }
+ }
+
+ private void updateEditor() {
+ mLayoutEditor.commitPages(false /* onSave */);
+
+ // because the target changed we must reset the configured resources.
+ mConfiguredFrameworkRes = mConfiguredProjectRes = 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
+ mLayoutEditor.initUiRootNode(true /*force*/);
+ }
+
+ private IProject getProject() {
+ return getLayoutEditor().getProject();
+ }
+ }
+
+ // ----------------
+
+ /**
+ * 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 (mNeedsRecompute || mNeedsXmlReload) {
+ recomputeLayout();
+ }
+ }
+
+ /**
+ * Responds to a page change that made the Graphical editor page the deactivated page
+ */
+ public void deactivated() {
+ // nothing to be done here for now.
+ }
+
+ /**
+ * Opens and initialize the editor with a new file.
+ * @param file the file being edited.
+ */
+ public void openFile(IFile file) {
+ mEditedFile = file;
+ mConfigComposite.openFile(mEditedFile);
+
+ if (mReloadListener == null) {
+ mReloadListener = new ReloadListener();
+ LayoutReloadMonitor.getMonitor().addListener(mEditedFile.getProject(), mReloadListener);
+ }
+
+ if (mRulesEngine == null) {
+ mRulesEngine = new RulesEngine(mEditedFile.getProject());
+ if (mLayoutCanvas != null) {
+ mLayoutCanvas.setRulesEngine(mRulesEngine);
+ }
+ }
+ }
+
+ /**
+ * Resets the editor with a replacement file.
+ * @param file the replacement file.
+ */
+ public void replaceFile(IFile file) {
+ mEditedFile = file;
+ mConfigComposite.replaceFile(mEditedFile);
+ }
+
+ /**
+ * 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;
+ mConfigComposite.changeFileOnNewConfig(mEditedFile);
+ }
+
+ public void onTargetChange() {
+ mConfigComposite.onTargetChange();
+ mConfigListener.onConfigurationChange();
+ }
+
+ public void onSdkChange() {
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
+ if (target != null) {
+ mConfigComposite.onSdkLoaded(target);
+ mConfigListener.onConfigurationChange();
+ }
+ }
+ }
+
+ public Clipboard getClipboard() {
+ return mClipboard;
+ }
+
+ public LayoutEditor getLayoutEditor() {
+ return mLayoutEditor;
+ }
+
+ public UiDocumentNode getModel() {
+ return mLayoutEditor.getUiRootNode();
+ }
+
+ public SelectionSynchronizer getSelectionSynchronizer() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Callback for XML model changed. Only update/recompute the layout if the editor is visible
+ */
+ public void onXmlModelChanged() {
+ if (mLayoutEditor.isGraphicalEditorActive()) {
+ doXmlReload(true /* force */);
+ recomputeLayout();
+ } else {
+ mNeedsXmlReload = true;
+ }
+ }
+
+ /**
+ * Actually performs the XML reload
+ * @see #onXmlModelChanged()
+ */
+ private void doXmlReload(boolean force) {
+ if (force || mNeedsXmlReload) {
+
+ // TODO : update the mLayoutCanvas, preserving the current selection if possible.
+
+// GraphicalViewer viewer = getGraphicalViewer();
+//
+// // try to preserve the selection before changing the content
+// SelectionManager selMan = viewer.getSelectionManager();
+// ISelection selection = selMan.getSelection();
+//
+// try {
+// viewer.setContents(getModel());
+// } finally {
+// selMan.setSelection(selection);
+// }
+
+ mNeedsXmlReload = false;
+ }
+ }
+
+ public void recomputeLayout() {
+ doXmlReload(false /* force */);
+ try {
+ // 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;
+ }
+
+ IProject iProject = mEditedFile.getProject();
+
+ 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.
+ AdtPlugin.printErrorToConsole(iProject.getName(), message);
+ return;
+ }
+
+ Sdk currentSdk = Sdk.getCurrent();
+ if (currentSdk != null) {
+ IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject());
+ if (target == null) {
+ displayError("The project target is not set.");
+ return;
+ }
+
+ AndroidTargetData data = currentSdk.getTargetData(target);
+ if (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);
+ switch (targetLoadStatus) {
+ case LOADING:
+ displayError("The project target (%1$s) is still loading.\n%2$s will refresh automatically once the process is finished.",
+ target.getName(), mEditedFile.getName());
+
+ break;
+ case FAILED: // known failure
+ case LOADED: // success but data isn't loaded?!?!
+ displayError("The project target (%s) was not properly loaded.",
+ target.getName());
+ break;
+ }
+
+ return;
+ }
+
+ // check there is actually a model (maybe the file is empty).
+ UiDocumentNode model = getModel();
+
+ if (model.getUiChildren().size() == 0) {
+ displayError("No Xml content. Go to the Outline view and add nodes.");
+ return;
+ }
+
+ LayoutBridge bridge = data.getLayoutBridge();
+
+ if (bridge.bridge != null) { // bridge can never be null.
+ ResourceManager resManager = ResourceManager.getInstance();
+
+ ProjectResources projectRes = resManager.getProjectResources(iProject);
+ if (projectRes == null) {
+ displayError("Missing project resources.");
+ return;
+ }
+
+ // get the resources of the file's project.
+ Map<String, Map<String, IResourceValue>> configuredProjectRes =
+ mConfigListener.getConfiguredProjectResources();
+
+ // get the framework resources
+ Map<String, Map<String, IResourceValue>> frameworkResources =
+ mConfigListener.getConfiguredFrameworkResources();
+
+ if (configuredProjectRes != null && frameworkResources != null) {
+ if (mProjectCallback == null) {
+ mProjectCallback = new ProjectCallback(
+ bridge.classLoader, projectRes, iProject);
+ }
+
+ if (mLogger == null) {
+ mLogger = new ILayoutLog() {
+ public void error(String message) {
+ AdtPlugin.printErrorToConsole(mEditedFile.getName(), message);
+ }
+
+ public void error(Throwable error) {
+ String message = error.getMessage();
+ if (message == null) {
+ message = error.getClass().getName();
+ }
+
+ PrintStream ps = new PrintStream(AdtPlugin.getErrorStream());
+ error.printStackTrace(ps);
+ }
+
+ public void warning(String message) {
+ AdtPlugin.printToConsole(mEditedFile.getName(), message);
+ }
+ };
+ }
+
+ // get the selected theme
+ String theme = mConfigComposite.getTheme();
+ if (theme != null) {
+ // Compute the layout
+ Rectangle rect = getBounds();
+
+ int width = rect.width;
+ int height = rect.height;
+ if (mUseExplodeMode) {
+ // compute how many padding in x and y will bump the screen size
+ ExplodedRenderingHelper helper = new ExplodedRenderingHelper(
+ getModel(), iProject);
+
+ // there are 2 paddings for each view
+ // left and right, or top and bottom.
+ int paddingValue = ExplodedRenderingHelper.PADDING_VALUE * 2;
+
+ width += helper.getWidthPadding() * paddingValue;
+ height += helper.getHeightPadding() * paddingValue;
+ }
+
+ int density = mConfigComposite.getDensity().getDpiValue();
+ float xdpi = mConfigComposite.getXDpi();
+ float ydpi = mConfigComposite.getYDpi();
+ boolean isProjectTheme = mConfigComposite.isProjectTheme();
+
+ UiElementPullParser parser = new UiElementPullParser(getModel(),
+ mUseExplodeMode, density, xdpi, iProject);
+
+ ILayoutResult result = computeLayout(bridge, parser,
+ iProject /* projectKey */,
+ width, height, !mConfigComposite.getClipping(),
+ density, xdpi, ydpi,
+ theme, isProjectTheme,
+ configuredProjectRes, frameworkResources, mProjectCallback,
+ mLogger);
+
+ mLayoutCanvas.setResult(result);
+
+ // update the UiElementNode with the layout info.
+ if (result.getSuccess() == ILayoutResult.SUCCESS) {
+ hideError();
+ } else {
+ displayError(result.getErrorMessage());
+ }
+
+ model.refreshUi();
+ }
+ }
+ } else {
+ // SDK is loaded but not the layout library!
+
+ // check whether the bridge managed to load, or not
+ if (bridge.status == 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 {
+ displayError("Eclipse failed to load the framework information and the layout library!");
+ }
+ }
+ } else {
+ displayError("Eclipse is loading the SDK.\n%1$s will refresh automatically once the process is finished.",
+ mEditedFile.getName());
+ }
+ } finally {
+ // no matter the result, we are done doing the recompute based on the latest
+ // resource/code change.
+ mNeedsRecompute = false;
+ }
+ }
+
+ /**
+ * Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
+ * the implementation API level.
+ *
+ * Implementation detail: the bridge's computeLayout() method already returns a newly
+ * allocated ILayoutResult.
+ */
+ @SuppressWarnings("deprecation")
+ private static ILayoutResult computeLayout(LayoutBridge bridge,
+ IXmlPullParser layoutDescription, Object projectKey,
+ int screenWidth, int screenHeight, boolean renderFullSize,
+ int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map<String, Map<String, IResourceValue>> projectResources,
+ Map<String, Map<String, IResourceValue>> frameworkResources,
+ IProjectCallback projectCallback, ILayoutLog logger) {
+
+ if (bridge.apiLevel >= ILayoutBridge.API_CURRENT) {
+ // newest API with support for "render full height"
+ // TODO: link boolean to UI.
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, renderFullSize,
+ density, xdpi, ydpi,
+ themeName, isProjectTheme,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ } else if (bridge.apiLevel == 3) {
+ // newer api with density support.
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
+ themeName, isProjectTheme,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ } else if (bridge.apiLevel == 2) {
+ // api with boolean for separation of project/framework theme
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ } else {
+ // oldest api with no density/dpi, and project theme boolean mixed
+ // into the theme name.
+
+ // change the string if it's a custom theme to make sure we can
+ // differentiate them
+ if (isProjectTheme) {
+ themeName = "*" + themeName; //$NON-NLS-1$
+ }
+
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, themeName,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ }
+ }
+
+ public Rectangle getBounds() {
+ return mConfigComposite.getScreenBounds();
+ }
+
+ public void reloadPalette() {
+ if (mPalette != null) {
+ mPalette.reloadPalette(mLayoutEditor.getTargetData());
+ }
+ }
+
+ /**
+ * Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node
+ * created by {@link ElementCreateCommand#execute()}.
+ *
+ * @param uiNodeModel The {@link UiElementNode} to select.
+ */
+ public void selectModel(UiElementNode uiNodeModel) {
+
+ // TODO this method was useful for GLE1. We may not need it anymore now.
+
+// GraphicalViewer viewer = getGraphicalViewer();
+//
+// // Give focus to the graphical viewer (in case the outline has it)
+// viewer.getControl().forceFocus();
+//
+// Object editPart = viewer.getEditPartRegistry().get(uiNodeModel);
+//
+// if (editPart instanceof EditPart) {
+// viewer.select((EditPart)editPart);
+// }
+ }
+
+ private class ReloadListener implements ILayoutReloadListener {
+ /*
+ * Called when the file changes triggered a redraw of the layout
+ */
+ public void reloadLayout(ChangeFlags flags) {
+ boolean recompute = false;
+
+ if (flags.rClass) {
+ recompute = true;
+ if (mEditedFile != null) {
+ ProjectResources projectRes = ResourceManager.getInstance().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.
+ mLayoutCanvas.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ mConfigComposite.updateLocales();
+ }
+ });
+ }
+
+ 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;
+
+ // clear the cache in the bridge in case a bitmap/9-patch changed.
+ IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject());
+ if (target != null) {
+
+ AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
+ if (data != null) {
+ LayoutBridge bridge = data.getLayoutBridge();
+
+ if (bridge.bridge != null) {
+ bridge.bridge.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 (recompute) {
+ mLayoutCanvas.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ if (mLayoutEditor.isGraphicalEditorActive()) {
+ recomputeLayout();
+ } else {
+ mNeedsRecompute = 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
index 1463e87..0751492 100755
--- 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
@@ -1,775 +1,775 @@
-/*
- * 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.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.Rect;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
-import com.android.layoutlib.api.ILayoutResult;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.SWTException;
-import org.eclipse.swt.dnd.Clipboard;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseMoveListener;
-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.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.Canvas;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * Displays the image rendered by the {@link GraphicalEditorPart} and handles
- * the interaction with the widgets.
- * <p/>
- *
- * @since GLE2
- *
- * TODO list:
- * - gray on error, keep select but disable d'n'd.
- * - make sure it is scrollable (Canvas derives from Scrollable, so prolly just setting bounds.)
- * - handle drop target (from palette).
- * - handle drag'n'drop (internal, for moving/duplicating).
- * - handle context menu (depending on selection).
- * - selection synchronization with the outline (both ways).
- */
-/* package */ class LayoutCanvas extends Canvas {
-
- /**
- * Margin around the rendered image. Should be enough space to display the layout
- * width and height pseudo widgets.
- */
- static final int IMAGE_MARGIN = 5;
-
-
- /** The Groovy Rules Engine, associated with the current project. */
- private RulesEngine mRulesEngine;
-
- /*
- * The last valid ILayoutResult passed to {@link #setResult(ILayoutResult)}.
- * This can be null.
- * When non null, {@link #mLastValidViewInfoRoot} is guaranteed to be non-null too.
- */
- private ILayoutResult mLastValidResult;
-
- /**
- * The CanvasViewInfo root created for the last update of {@link #mLastValidResult}.
- * This is null when {@link #mLastValidResult} is null.
- * When non null, {@link #mLastValidResult} is guaranteed to be non-null too.
- */
- private CanvasViewInfo mLastValidViewInfoRoot;
-
- /**
- * True when the last {@link #setResult(ILayoutResult)} provided a valid {@link ILayoutResult}
- * in which case it is also available in {@link #mLastValidResult}.
- * 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/>
- * When this is false, {@link #mLastValidResult} can be non-null and points to an older
- * layout result.
- */
- private boolean mIsResultValid;
-
- /** Current background image. Null when there's no image. */
- private Image mImage;
-
- /** The current selection list. The list is never null, however it can be empty. */
- private final LinkedList<CanvasSelection> mSelections = new LinkedList<CanvasSelection>();
-
- /** CanvasSelection border color. Do not dispose, it's a system color. */
- private Color mSelectionFgColor;
-
- /** CanvasSelection name font. Do not dispose, it's a system font. */
- private Font mSelectionFont;
-
- /** Pixel height of the font displaying the selection name. Initially set to 0 and only
- * initialized in onPaint() when we have a GC. */
- private int mSelectionFontHeight;
-
- /** Current hover view info. Null when no mouse hover. */
- private CanvasViewInfo mHoverViewInfo;
-
- /** Current mouse hover border rectangle. Null when there's no mouse hover. */
- private Rectangle mHoverRect;
-
- /** Hover border color. Must be disposed, it's NOT a system color. */
- private Color mHoverFgColor;
-
- /** Outline color. Do not dispose, it's a system color. */
- private Color mOutlineColor;
-
- /**
- * 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;
-
- /** When true, always display the outline of all views. */
- private boolean mShowOutline;
-
- /** Drop target associated with this composite. */
- private DropTarget mDropTarget;
-
- /** Drop listener, with feedback from current drop */
- private CanvasDropListener mDropListener;
-
- /** Drop color. Do not dispose, it's a system color. */
- private Color mDropFgColor;
-
-
- public LayoutCanvas(RulesEngine rulesEngine, Composite parent, int style) {
- super(parent, style | SWT.DOUBLE_BUFFERED);
- mRulesEngine = rulesEngine;
-
- Display d = getDisplay();
- mSelectionFgColor = d.getSystemColor(SWT.COLOR_RED);
- mHoverFgColor = new Color(d, 0xFF, 0x99, 0x00); // orange
- mOutlineColor = d.getSystemColor(SWT.COLOR_GREEN);
- mSelectionFont = d.getSystemFont();
- mDropFgColor = d.getSystemColor(SWT.COLOR_YELLOW);
-
- addPaintListener(new PaintListener() {
- public void paintControl(PaintEvent e) {
- onPaint(e);
- }
- });
-
- addMouseMoveListener(new MouseMoveListener() {
- public void mouseMove(MouseEvent e) {
- onMouseMove(e);
- }
- });
-
- addMouseListener(new MouseListener() {
- public void mouseUp(MouseEvent e) {
- onMouseUp(e);
- }
-
- public void mouseDown(MouseEvent e) {
- onMouseDown(e);
- }
-
- public void mouseDoubleClick(MouseEvent e) {
- onDoubleClick(e);
- }
- });
-
- mDropTarget = new DropTarget(this, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT);
- mDropTarget.setTransfer(new Transfer[] { ElementDescTransfer.getInstance() });
- mDropListener = new CanvasDropListener(this);
- mDropTarget.addDropListener(mDropListener);
- }
-
- @Override
- public void dispose() {
- super.dispose();
-
- if (mHoverFgColor != null) {
- mHoverFgColor.dispose();
- mHoverFgColor = null;
- }
-
- if (mDropTarget != null) {
- mDropTarget.dispose();
- mDropTarget = null;
- }
-
- if (mRulesEngine != null) {
- mRulesEngine.dispose();
- mRulesEngine = null;
- }
- }
-
- /**
- * Returns true when the last {@link #setResult(ILayoutResult)} provided a valid
- * {@link ILayoutResult} in which case it is also available in {@link #mLastValidResult}.
- * 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/>
- * When this is false, {@link #mLastValidResult} can be non-null and points to an older
- * layout result.
- */
- /* package */ boolean isResultValid() {
- return mIsResultValid;
- }
-
- /** Returns the Groovy Rules Engine, associated with the current project. */
- /* package */ RulesEngine getRulesEngine() {
- return mRulesEngine;
- }
-
- /** Sets the Groovy Rules Engine, associated with the current project. */
- /* package */ void setRulesEngine(RulesEngine rulesEngine) {
- mRulesEngine = rulesEngine;
- }
-
- /**
- * 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 result The new rendering result, either valid or not.
- */
- public void setResult(ILayoutResult result) {
- // disable any hover
- mHoverRect = null;
-
- mIsResultValid = (result != null && result.getSuccess() == ILayoutResult.SUCCESS);
-
- if (mIsResultValid && result != null) {
- mLastValidResult = result;
- mLastValidViewInfoRoot = new CanvasViewInfo(result.getRootView());
- setImage(result.getImage());
-
- // Check if the selection is still the same (based on the object keys)
- // and eventually recompute their bounds.
- for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
- CanvasSelection s = it.next();
-
- // Check the if the selected object still exists
- Object key = s.getViewInfo().getUiViewKey();
- CanvasViewInfo vi = findViewInfoKey(key, mLastValidViewInfoRoot);
-
- // 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) {
- it.add(new CanvasSelection(vi, mRulesEngine));
- }
- }
-
- // remove the current alternate selection views
- mAltSelection = null;
- }
-
- redraw();
- }
-
- public void setShowOutline(boolean newState) {
- mShowOutline = newState;
- redraw();
- }
-
- /**
- * Called by the {@link GraphicalEditorPart} when the Copy action is requested.
- *
- * @param clipboard The shared clipboard. Must not be disposed.
- */
- public void onCopy(Clipboard clipboard) {
- // TODO implement copy to clipbard. Also will need to provide feedback to enable
- // copy only when there's a selection.
- }
-
- /**
- * Called by the {@link GraphicalEditorPart} when the Cut action is requested.
- *
- * @param clipboard The shared clipboard. Must not be disposed.
- */
- public void onCut(Clipboard clipboard) {
- // TODO implement copy to clipbard. Also will need to provide feedback to enable
- // cut only when there's a selection.
- }
-
- /**
- * Called by the {@link GraphicalEditorPart} when the Paste action is requested.
- *
- * @param clipboard The shared clipboard. Must not be disposed.
- */
- public void onPaste(Clipboard clipboard) {
-
- }
-
- /**
- * Called by the {@link GraphicalEditorPart} when the Select All action is requested.
- */
- public void onSelectAll() {
- // First clear the current selection, if any.
- mSelections.clear();
- mAltSelection = null;
-
- // Now select everything if there's a valid layout
- if (mIsResultValid && mLastValidResult != null) {
- selectAllViewInfos(mLastValidViewInfoRoot);
- redraw();
- }
- }
-
- /**
- * Delete action
- */
- public void onDelete() {
- // TODO not implemented yet, not even hooked in yet!
- }
-
- //---
-
- /**
- * Sets the image of the last *successful* rendering.
- * Converts the AWT image into an SWT image.
- */
- private void setImage(BufferedImage awtImage) {
- int width = awtImage.getWidth();
- int height = awtImage.getHeight();
-
- Raster raster = awtImage.getData(new java.awt.Rectangle(width, height));
- int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData();
-
- ImageData imageData = new ImageData(width, height, 32,
- new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
-
- imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
-
- mImage = new Image(getDisplay(), imageData);
- }
-
- /**
- * Sets the alpha for the given GC.
- * <p/>
- * Alpha may not work on all platforms and may fail with an exception.
- *
- * @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;
- }
- }
-
- /**
- * Paints the canvas in response to paint events.
- */
- private void onPaint(PaintEvent e) {
- GC gc = e.gc;
-
- if (mImage != null) {
- if (!mIsResultValid) {
- gc_setAlpha(gc, 128); // half-transparent
- }
-
- gc.drawImage(mImage, IMAGE_MARGIN, IMAGE_MARGIN);
-
- if (!mIsResultValid) {
- gc_setAlpha(gc, 255); // opaque
- }
- }
-
- if (mShowOutline) {
- gc.setForeground(mOutlineColor);
- gc.setLineStyle(SWT.LINE_DOT);
- drawOutline(gc, mLastValidViewInfoRoot);
- }
-
- if (mHoverRect != null) {
- gc.setForeground(mHoverFgColor);
- gc.setLineStyle(SWT.LINE_DOT);
- gc.drawRectangle(mHoverRect);
- }
-
- // initialize the selection font height once. We need the GC to do that.
- if (mSelectionFontHeight == 0) {
- gc.setFont(mSelectionFont);
- FontMetrics fm = gc.getFontMetrics();
- mSelectionFontHeight = fm.getHeight();
- }
-
- for (CanvasSelection s : mSelections) {
- drawSelection(gc, s);
- }
-
- drawDropZones(gc);
- }
-
- private void drawOutline(GC gc, CanvasViewInfo info) {
-
- Rectangle r = info.getAbsRect();
- gc.drawRectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN, r.width, r.height);
-
- for (CanvasViewInfo vi : info.getChildren()) {
- drawOutline(gc, vi);
- }
- }
-
- private void drawSelection(GC gc, CanvasSelection s) {
- Rectangle r = s.getRect();
-
- gc.setForeground(mSelectionFgColor);
- gc.setLineStyle(SWT.LINE_SOLID);
- gc.drawRectangle(s.getRect());
-
- String name = s.getName();
-
- if (name != null) {
- int xs = r.x + 2;
- int ys = r.y - mSelectionFontHeight;
- if (ys < 0) {
- ys = r.y + r.height;
- }
- gc.drawString(name, xs, ys, true /*transparent*/);
- }
- }
-
- private void drawDropZones(GC gc) {
- if (mDropListener == null) {
- return;
- }
-
- CanvasViewInfo vi = mDropListener.getTargetView();
- if (vi == null) {
- return;
- }
-
- gc.setForeground(mDropFgColor);
-
- ArrayList<DropZone> zones = mDropListener.getZones();
- if (zones != null) {
-
- gc.setLineStyle(SWT.LINE_SOLID);
- gc.setLineWidth(1);
-
- DropZone curr = mDropListener.getCurrentZone();
-
- for (DropZone zone : zones) {
- Rect r = zone.bounds;
- if (r != null && r.w > 0 && r.h > 0) {
- int x = r.x + IMAGE_MARGIN;
- int y = r.y + IMAGE_MARGIN;
-
- int alpha = 128; // half-transparent
- if (zone == curr) {
- alpha = 192;
- }
-
- if (gc_setAlpha(gc, alpha)) {
- gc.fillRectangle(x, y, r.w, r.h);
- gc_setAlpha(gc, 255); // opaque
- }
-
- gc.drawRectangle(x, y, r.w, r.h);
- }
- }
-
- }
-
- gc.setLineStyle(SWT.LINE_DOT);
- gc.setLineWidth(3);
- Rectangle r = vi.getAbsRect();
- gc.drawRectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN, r.width, r.height);
- gc.setLineWidth(1);
- }
-
- /**
- * Hover on top of a known child.
- */
- private void onMouseMove(MouseEvent e) {
- if (mLastValidResult != null) {
- CanvasViewInfo root = mLastValidViewInfoRoot;
- CanvasViewInfo vi = findViewInfoAt(e.x - IMAGE_MARGIN, e.y - IMAGE_MARGIN);
-
- // We don't hover on the root since it's not a widget per see and it is always there.
- if (vi == root) {
- vi = null;
- }
-
- boolean needsUpdate = vi != mHoverViewInfo;
- mHoverViewInfo = vi;
-
- if (vi == null) {
- mHoverRect = null;
- } else {
- Rectangle r = vi.getSelectionRect();
- mHoverRect = new Rectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN,
- r.width, r.height);
- }
-
- if (needsUpdate) {
- redraw();
- }
- }
- }
-
- private void onMouseDown(MouseEvent e) {
- // pass, not used yet.
- }
-
- /**
- * Performs selection on mouse up (not mouse down).
- * <p/>
- * Shift key 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).
- */
- private void onMouseUp(MouseEvent e) {
- if (mLastValidResult != null) {
-
- boolean isShift = (e.stateMask & SWT.SHIFT) != 0;
- boolean isAlt = (e.stateMask & SWT.ALT) != 0;
-
- int x = e.x - IMAGE_MARGIN;
- int y = e.y - IMAGE_MARGIN;
- CanvasViewInfo vi = findViewInfoAt(x, y);
-
- if (isShift && !isAlt) {
- // 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)) {
- redraw();
- return;
- }
-
- // otherwise add it.
- mSelections.add(new CanvasSelection(vi, mRulesEngine));
- redraw();
- }
-
- } else if (isAlt) {
- // 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, findAltViewInfoAt(
- x, y, mLastValidViewInfoRoot, null));
-
- // 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(new CanvasSelection(vi2, mRulesEngine));
- }
- } 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(new CanvasSelection(vi2, mRulesEngine));
- }
- }
- redraw();
-
- } else {
- // Case where no modifier is pressed: either select or reset the selection.
-
- // reset alternate selection if any
- mAltSelection = null;
-
- // reset (multi)selection if any
- if (mSelections.size() > 0) {
- if (mSelections.size() == 1 && mSelections.getFirst().getViewInfo() == vi) {
- // CanvasSelection remains the same, don't touch it.
- return;
- }
- mSelections.clear();
- }
-
- if (vi != null) {
- mSelections.add(new CanvasSelection(vi, mRulesEngine));
- }
- redraw();
- }
- }
- }
-
- /** Deselects a view info. Returns true if the object was actually selected. */
- private boolean deselect(CanvasViewInfo canvasViewInfo) {
- if (canvasViewInfo == null) {
- return false;
- }
-
- for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
- CanvasSelection s = it.next();
- if (canvasViewInfo == s.getViewInfo()) {
- it.remove();
- return true;
- }
- }
-
- return false;
- }
-
- /** Deselects multiple view infos, */
- private void deselectAll(List<CanvasViewInfo> canvasViewInfos) {
- for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
- CanvasSelection s = it.next();
- if (canvasViewInfos.contains(s.getViewInfo())) {
- it.remove();
- }
- }
- }
-
- private void onDoubleClick(MouseEvent e) {
- // pass, not used yet.
- }
-
- /**
- * Tries to find a child with the same view key in the view info sub-tree.
- * Returns null if not found.
- */
- private CanvasViewInfo findViewInfoKey(Object viewKey, CanvasViewInfo canvasViewInfo) {
- if (canvasViewInfo.getUiViewKey() == viewKey) {
- return canvasViewInfo;
- }
-
- // try to find a matching child
- for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
- CanvasViewInfo v = findViewInfoKey(viewKey, child);
- if (v != null) {
- return v;
- }
- }
-
- return null;
- }
-
-
- /**
- * 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.
- *
- * Returns null if not found or if there's view info root.
- */
- /* package */ CanvasViewInfo findViewInfoAt(int x, int y) {
- if (mLastValidViewInfoRoot == null) {
- return null;
- } else {
- return findViewInfoAt(x, y, mLastValidViewInfoRoot);
- }
- }
-
- /**
- * 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(int x, int y, CanvasViewInfo canvasViewInfo) {
- Rectangle r = canvasViewInfo.getSelectionRect();
- if (r.contains(x, y)) {
-
- // try to find a matching child first
- for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
- CanvasViewInfo v = findViewInfoAt(x, y, child);
- if (v != null) {
- return v;
- }
- }
-
- // if no children matched, this is the view that we're looking for
- return canvasViewInfo;
- }
-
- return null;
- }
-
- private ArrayList<CanvasViewInfo> findAltViewInfoAt(
- int x, int y, CanvasViewInfo parent, ArrayList<CanvasViewInfo> outList) {
- Rectangle r;
-
- if (outList == null) {
- outList = new ArrayList<CanvasViewInfo>();
-
- // add the parent root only once
- r = parent.getSelectionRect();
- if (r.contains(x, y)) {
- outList.add(parent);
- }
- }
-
- if (parent.getChildren().size() > 0) {
- // then add all children that match the position
- for (CanvasViewInfo child : parent.getChildren()) {
- r = child.getSelectionRect();
- if (r.contains(x, y)) {
- outList.add(child);
- }
- }
-
- // finally recurse in the children
- for (CanvasViewInfo child : parent.getChildren()) {
- r = child.getSelectionRect();
- if (r.contains(x, y)) {
- findAltViewInfoAt(x, y, child, outList);
- }
- }
- }
-
- return outList;
- }
-
- /**
- * Used by {@link #onSelectAll()} to add all current view infos to the selection list.
- *
- * @param canvasViewInfo The root to add. This info and all its children will be added to the
- * selection list.
- */
- private void selectAllViewInfos(CanvasViewInfo canvasViewInfo) {
- mSelections.add(new CanvasSelection(canvasViewInfo, mRulesEngine));
- for (CanvasViewInfo vi : canvasViewInfo.getChildren()) {
- selectAllViewInfos(vi);
- }
- }
-}
+/*
+ * 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.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
+import com.android.layoutlib.api.ILayoutResult;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+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.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.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.awt.image.Raster;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Displays the image rendered by the {@link GraphicalEditorPart} and handles
+ * the interaction with the widgets.
+ * <p/>
+ *
+ * @since GLE2
+ *
+ * TODO list:
+ * - gray on error, keep select but disable d'n'd.
+ * - make sure it is scrollable (Canvas derives from Scrollable, so prolly just setting bounds.)
+ * - handle drop target (from palette).
+ * - handle drag'n'drop (internal, for moving/duplicating).
+ * - handle context menu (depending on selection).
+ * - selection synchronization with the outline (both ways).
+ */
+/* package */ class LayoutCanvas extends Canvas {
+
+ /**
+ * Margin around the rendered image. Should be enough space to display the layout
+ * width and height pseudo widgets.
+ */
+ static final int IMAGE_MARGIN = 5;
+
+
+ /** The Groovy Rules Engine, associated with the current project. */
+ private RulesEngine mRulesEngine;
+
+ /*
+ * The last valid ILayoutResult passed to {@link #setResult(ILayoutResult)}.
+ * This can be null.
+ * When non null, {@link #mLastValidViewInfoRoot} is guaranteed to be non-null too.
+ */
+ private ILayoutResult mLastValidResult;
+
+ /**
+ * The CanvasViewInfo root created for the last update of {@link #mLastValidResult}.
+ * This is null when {@link #mLastValidResult} is null.
+ * When non null, {@link #mLastValidResult} is guaranteed to be non-null too.
+ */
+ private CanvasViewInfo mLastValidViewInfoRoot;
+
+ /**
+ * True when the last {@link #setResult(ILayoutResult)} provided a valid {@link ILayoutResult}
+ * in which case it is also available in {@link #mLastValidResult}.
+ * 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/>
+ * When this is false, {@link #mLastValidResult} can be non-null and points to an older
+ * layout result.
+ */
+ private boolean mIsResultValid;
+
+ /** Current background image. Null when there's no image. */
+ private Image mImage;
+
+ /** The current selection list. The list is never null, however it can be empty. */
+ private final LinkedList<CanvasSelection> mSelections = new LinkedList<CanvasSelection>();
+
+ /** CanvasSelection border color. Do not dispose, it's a system color. */
+ private Color mSelectionFgColor;
+
+ /** CanvasSelection name font. Do not dispose, it's a system font. */
+ private Font mSelectionFont;
+
+ /** Pixel height of the font displaying the selection name. Initially set to 0 and only
+ * initialized in onPaint() when we have a GC. */
+ private int mSelectionFontHeight;
+
+ /** Current hover view info. Null when no mouse hover. */
+ private CanvasViewInfo mHoverViewInfo;
+
+ /** Current mouse hover border rectangle. Null when there's no mouse hover. */
+ private Rectangle mHoverRect;
+
+ /** Hover border color. Must be disposed, it's NOT a system color. */
+ private Color mHoverFgColor;
+
+ /** Outline color. Do not dispose, it's a system color. */
+ private Color mOutlineColor;
+
+ /**
+ * 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;
+
+ /** When true, always display the outline of all views. */
+ private boolean mShowOutline;
+
+ /** Drop target associated with this composite. */
+ private DropTarget mDropTarget;
+
+ /** Drop listener, with feedback from current drop */
+ private CanvasDropListener mDropListener;
+
+ /** Drop color. Do not dispose, it's a system color. */
+ private Color mDropFgColor;
+
+
+ public LayoutCanvas(RulesEngine rulesEngine, Composite parent, int style) {
+ super(parent, style | SWT.DOUBLE_BUFFERED);
+ mRulesEngine = rulesEngine;
+
+ Display d = getDisplay();
+ mSelectionFgColor = d.getSystemColor(SWT.COLOR_RED);
+ mHoverFgColor = new Color(d, 0xFF, 0x99, 0x00); // orange
+ mOutlineColor = d.getSystemColor(SWT.COLOR_GREEN);
+ mSelectionFont = d.getSystemFont();
+ mDropFgColor = d.getSystemColor(SWT.COLOR_YELLOW);
+
+ addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ onPaint(e);
+ }
+ });
+
+ addMouseMoveListener(new MouseMoveListener() {
+ public void mouseMove(MouseEvent e) {
+ onMouseMove(e);
+ }
+ });
+
+ addMouseListener(new MouseListener() {
+ public void mouseUp(MouseEvent e) {
+ onMouseUp(e);
+ }
+
+ public void mouseDown(MouseEvent e) {
+ onMouseDown(e);
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ onDoubleClick(e);
+ }
+ });
+
+ mDropTarget = new DropTarget(this, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT);
+ mDropTarget.setTransfer(new Transfer[] { ElementDescTransfer.getInstance() });
+ mDropListener = new CanvasDropListener(this);
+ mDropTarget.addDropListener(mDropListener);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ if (mHoverFgColor != null) {
+ mHoverFgColor.dispose();
+ mHoverFgColor = null;
+ }
+
+ if (mDropTarget != null) {
+ mDropTarget.dispose();
+ mDropTarget = null;
+ }
+
+ if (mRulesEngine != null) {
+ mRulesEngine.dispose();
+ mRulesEngine = null;
+ }
+ }
+
+ /**
+ * Returns true when the last {@link #setResult(ILayoutResult)} provided a valid
+ * {@link ILayoutResult} in which case it is also available in {@link #mLastValidResult}.
+ * 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/>
+ * When this is false, {@link #mLastValidResult} can be non-null and points to an older
+ * layout result.
+ */
+ /* package */ boolean isResultValid() {
+ return mIsResultValid;
+ }
+
+ /** Returns the Groovy Rules Engine, associated with the current project. */
+ /* package */ RulesEngine getRulesEngine() {
+ return mRulesEngine;
+ }
+
+ /** Sets the Groovy Rules Engine, associated with the current project. */
+ /* package */ void setRulesEngine(RulesEngine rulesEngine) {
+ mRulesEngine = rulesEngine;
+ }
+
+ /**
+ * 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 result The new rendering result, either valid or not.
+ */
+ public void setResult(ILayoutResult result) {
+ // disable any hover
+ mHoverRect = null;
+
+ mIsResultValid = (result != null && result.getSuccess() == ILayoutResult.SUCCESS);
+
+ if (mIsResultValid && result != null) {
+ mLastValidResult = result;
+ mLastValidViewInfoRoot = new CanvasViewInfo(result.getRootView());
+ setImage(result.getImage());
+
+ // Check if the selection is still the same (based on the object keys)
+ // and eventually recompute their bounds.
+ for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
+ CanvasSelection s = it.next();
+
+ // Check the if the selected object still exists
+ Object key = s.getViewInfo().getUiViewKey();
+ CanvasViewInfo vi = findViewInfoKey(key, mLastValidViewInfoRoot);
+
+ // 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) {
+ it.add(new CanvasSelection(vi, mRulesEngine));
+ }
+ }
+
+ // remove the current alternate selection views
+ mAltSelection = null;
+ }
+
+ redraw();
+ }
+
+ public void setShowOutline(boolean newState) {
+ mShowOutline = newState;
+ redraw();
+ }
+
+ /**
+ * Called by the {@link GraphicalEditorPart} when the Copy action is requested.
+ *
+ * @param clipboard The shared clipboard. Must not be disposed.
+ */
+ public void onCopy(Clipboard clipboard) {
+ // TODO implement copy to clipbard. Also will need to provide feedback to enable
+ // copy only when there's a selection.
+ }
+
+ /**
+ * Called by the {@link GraphicalEditorPart} when the Cut action is requested.
+ *
+ * @param clipboard The shared clipboard. Must not be disposed.
+ */
+ public void onCut(Clipboard clipboard) {
+ // TODO implement copy to clipbard. Also will need to provide feedback to enable
+ // cut only when there's a selection.
+ }
+
+ /**
+ * Called by the {@link GraphicalEditorPart} when the Paste action is requested.
+ *
+ * @param clipboard The shared clipboard. Must not be disposed.
+ */
+ public void onPaste(Clipboard clipboard) {
+
+ }
+
+ /**
+ * Called by the {@link GraphicalEditorPart} when the Select All action is requested.
+ */
+ public void onSelectAll() {
+ // First clear the current selection, if any.
+ mSelections.clear();
+ mAltSelection = null;
+
+ // Now select everything if there's a valid layout
+ if (mIsResultValid && mLastValidResult != null) {
+ selectAllViewInfos(mLastValidViewInfoRoot);
+ redraw();
+ }
+ }
+
+ /**
+ * Delete action
+ */
+ public void onDelete() {
+ // TODO not implemented yet, not even hooked in yet!
+ }
+
+ //---
+
+ /**
+ * Sets the image of the last *successful* rendering.
+ * Converts the AWT image into an SWT image.
+ */
+ private void setImage(BufferedImage awtImage) {
+ int width = awtImage.getWidth();
+ int height = awtImage.getHeight();
+
+ Raster raster = awtImage.getData(new java.awt.Rectangle(width, height));
+ int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData();
+
+ ImageData imageData = new ImageData(width, height, 32,
+ new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
+
+ imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
+
+ mImage = new Image(getDisplay(), imageData);
+ }
+
+ /**
+ * Sets the alpha for the given GC.
+ * <p/>
+ * Alpha may not work on all platforms and may fail with an exception.
+ *
+ * @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;
+ }
+ }
+
+ /**
+ * Paints the canvas in response to paint events.
+ */
+ private void onPaint(PaintEvent e) {
+ GC gc = e.gc;
+
+ if (mImage != null) {
+ if (!mIsResultValid) {
+ gc_setAlpha(gc, 128); // half-transparent
+ }
+
+ gc.drawImage(mImage, IMAGE_MARGIN, IMAGE_MARGIN);
+
+ if (!mIsResultValid) {
+ gc_setAlpha(gc, 255); // opaque
+ }
+ }
+
+ if (mShowOutline) {
+ gc.setForeground(mOutlineColor);
+ gc.setLineStyle(SWT.LINE_DOT);
+ drawOutline(gc, mLastValidViewInfoRoot);
+ }
+
+ if (mHoverRect != null) {
+ gc.setForeground(mHoverFgColor);
+ gc.setLineStyle(SWT.LINE_DOT);
+ gc.drawRectangle(mHoverRect);
+ }
+
+ // initialize the selection font height once. We need the GC to do that.
+ if (mSelectionFontHeight == 0) {
+ gc.setFont(mSelectionFont);
+ FontMetrics fm = gc.getFontMetrics();
+ mSelectionFontHeight = fm.getHeight();
+ }
+
+ for (CanvasSelection s : mSelections) {
+ drawSelection(gc, s);
+ }
+
+ drawDropZones(gc);
+ }
+
+ private void drawOutline(GC gc, CanvasViewInfo info) {
+
+ Rectangle r = info.getAbsRect();
+ gc.drawRectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN, r.width, r.height);
+
+ for (CanvasViewInfo vi : info.getChildren()) {
+ drawOutline(gc, vi);
+ }
+ }
+
+ private void drawSelection(GC gc, CanvasSelection s) {
+ Rectangle r = s.getRect();
+
+ gc.setForeground(mSelectionFgColor);
+ gc.setLineStyle(SWT.LINE_SOLID);
+ gc.drawRectangle(s.getRect());
+
+ String name = s.getName();
+
+ if (name != null) {
+ int xs = r.x + 2;
+ int ys = r.y - mSelectionFontHeight;
+ if (ys < 0) {
+ ys = r.y + r.height;
+ }
+ gc.drawString(name, xs, ys, true /*transparent*/);
+ }
+ }
+
+ private void drawDropZones(GC gc) {
+ if (mDropListener == null) {
+ return;
+ }
+
+ CanvasViewInfo vi = mDropListener.getTargetView();
+ if (vi == null) {
+ return;
+ }
+
+ gc.setForeground(mDropFgColor);
+
+ ArrayList<DropZone> zones = mDropListener.getZones();
+ if (zones != null) {
+
+ gc.setLineStyle(SWT.LINE_SOLID);
+ gc.setLineWidth(1);
+
+ DropZone curr = mDropListener.getCurrentZone();
+
+ for (DropZone zone : zones) {
+ Rect r = zone.bounds;
+ if (r != null && r.w > 0 && r.h > 0) {
+ int x = r.x + IMAGE_MARGIN;
+ int y = r.y + IMAGE_MARGIN;
+
+ int alpha = 128; // half-transparent
+ if (zone == curr) {
+ alpha = 192;
+ }
+
+ if (gc_setAlpha(gc, alpha)) {
+ gc.fillRectangle(x, y, r.w, r.h);
+ gc_setAlpha(gc, 255); // opaque
+ }
+
+ gc.drawRectangle(x, y, r.w, r.h);
+ }
+ }
+
+ }
+
+ gc.setLineStyle(SWT.LINE_DOT);
+ gc.setLineWidth(3);
+ Rectangle r = vi.getAbsRect();
+ gc.drawRectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN, r.width, r.height);
+ gc.setLineWidth(1);
+ }
+
+ /**
+ * Hover on top of a known child.
+ */
+ private void onMouseMove(MouseEvent e) {
+ if (mLastValidResult != null) {
+ CanvasViewInfo root = mLastValidViewInfoRoot;
+ CanvasViewInfo vi = findViewInfoAt(e.x - IMAGE_MARGIN, e.y - IMAGE_MARGIN);
+
+ // We don't hover on the root since it's not a widget per see and it is always there.
+ if (vi == root) {
+ vi = null;
+ }
+
+ boolean needsUpdate = vi != mHoverViewInfo;
+ mHoverViewInfo = vi;
+
+ if (vi == null) {
+ mHoverRect = null;
+ } else {
+ Rectangle r = vi.getSelectionRect();
+ mHoverRect = new Rectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN,
+ r.width, r.height);
+ }
+
+ if (needsUpdate) {
+ redraw();
+ }
+ }
+ }
+
+ private void onMouseDown(MouseEvent e) {
+ // pass, not used yet.
+ }
+
+ /**
+ * Performs selection on mouse up (not mouse down).
+ * <p/>
+ * Shift key 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).
+ */
+ private void onMouseUp(MouseEvent e) {
+ if (mLastValidResult != null) {
+
+ boolean isShift = (e.stateMask & SWT.SHIFT) != 0;
+ boolean isAlt = (e.stateMask & SWT.ALT) != 0;
+
+ int x = e.x - IMAGE_MARGIN;
+ int y = e.y - IMAGE_MARGIN;
+ CanvasViewInfo vi = findViewInfoAt(x, y);
+
+ if (isShift && !isAlt) {
+ // 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)) {
+ redraw();
+ return;
+ }
+
+ // otherwise add it.
+ mSelections.add(new CanvasSelection(vi, mRulesEngine));
+ redraw();
+ }
+
+ } else if (isAlt) {
+ // 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, findAltViewInfoAt(
+ x, y, mLastValidViewInfoRoot, null));
+
+ // 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(new CanvasSelection(vi2, mRulesEngine));
+ }
+ } 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(new CanvasSelection(vi2, mRulesEngine));
+ }
+ }
+ redraw();
+
+ } else {
+ // Case where no modifier is pressed: either select or reset the selection.
+
+ // reset alternate selection if any
+ mAltSelection = null;
+
+ // reset (multi)selection if any
+ if (mSelections.size() > 0) {
+ if (mSelections.size() == 1 && mSelections.getFirst().getViewInfo() == vi) {
+ // CanvasSelection remains the same, don't touch it.
+ return;
+ }
+ mSelections.clear();
+ }
+
+ if (vi != null) {
+ mSelections.add(new CanvasSelection(vi, mRulesEngine));
+ }
+ redraw();
+ }
+ }
+ }
+
+ /** Deselects a view info. Returns true if the object was actually selected. */
+ private boolean deselect(CanvasViewInfo canvasViewInfo) {
+ if (canvasViewInfo == null) {
+ return false;
+ }
+
+ for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
+ CanvasSelection s = it.next();
+ if (canvasViewInfo == s.getViewInfo()) {
+ it.remove();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Deselects multiple view infos, */
+ private void deselectAll(List<CanvasViewInfo> canvasViewInfos) {
+ for (ListIterator<CanvasSelection> it = mSelections.listIterator(); it.hasNext(); ) {
+ CanvasSelection s = it.next();
+ if (canvasViewInfos.contains(s.getViewInfo())) {
+ it.remove();
+ }
+ }
+ }
+
+ private void onDoubleClick(MouseEvent e) {
+ // pass, not used yet.
+ }
+
+ /**
+ * Tries to find a child with the same view key in the view info sub-tree.
+ * Returns null if not found.
+ */
+ private CanvasViewInfo findViewInfoKey(Object viewKey, CanvasViewInfo canvasViewInfo) {
+ if (canvasViewInfo.getUiViewKey() == viewKey) {
+ return canvasViewInfo;
+ }
+
+ // try to find a matching child
+ for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
+ CanvasViewInfo v = findViewInfoKey(viewKey, child);
+ if (v != null) {
+ return v;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * 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.
+ *
+ * Returns null if not found or if there's view info root.
+ */
+ /* package */ CanvasViewInfo findViewInfoAt(int x, int y) {
+ if (mLastValidViewInfoRoot == null) {
+ return null;
+ } else {
+ return findViewInfoAt(x, y, mLastValidViewInfoRoot);
+ }
+ }
+
+ /**
+ * 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(int x, int y, CanvasViewInfo canvasViewInfo) {
+ Rectangle r = canvasViewInfo.getSelectionRect();
+ if (r.contains(x, y)) {
+
+ // try to find a matching child first
+ for (CanvasViewInfo child : canvasViewInfo.getChildren()) {
+ CanvasViewInfo v = findViewInfoAt(x, y, child);
+ if (v != null) {
+ return v;
+ }
+ }
+
+ // if no children matched, this is the view that we're looking for
+ return canvasViewInfo;
+ }
+
+ return null;
+ }
+
+ private ArrayList<CanvasViewInfo> findAltViewInfoAt(
+ int x, int y, CanvasViewInfo parent, ArrayList<CanvasViewInfo> outList) {
+ Rectangle r;
+
+ if (outList == null) {
+ outList = new ArrayList<CanvasViewInfo>();
+
+ // add the parent root only once
+ r = parent.getSelectionRect();
+ if (r.contains(x, y)) {
+ outList.add(parent);
+ }
+ }
+
+ if (parent.getChildren().size() > 0) {
+ // then add all children that match the position
+ for (CanvasViewInfo child : parent.getChildren()) {
+ r = child.getSelectionRect();
+ if (r.contains(x, y)) {
+ outList.add(child);
+ }
+ }
+
+ // finally recurse in the children
+ for (CanvasViewInfo child : parent.getChildren()) {
+ r = child.getSelectionRect();
+ if (r.contains(x, y)) {
+ findAltViewInfoAt(x, y, child, outList);
+ }
+ }
+ }
+
+ return outList;
+ }
+
+ /**
+ * Used by {@link #onSelectAll()} to add all current view infos to the selection list.
+ *
+ * @param canvasViewInfo The root to add. This info and all its children will be added to the
+ * selection list.
+ */
+ private void selectAllViewInfos(CanvasViewInfo canvasViewInfo) {
+ mSelections.add(new CanvasSelection(canvasViewInfo, mRulesEngine));
+ for (CanvasViewInfo vi : canvasViewInfo.getChildren()) {
+ selectAllViewInfos(vi);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
index e780595..7a64789 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java
@@ -1,358 +1,358 @@
-/*
- * 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.sdk.AndroidTargetData;
-
-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.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.graphics.Point;
-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.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.ScrollBar;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A palette composite 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/>
- *
- * @since GLE2
- *
- * 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 icons that depict the element and/or automatically rendered icons
- * based on a rendering of the widget.
- * - Optional: have context-sensitive tools items, e.g. selection arrow tool,
- * group selection tool, alignment, etc.
- * - Different view strategies: big icon, small icons, text vs no text, compact grid.
- * - This would only be useful with meaningful icons. Out current 1-letter icons are not enough
- * to get rid of text labels.
- */
-public class PaletteComposite extends Composite {
-
-
- /** The parent grid layout that contains all the {@link Toggle} and {@link Item} widgets. */
- private Composite mRoot;
-
- /**
- * Create the composite.
- * @param parent The parent composite.
- */
- public PaletteComposite(Composite parent) {
- super(parent, SWT.BORDER | SWT.V_SCROLL);
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- /**
- * Loads or reloads the palette elements by using the layout and view descriptors from the
- * given target data.
- *
- * @param targetData The target data that contains the descriptors. If null or empty,
- * no groups will be created.
- */
- public void reloadPalette(AndroidTargetData targetData) {
-
- for (Control c : getChildren()) {
- c.dispose();
- }
-
- setGridLayout(this, 2);
-
- mRoot = new Composite(this, SWT.NONE);
- setGridLayout(mRoot, 0);
-
- if (targetData != null) {
- addGroup(mRoot, "Views", targetData.getLayoutDescriptors().getViewDescriptors());
- addGroup(mRoot, "Layouts", targetData.getLayoutDescriptors().getLayoutDescriptors());
- }
-
- layout(true);
-
- final ScrollBar vbar = getVerticalBar();
-
- vbar.setMaximum(getSize().y);
-
- for (Listener listener : vbar.getListeners(SWT.Selection)) {
- vbar.removeListener(SWT.Selection, listener);
- }
-
- vbar.addListener(SWT.Selection, new Listener() {
- public void handleEvent(Event event) {
- Point p = mRoot.getLocation();
- p.y = - vbar.getSelection();
- mRoot.setLocation(p);
- }
- });
- }
-
- private void setGridLayout(Composite parent, int spacing) {
- GridLayout gl = new GridLayout(1, false);
- gl.horizontalSpacing = 0;
- gl.verticalSpacing = 0;
- gl.marginHeight = spacing;
- gl.marginBottom = spacing;
- gl.marginLeft = spacing;
- gl.marginRight = spacing;
- gl.marginTop = spacing;
- gl.marginBottom = spacing;
- parent.setLayout(gl);
- }
-
- private void addGroup(Composite parent,
- String uiName,
- List<ElementDescriptor> descriptors) {
-
- Composite group = new Composite(parent, SWT.NONE);
- setGridLayout(group, 0);
-
- Toggle toggle = new Toggle(group, uiName);
-
- for (ElementDescriptor desc : descriptors) {
- Item item = new Item(group, desc);
- toggle.addItem(item);
- GridData gd = new GridData();
- item.setLayoutData(gd);
- }
- }
-
- /**
- * A Toggle widget is a row that is the head of a group.
- * <p/>
- * When clicked, the toggle will show/hide all the {@link Item} widgets that have been
- * added to it using {@link #addItem(Item)}.
- */
- private static class Toggle extends CLabel implements MouseTrackListener, MouseListener {
- private boolean mMouseIn;
- private DragSource mSource;
- private ArrayList<Item> mItems = new ArrayList<Item>();
-
- public Toggle(Composite parent, String groupName) {
- super(parent, SWT.NONE);
- mMouseIn = false;
-
- setData(null);
-
- String s = String.format("-= %s =-", groupName);
- setText(s);
- setToolTipText(s);
- //TODO use triangle icon and swap it -- setImage(desc.getIcon());
- addMouseTrackListener(this);
- addMouseListener(this);
- }
-
- public void addItem(Item item) {
- mItems.add(item);
- }
-
- @Override
- public void dispose() {
- if (mSource != null) {
- mSource.dispose();
- mSource = null;
- }
- super.dispose();
- }
-
- @Override
- public int getStyle() {
- int style = super.getStyle();
- if (mMouseIn) {
- style |= SWT.SHADOW_IN;
- }
- return style;
- }
-
- // -- MouseTrackListener callbacks
-
- public void mouseEnter(MouseEvent e) {
- if (!mMouseIn) {
- mMouseIn = true;
- redraw();
- }
- }
-
- public void mouseExit(MouseEvent e) {
- if (mMouseIn) {
- mMouseIn = false;
- redraw();
- }
- }
-
- public void mouseHover(MouseEvent e) {
- // pass
- }
-
- // -- MouseListener callbacks
-
- public void mouseDoubleClick(MouseEvent arg0) {
- // pass
- }
-
- public void mouseDown(MouseEvent arg0) {
- // pass
- }
-
- public void mouseUp(MouseEvent arg0) {
- for (Item i : mItems) {
- if (i.isVisible()) {
- Object ld = i.getLayoutData();
- if (ld instanceof GridData) {
- GridData gd = (GridData) ld;
-
- i.setData(gd.heightHint != SWT.DEFAULT ?
- Integer.valueOf(gd.heightHint) :
- null);
- gd.heightHint = 0;
- }
- } else {
- Object ld = i.getLayoutData();
- if (ld instanceof GridData) {
- GridData gd = (GridData) ld;
-
- Object d = i.getData();
- if (d instanceof Integer) {
- gd.heightHint = ((Integer) d).intValue();
- } else {
- gd.heightHint = SWT.DEFAULT;
- }
- }
- }
- i.setVisible(!i.isVisible());
- }
-
- getParent().getParent().layout(true /*changed*/);
- }
- }
-
- /**
- * An Item widget represents one {@link ElementDescriptor} that can be dropped on the
- * GLE2 canvas using drag'n'drop.
- */
- private static class Item extends CLabel implements MouseTrackListener {
-
- private boolean mMouseIn;
- private DragSource mSource;
-
- public Item(Composite parent, ElementDescriptor desc) {
- super(parent, SWT.NONE);
- mMouseIn = false;
-
- setText(desc.getUiName());
- setImage(desc.getIcon());
- setToolTipText(desc.getTooltip());
- addMouseTrackListener(this);
-
- // DND Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
- mSource = new DragSource(this, DND.DROP_COPY);
- mSource.setTransfer(new Transfer[] { ElementDescTransfer.getInstance() });
- mSource.addDragListener(new DescDragSourceListener(desc));
- }
-
- @Override
- public void dispose() {
- if (mSource != null) {
- mSource.dispose();
- mSource = null;
- }
- super.dispose();
- }
-
- @Override
- public int getStyle() {
- int style = super.getStyle();
- if (mMouseIn) {
- style |= SWT.SHADOW_IN;
- }
- return style;
- }
-
- public void mouseEnter(MouseEvent e) {
- if (!mMouseIn) {
- mMouseIn = true;
- redraw();
- }
- }
-
- public void mouseExit(MouseEvent e) {
- if (mMouseIn) {
- mMouseIn = false;
- redraw();
- }
- }
-
- public void mouseHover(MouseEvent e) {
- // pass
- }
- }
-
- /**
- * A {@link DragSourceListener} that deals with drag'n'drop of
- * {@link ElementDescriptor}s.
- */
- private static class DescDragSourceListener implements DragSourceListener {
-
- private final ElementDescriptor mDesc;
-
- public DescDragSourceListener(ElementDescriptor desc) {
- mDesc = desc;
- }
-
- public void dragStart(DragSourceEvent e) {
- if (mDesc == null) {
- e.doit = false;
- }
- }
-
-
- public void dragSetData(DragSourceEvent e) {
- // Provide the data for the drop when requested by the other side.
- if (ElementDescTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = mDesc;
- }
- }
-
- public void dragFinished(DragSourceEvent e) {
- // Nothing to do here.
- }
- }
-
-}
+/*
+ * 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.sdk.AndroidTargetData;
+
+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.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Point;
+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.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ScrollBar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A palette composite 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/>
+ *
+ * @since GLE2
+ *
+ * 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 icons that depict the element and/or automatically rendered icons
+ * based on a rendering of the widget.
+ * - Optional: have context-sensitive tools items, e.g. selection arrow tool,
+ * group selection tool, alignment, etc.
+ * - Different view strategies: big icon, small icons, text vs no text, compact grid.
+ * - This would only be useful with meaningful icons. Out current 1-letter icons are not enough
+ * to get rid of text labels.
+ */
+public class PaletteComposite extends Composite {
+
+
+ /** The parent grid layout that contains all the {@link Toggle} and {@link Item} widgets. */
+ private Composite mRoot;
+
+ /**
+ * Create the composite.
+ * @param parent The parent composite.
+ */
+ public PaletteComposite(Composite parent) {
+ super(parent, SWT.BORDER | SWT.V_SCROLL);
+ }
+
+ @Override
+ protected void checkSubclass() {
+ // Disable the check that prevents subclassing of SWT components
+ }
+
+ /**
+ * Loads or reloads the palette elements by using the layout and view descriptors from the
+ * given target data.
+ *
+ * @param targetData The target data that contains the descriptors. If null or empty,
+ * no groups will be created.
+ */
+ public void reloadPalette(AndroidTargetData targetData) {
+
+ for (Control c : getChildren()) {
+ c.dispose();
+ }
+
+ setGridLayout(this, 2);
+
+ mRoot = new Composite(this, SWT.NONE);
+ setGridLayout(mRoot, 0);
+
+ if (targetData != null) {
+ addGroup(mRoot, "Views", targetData.getLayoutDescriptors().getViewDescriptors());
+ addGroup(mRoot, "Layouts", targetData.getLayoutDescriptors().getLayoutDescriptors());
+ }
+
+ layout(true);
+
+ final ScrollBar vbar = getVerticalBar();
+
+ vbar.setMaximum(getSize().y);
+
+ for (Listener listener : vbar.getListeners(SWT.Selection)) {
+ vbar.removeListener(SWT.Selection, listener);
+ }
+
+ vbar.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ Point p = mRoot.getLocation();
+ p.y = - vbar.getSelection();
+ mRoot.setLocation(p);
+ }
+ });
+ }
+
+ private void setGridLayout(Composite parent, int spacing) {
+ GridLayout gl = new GridLayout(1, false);
+ gl.horizontalSpacing = 0;
+ gl.verticalSpacing = 0;
+ gl.marginHeight = spacing;
+ gl.marginBottom = spacing;
+ gl.marginLeft = spacing;
+ gl.marginRight = spacing;
+ gl.marginTop = spacing;
+ gl.marginBottom = spacing;
+ parent.setLayout(gl);
+ }
+
+ private void addGroup(Composite parent,
+ String uiName,
+ List<ElementDescriptor> descriptors) {
+
+ Composite group = new Composite(parent, SWT.NONE);
+ setGridLayout(group, 0);
+
+ Toggle toggle = new Toggle(group, uiName);
+
+ for (ElementDescriptor desc : descriptors) {
+ Item item = new Item(group, desc);
+ toggle.addItem(item);
+ GridData gd = new GridData();
+ item.setLayoutData(gd);
+ }
+ }
+
+ /**
+ * A Toggle widget is a row that is the head of a group.
+ * <p/>
+ * When clicked, the toggle will show/hide all the {@link Item} widgets that have been
+ * added to it using {@link #addItem(Item)}.
+ */
+ private static class Toggle extends CLabel implements MouseTrackListener, MouseListener {
+ private boolean mMouseIn;
+ private DragSource mSource;
+ private ArrayList<Item> mItems = new ArrayList<Item>();
+
+ public Toggle(Composite parent, String groupName) {
+ super(parent, SWT.NONE);
+ mMouseIn = false;
+
+ setData(null);
+
+ String s = String.format("-= %s =-", groupName);
+ setText(s);
+ setToolTipText(s);
+ //TODO use triangle icon and swap it -- setImage(desc.getIcon());
+ addMouseTrackListener(this);
+ addMouseListener(this);
+ }
+
+ public void addItem(Item item) {
+ mItems.add(item);
+ }
+
+ @Override
+ public void dispose() {
+ if (mSource != null) {
+ mSource.dispose();
+ mSource = null;
+ }
+ super.dispose();
+ }
+
+ @Override
+ public int getStyle() {
+ int style = super.getStyle();
+ if (mMouseIn) {
+ style |= SWT.SHADOW_IN;
+ }
+ return style;
+ }
+
+ // -- MouseTrackListener callbacks
+
+ public void mouseEnter(MouseEvent e) {
+ if (!mMouseIn) {
+ mMouseIn = true;
+ redraw();
+ }
+ }
+
+ public void mouseExit(MouseEvent e) {
+ if (mMouseIn) {
+ mMouseIn = false;
+ redraw();
+ }
+ }
+
+ public void mouseHover(MouseEvent e) {
+ // pass
+ }
+
+ // -- MouseListener callbacks
+
+ public void mouseDoubleClick(MouseEvent arg0) {
+ // pass
+ }
+
+ public void mouseDown(MouseEvent arg0) {
+ // pass
+ }
+
+ public void mouseUp(MouseEvent arg0) {
+ for (Item i : mItems) {
+ if (i.isVisible()) {
+ Object ld = i.getLayoutData();
+ if (ld instanceof GridData) {
+ GridData gd = (GridData) ld;
+
+ i.setData(gd.heightHint != SWT.DEFAULT ?
+ Integer.valueOf(gd.heightHint) :
+ null);
+ gd.heightHint = 0;
+ }
+ } else {
+ Object ld = i.getLayoutData();
+ if (ld instanceof GridData) {
+ GridData gd = (GridData) ld;
+
+ Object d = i.getData();
+ if (d instanceof Integer) {
+ gd.heightHint = ((Integer) d).intValue();
+ } else {
+ gd.heightHint = SWT.DEFAULT;
+ }
+ }
+ }
+ i.setVisible(!i.isVisible());
+ }
+
+ getParent().getParent().layout(true /*changed*/);
+ }
+ }
+
+ /**
+ * An Item widget represents one {@link ElementDescriptor} that can be dropped on the
+ * GLE2 canvas using drag'n'drop.
+ */
+ private static class Item extends CLabel implements MouseTrackListener {
+
+ private boolean mMouseIn;
+ private DragSource mSource;
+
+ public Item(Composite parent, ElementDescriptor desc) {
+ super(parent, SWT.NONE);
+ mMouseIn = false;
+
+ setText(desc.getUiName());
+ setImage(desc.getIcon());
+ setToolTipText(desc.getTooltip());
+ addMouseTrackListener(this);
+
+ // DND Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
+ mSource = new DragSource(this, DND.DROP_COPY);
+ mSource.setTransfer(new Transfer[] { ElementDescTransfer.getInstance() });
+ mSource.addDragListener(new DescDragSourceListener(desc));
+ }
+
+ @Override
+ public void dispose() {
+ if (mSource != null) {
+ mSource.dispose();
+ mSource = null;
+ }
+ super.dispose();
+ }
+
+ @Override
+ public int getStyle() {
+ int style = super.getStyle();
+ if (mMouseIn) {
+ style |= SWT.SHADOW_IN;
+ }
+ return style;
+ }
+
+ public void mouseEnter(MouseEvent e) {
+ if (!mMouseIn) {
+ mMouseIn = true;
+ redraw();
+ }
+ }
+
+ public void mouseExit(MouseEvent e) {
+ if (mMouseIn) {
+ mMouseIn = false;
+ redraw();
+ }
+ }
+
+ public void mouseHover(MouseEvent e) {
+ // pass
+ }
+ }
+
+ /**
+ * A {@link DragSourceListener} that deals with drag'n'drop of
+ * {@link ElementDescriptor}s.
+ */
+ private static class DescDragSourceListener implements DragSourceListener {
+
+ private final ElementDescriptor mDesc;
+
+ public DescDragSourceListener(ElementDescriptor desc) {
+ mDesc = desc;
+ }
+
+ public void dragStart(DragSourceEvent e) {
+ if (mDesc == null) {
+ e.doit = false;
+ }
+ }
+
+
+ public void dragSetData(DragSourceEvent e) {
+ // Provide the data for the drop when requested by the other side.
+ if (ElementDescTransfer.getInstance().isSupportedType(e.dataType)) {
+ e.data = mDesc;
+ }
+ }
+
+ public void dragFinished(DragSourceEvent e) {
+ // Nothing to do here.
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
index d639fb9..fd0f475 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
@@ -17,8 +17,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gre;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.Rect;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Rect;
import com.android.ide.eclipse.adt.internal.editors.AndroidEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
index 48986b6..0149096 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
@@ -18,10 +18,10 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.gscripts.DropZone;
-import com.android.ide.eclipse.adt.gscripts.INodeProxy;
-import com.android.ide.eclipse.adt.gscripts.IViewRule;
-import com.android.ide.eclipse.adt.gscripts.Point;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.INodeProxy;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.IViewRule;
+import com.android.ide.eclipse.adt.editors.layout.gscripts.Point;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;