aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java304
1 files changed, 304 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java
new file mode 100644
index 000000000..f7085fc12
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/FragmentMenu.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+
+import static com.android.SdkConstants.ANDROID_LAYOUT_RESOURCE_PREFIX;
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_CLASS;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX;
+import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutMetadata.KEY_FRAGMENT_LAYOUT;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
+import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
+import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
+import com.android.resources.ResourceType;
+import com.android.utils.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Menu;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment context menu allowing a layout to be chosen for previewing in the fragment frame.
+ */
+public class FragmentMenu extends SubmenuAction {
+ private static final String R_LAYOUT_RESOURCE_PREFIX = "R.layout."; //$NON-NLS-1$
+ private static final String ANDROID_R_PREFIX = "android.R.layout"; //$NON-NLS-1$
+
+ /** Associated canvas */
+ private final LayoutCanvas mCanvas;
+
+ /**
+ * Creates a "Preview Fragment" menu
+ *
+ * @param canvas associated canvas
+ */
+ public FragmentMenu(LayoutCanvas canvas) {
+ super("Fragment Layout");
+ mCanvas = canvas;
+ }
+
+ @Override
+ protected void addMenuItems(Menu menu) {
+ IAction action = new PickLayoutAction("Choose Layout...");
+ new ActionContributionItem(action).fill(menu, -1);
+
+ SelectionManager selectionManager = mCanvas.getSelectionManager();
+ List<SelectionItem> selections = selectionManager.getSelections();
+ if (selections.size() == 0) {
+ return;
+ }
+
+ SelectionItem first = selections.get(0);
+ UiViewElementNode node = first.getViewInfo().getUiViewNode();
+ if (node == null) {
+ return;
+ }
+ Element element = (Element) node.getXmlNode();
+
+ String selected = getSelectedLayout();
+ if (selected != null) {
+ if (selected.startsWith(ANDROID_LAYOUT_RESOURCE_PREFIX)) {
+ selected = selected.substring(ANDROID_LAYOUT_RESOURCE_PREFIX.length());
+ }
+ }
+
+ String fqcn = getFragmentClass(element);
+ if (fqcn != null) {
+ // Look up the corresponding activity class and try to figure out
+ // which layouts it is referring to and list these here as reasonable
+ // guesses
+ IProject project = mCanvas.getEditorDelegate().getEditor().getProject();
+ String source = null;
+ try {
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
+ IType type = javaProject.findType(fqcn);
+ if (type != null) {
+ source = type.getSource();
+ }
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+ // Find layouts. This is based on just skimming the Fragment class and looking
+ // for layout references of the form R.layout.*.
+ if (source != null) {
+ String self = mCanvas.getLayoutResourceName();
+ // Pair of <title,layout> to be displayed to the user
+ List<Pair<String, String>> layouts = new ArrayList<Pair<String, String>>();
+
+ if (source.contains("extends ListFragment")) { //$NON-NLS-1$
+ layouts.add(Pair.of("list_content", //$NON-NLS-1$
+ "@android:layout/list_content")); //$NON-NLS-1$
+ }
+
+ int index = 0;
+ while (true) {
+ index = source.indexOf(R_LAYOUT_RESOURCE_PREFIX, index);
+ if (index == -1) {
+ break;
+ } else {
+ index += R_LAYOUT_RESOURCE_PREFIX.length();
+ int end = index;
+ while (end < source.length()) {
+ char c = source.charAt(end);
+ if (!Character.isJavaIdentifierPart(c)) {
+ break;
+ }
+ end++;
+ }
+ if (end > index) {
+ String title = source.substring(index, end);
+ String layout;
+ // Is this R.layout part of an android.R.layout?
+ int len = ANDROID_R_PREFIX.length() + 1; // prefix length to check
+ if (index > len && source.startsWith(ANDROID_R_PREFIX, index - len)) {
+ layout = ANDROID_LAYOUT_RESOURCE_PREFIX + title;
+ } else {
+ layout = LAYOUT_RESOURCE_PREFIX + title;
+ }
+ if (!self.equals(title)) {
+ layouts.add(Pair.of(title, layout));
+ }
+ }
+ }
+
+ index++;
+ }
+
+ if (layouts.size() > 0) {
+ new Separator().fill(menu, -1);
+ for (Pair<String, String> layout : layouts) {
+ action = new SetFragmentLayoutAction(layout.getFirst(),
+ layout.getSecond(), selected);
+ new ActionContributionItem(action).fill(menu, -1);
+ }
+ }
+ }
+ }
+
+ if (selected != null) {
+ new Separator().fill(menu, -1);
+ action = new SetFragmentLayoutAction("Clear", null, null);
+ new ActionContributionItem(action).fill(menu, -1);
+ }
+ }
+
+ /**
+ * Returns the class name of the fragment associated with the given {@code <fragment>}
+ * element.
+ *
+ * @param element the element for the fragment tag
+ * @return the fully qualified fragment class name, or null
+ */
+ @Nullable
+ public static String getFragmentClass(@NonNull Element element) {
+ String fqcn = element.getAttribute(ATTR_CLASS);
+ if (fqcn == null || fqcn.length() == 0) {
+ fqcn = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ }
+ if (fqcn != null && fqcn.length() > 0) {
+ return fqcn;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the layout to be shown for the given {@code <fragment>} node.
+ *
+ * @param node the node corresponding to the {@code <fragment>} element
+ * @return the resource path to a layout to render for this fragment, or null
+ */
+ @Nullable
+ public static String getFragmentLayout(@NonNull Node node) {
+ String layout = LayoutMetadata.getProperty(
+ node, LayoutMetadata.KEY_FRAGMENT_LAYOUT);
+ if (layout != null) {
+ return layout;
+ }
+
+ return null;
+ }
+
+ /** Returns the name of the currently displayed layout in the fragment, or null */
+ @Nullable
+ private String getSelectedLayout() {
+ SelectionManager selectionManager = mCanvas.getSelectionManager();
+ for (SelectionItem item : selectionManager.getSelections()) {
+ UiViewElementNode node = item.getViewInfo().getUiViewNode();
+ if (node != null) {
+ String layout = getFragmentLayout(node.getXmlNode());
+ if (layout != null) {
+ return layout;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Set the given layout as the new fragment layout
+ *
+ * @param layout the layout resource name to show in this fragment
+ */
+ public void setNewLayout(@Nullable String layout) {
+ LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
+ GraphicalEditorPart graphicalEditor = delegate.getGraphicalEditor();
+ SelectionManager selectionManager = mCanvas.getSelectionManager();
+
+ for (SelectionItem item : selectionManager.getSnapshot()) {
+ UiViewElementNode node = item.getViewInfo().getUiViewNode();
+ if (node != null) {
+ Node xmlNode = node.getXmlNode();
+ LayoutMetadata.setProperty(delegate.getEditor(), xmlNode, KEY_FRAGMENT_LAYOUT,
+ layout);
+ }
+ }
+
+ // Refresh
+ graphicalEditor.recomputeLayout();
+ mCanvas.redraw();
+ }
+
+ /** Action to set the given layout as the new layout in a fragment */
+ private class SetFragmentLayoutAction extends Action {
+ private final String mLayout;
+
+ public SetFragmentLayoutAction(String title, String layout, String selected) {
+ super(title, IAction.AS_RADIO_BUTTON);
+ mLayout = layout;
+
+ if (layout != null && layout.equals(selected)) {
+ setChecked(true);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (isChecked()) {
+ setNewLayout(mLayout);
+ }
+ }
+ }
+
+ /**
+ * Action which brings up the "Create new XML File" wizard, pre-selected with the
+ * animation category
+ */
+ private class PickLayoutAction extends Action {
+
+ public PickLayoutAction(String title) {
+ super(title, IAction.AS_PUSH_BUTTON);
+ }
+
+ @Override
+ public void run() {
+ LayoutEditorDelegate delegate = mCanvas.getEditorDelegate();
+ IFile file = delegate.getEditor().getInputFile();
+ GraphicalEditorPart editor = delegate.getGraphicalEditor();
+ ResourceChooser dlg = ResourceChooser.create(editor, ResourceType.LAYOUT)
+ .setInputValidator(CyclicDependencyValidator.create(file))
+ .setInitialSize(85, 10)
+ .setCurrentResource(getSelectedLayout());
+ int result = dlg.open();
+ if (result == ResourceChooser.CLEAR_RETURN_CODE) {
+ setNewLayout(null);
+ } else if (result == Window.OK) {
+ String newType = dlg.getCurrentResource();
+ setNewLayout(newType);
+ }
+ }
+ }
+}