aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.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/PaletteControl.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java1265
1 files changed, 0 insertions, 1265 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
deleted file mode 100644
index 46168b70f..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.VALUE_WRAP_CONTENT;
-import static com.android.SdkConstants.XMLNS_ANDROID;
-import static com.android.SdkConstants.XMLNS_URI;
-
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction.Toggle;
-import com.android.ide.common.rendering.LayoutLibrary;
-import com.android.ide.common.rendering.api.Capability;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.IconFactory;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
-import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.CustomViewDescriptorService;
-import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
-import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
-import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
-import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
-import com.android.utils.Pair;
-
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.IAction;
-import org.eclipse.jface.action.IToolBarManager;
-import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.action.Separator;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CLabel;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DragSource;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.MenuDetectEvent;
-import org.eclipse.swt.events.MenuDetectListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseTrackListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.RGB;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-import org.eclipse.wb.internal.core.editor.structure.IPage;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A palette control for the {@link GraphicalEditorPart}.
- * <p/>
- * The palette contains several groups, each with a UI name (e.g. layouts and views) and each
- * with a list of element descriptors.
- * <p/>
- *
- * TODO list:
- * - The available items should depend on the actual GLE2 Canvas selection. Selected android
- * views should force filtering on what they accept can be dropped on them (e.g. TabHost,
- * TableLayout). Should enable/disable them, not hide them, to avoid shuffling around.
- * - Optional: a text filter
- * - Optional: have context-sensitive tools items, e.g. selection arrow tool,
- * group selection tool, alignment, etc.
- */
-public class PaletteControl extends Composite {
-
- /**
- * Wrapper to create a {@link PaletteControl}
- */
- static class PalettePage implements IPage {
- private final GraphicalEditorPart mEditorPart;
- private PaletteControl mControl;
-
- PalettePage(GraphicalEditorPart editor) {
- mEditorPart = editor;
- }
-
- @Override
- public void createControl(Composite parent) {
- mControl = new PaletteControl(parent, mEditorPart);
- }
-
- @Override
- public Control getControl() {
- return mControl;
- }
-
- @Override
- public void dispose() {
- mControl.dispose();
- }
-
- @Override
- public void setToolBar(IToolBarManager toolBarManager) {
- }
-
- /**
- * Add tool bar items to the given toolbar
- *
- * @param toolbar the toolbar to add items into
- */
- void createToolbarItems(final ToolBar toolbar) {
- final ToolItem popupMenuItem = new ToolItem(toolbar, SWT.PUSH);
- popupMenuItem.setToolTipText("View Menu");
- popupMenuItem.setImage(IconFactory.getInstance().getIcon("view_menu"));
- popupMenuItem.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- Rectangle bounds = popupMenuItem.getBounds();
- // Align menu horizontally with the toolbar button and
- // vertically with the bottom of the toolbar
- Point point = toolbar.toDisplay(bounds.x, bounds.y + bounds.height);
- mControl.showMenu(point.x, point.y);
- }
- });
- }
-
- @Override
- public void setFocus() {
- mControl.setFocus();
- }
- }
-
- /**
- * The parent grid layout that contains all the {@link Toggle} and
- * {@link IconTextItem} widgets.
- */
- private GraphicalEditorPart mEditor;
- private Color mBackground;
- private Color mForeground;
-
- /** The palette modes control various ways to visualize and lay out the views */
- private static enum PaletteMode {
- /** Show rendered previews of the views */
- PREVIEW("Show Previews", true),
- /** Show rendered previews of the views, scaled down to 75% */
- SMALL_PREVIEW("Show Small Previews", true),
- /** Show rendered previews of the views, scaled down to 50% */
- TINY_PREVIEW("Show Tiny Previews", true),
- /** Show an icon + text label */
- ICON_TEXT("Show Icon and Text", false),
- /** Show only icons, packed multiple per row */
- ICON_ONLY("Show Only Icons", true);
-
- PaletteMode(String actionLabel, boolean wrap) {
- mActionLabel = actionLabel;
- mWrap = wrap;
- }
-
- public String getActionLabel() {
- return mActionLabel;
- }
-
- public boolean getWrap() {
- return mWrap;
- }
-
- public boolean isPreview() {
- return this == PREVIEW || this == SMALL_PREVIEW || this == TINY_PREVIEW;
- }
-
- public boolean isScaledPreview() {
- return this == SMALL_PREVIEW || this == TINY_PREVIEW;
- }
-
- private final String mActionLabel;
- private final boolean mWrap;
- };
-
- /** Token used in preference string to record alphabetical sorting */
- private static final String VALUE_ALPHABETICAL = "alpha"; //$NON-NLS-1$
- /** Token used in preference string to record categories being turned off */
- private static final String VALUE_NO_CATEGORIES = "nocat"; //$NON-NLS-1$
- /** Token used in preference string to record auto close being turned off */
- private static final String VALUE_NO_AUTOCLOSE = "noauto"; //$NON-NLS-1$
-
- private final PreviewIconFactory mPreviewIconFactory = new PreviewIconFactory(this);
- private PaletteMode mPaletteMode = null;
- /** Use alphabetical sorting instead of natural order? */
- private boolean mAlphabetical;
- /** Use categories instead of a single large list of views? */
- private boolean mCategories = true;
- /** Auto-close the previous category when new categories are opened */
- private boolean mAutoClose = true;
- private AccordionControl mAccordion;
- private String mCurrentTheme;
- private String mCurrentDevice;
- private IAndroidTarget mCurrentTarget;
- private AndroidTargetData mCurrentTargetData;
-
- /**
- * Create the composite.
- * @param parent The parent composite.
- * @param editor An editor associated with this palette.
- */
- public PaletteControl(Composite parent, GraphicalEditorPart editor) {
- super(parent, SWT.NONE);
-
- mEditor = editor;
- }
-
- /** Reads UI mode from persistent store to preserve palette mode across IDE sessions */
- private void loadPaletteMode() {
- String paletteModes = AdtPrefs.getPrefs().getPaletteModes();
- if (paletteModes.length() > 0) {
- String[] tokens = paletteModes.split(","); //$NON-NLS-1$
- try {
- mPaletteMode = PaletteMode.valueOf(tokens[0]);
- } catch (Throwable t) {
- mPaletteMode = PaletteMode.values()[0];
- }
- mAlphabetical = paletteModes.contains(VALUE_ALPHABETICAL);
- mCategories = !paletteModes.contains(VALUE_NO_CATEGORIES);
- mAutoClose = !paletteModes.contains(VALUE_NO_AUTOCLOSE);
- } else {
- mPaletteMode = PaletteMode.SMALL_PREVIEW;
- }
- }
-
- /**
- * Returns the most recently stored version of auto-close-mode; this is the last
- * user-initiated setting of the auto-close mode (we programmatically switch modes when
- * you enter icons-only mode, and set it back to this when going to any other mode)
- */
- private boolean getSavedAutoCloseMode() {
- return !AdtPrefs.getPrefs().getPaletteModes().contains(VALUE_NO_AUTOCLOSE);
- }
-
- /** Saves UI mode to persistent store to preserve palette mode across IDE sessions */
- private void savePaletteMode() {
- StringBuilder sb = new StringBuilder();
- sb.append(mPaletteMode);
- if (mAlphabetical) {
- sb.append(',').append(VALUE_ALPHABETICAL);
- }
- if (!mCategories) {
- sb.append(',').append(VALUE_NO_CATEGORIES);
- }
- if (!mAutoClose) {
- sb.append(',').append(VALUE_NO_AUTOCLOSE);
- }
- AdtPrefs.getPrefs().setPaletteModes(sb.toString());
- }
-
- private void refreshPalette() {
- IAndroidTarget oldTarget = mCurrentTarget;
- mCurrentTarget = null;
- mCurrentTargetData = null;
- mCurrentTheme = null;
- mCurrentDevice = null;
- reloadPalette(oldTarget);
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- @Override
- public void dispose() {
- if (mBackground != null) {
- mBackground.dispose();
- mBackground = null;
- }
- if (mForeground != null) {
- mForeground.dispose();
- mForeground = null;
- }
-
- super.dispose();
- }
-
- /**
- * Returns the currently displayed target
- *
- * @return the current target, or null
- */
- public IAndroidTarget getCurrentTarget() {
- return mCurrentTarget;
- }
-
- /**
- * Returns the currently displayed theme (in palette modes that support previewing)
- *
- * @return the current theme, or null
- */
- public String getCurrentTheme() {
- return mCurrentTheme;
- }
-
- /**
- * Returns the currently displayed device (in palette modes that support previewing)
- *
- * @return the current device, or null
- */
- public String getCurrentDevice() {
- return mCurrentDevice;
- }
-
- /** Returns true if previews in the palette should be made available */
- private boolean previewsAvailable() {
- // Not layoutlib 5 -- we require custom background support to do
- // a decent job with previews
- LayoutLibrary layoutLibrary = mEditor.getLayoutLibrary();
- return layoutLibrary != null && layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR);
- }
-
- /**
- * Loads or reloads the palette elements by using the layout and view descriptors from the
- * given target data.
- *
- * @param target The target that has just been loaded
- */
- public void reloadPalette(IAndroidTarget target) {
- ConfigurationChooser configChooser = mEditor.getConfigurationChooser();
- String theme = configChooser.getThemeName();
- String device = configChooser.getDeviceName();
- if (device == null) {
- return;
- }
- AndroidTargetData targetData =
- target != null ? Sdk.getCurrent().getTargetData(target) : null;
- if (target == mCurrentTarget && targetData == mCurrentTargetData
- && mCurrentTheme != null && mCurrentTheme.equals(theme)
- && mCurrentDevice != null && mCurrentDevice.equals(device)) {
- return;
- }
- mCurrentTheme = theme;
- mCurrentTarget = target;
- mCurrentTargetData = targetData;
- mCurrentDevice = device;
- mPreviewIconFactory.reset();
-
- if (targetData == null) {
- return;
- }
-
- Set<String> expandedCategories = null;
- if (mAccordion != null) {
- expandedCategories = mAccordion.getExpandedCategories();
- // We auto-expand all categories when showing icons-only. When returning to some
- // other mode we don't want to retain all categories open.
- if (expandedCategories.size() > 3) {
- expandedCategories = null;
- }
- }
-
- // Erase old content and recreate new
- for (Control c : getChildren()) {
- c.dispose();
- }
-
- if (mPaletteMode == null) {
- loadPaletteMode();
- assert mPaletteMode != null;
- }
-
- // Ensure that the palette mode is supported on this version of the layout library
- if (!previewsAvailable()) {
- if (mPaletteMode.isPreview()) {
- mPaletteMode = PaletteMode.ICON_TEXT;
- }
- }
-
- if (mPaletteMode.isPreview()) {
- if (mForeground != null) {
- mForeground.dispose();
- mForeground = null;
- }
- if (mBackground != null) {
- mBackground.dispose();
- mBackground = null;
- }
- RGB background = mPreviewIconFactory.getBackgroundColor();
- if (background != null) {
- mBackground = new Color(getDisplay(), background);
- }
- RGB foreground = mPreviewIconFactory.getForegroundColor();
- if (foreground != null) {
- mForeground = new Color(getDisplay(), foreground);
- }
- }
-
- List<String> headers = Collections.emptyList();
- final Map<String, List<ViewElementDescriptor>> categoryToItems;
- categoryToItems = new HashMap<String, List<ViewElementDescriptor>>();
- headers = new ArrayList<String>();
- List<Pair<String,List<ViewElementDescriptor>>> paletteEntries =
- ViewMetadataRepository.get().getPaletteEntries(targetData,
- mAlphabetical, mCategories);
- for (Pair<String,List<ViewElementDescriptor>> pair : paletteEntries) {
- String category = pair.getFirst();
- List<ViewElementDescriptor> categoryItems = pair.getSecond();
- headers.add(category);
- categoryToItems.put(category, categoryItems);
- }
-
- headers.add("Custom & Library Views");
-
- // Set the categories to expand the first item if
- // (1) we don't have a previously selected category, or
- // (2) there's just one category anyway, or
- // (3) the set of categories have changed so our previously selected category
- // doesn't exist anymore (can happen when you toggle "Show Categories")
- if ((expandedCategories == null && headers.size() > 0) || headers.size() == 1 ||
- (expandedCategories != null && expandedCategories.size() >= 1
- && !headers.contains(
- expandedCategories.iterator().next().replace("&&", "&")))) { //$NON-NLS-1$ //$NON-NLS-2$
- // Expand the first category if we don't have a previous selection (e.g. refresh)
- expandedCategories = Collections.singleton(headers.get(0));
- }
-
- boolean wrap = mPaletteMode.getWrap();
-
- // Pack icon-only view vertically; others stretch to fill palette region
- boolean fillVertical = mPaletteMode != PaletteMode.ICON_ONLY;
-
- mAccordion = new AccordionControl(this, SWT.NONE, headers, fillVertical, wrap,
- expandedCategories) {
- @Override
- protected Composite createChildContainer(Composite parent, Object header, int style) {
- assert categoryToItems != null;
- List<ViewElementDescriptor> list = categoryToItems.get(header);
- final Composite composite;
- if (list == null) {
- assert header.equals("Custom & Library Views");
-
- Composite wrapper = new Composite(parent, SWT.NONE);
- GridLayout gridLayout = new GridLayout(1, false);
- gridLayout.marginWidth = gridLayout.marginHeight = 0;
- gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
- gridLayout.marginBottom = 3;
- wrapper.setLayout(gridLayout);
- if (mPaletteMode.isPreview() && mBackground != null) {
- wrapper.setBackground(mBackground);
- }
- composite = super.createChildContainer(wrapper, header, style);
- if (mPaletteMode.isPreview() && mBackground != null) {
- composite.setBackground(mBackground);
- }
- composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
-
- Button refreshButton = new Button(wrapper, SWT.PUSH | SWT.FLAT);
- refreshButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER,
- false, false, 1, 1));
- refreshButton.setText("Refresh");
- refreshButton.setImage(IconFactory.getInstance().getIcon("refresh")); //$NON-NLS-1$
- refreshButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
- finder.refresh(new ViewFinderListener(composite));
- }
- });
-
- wrapper.layout(true);
- } else {
- composite = super.createChildContainer(parent, header, style);
- if (mPaletteMode.isPreview() && mBackground != null) {
- composite.setBackground(mBackground);
- }
- }
- addMenu(composite);
- return composite;
- }
- @Override
- protected void createChildren(Composite parent, Object header) {
- assert categoryToItems != null;
- List<ViewElementDescriptor> list = categoryToItems.get(header);
- if (list == null) {
- assert header.equals("Custom & Library Views");
- addCustomItems(parent);
- return;
- } else {
- for (ViewElementDescriptor desc : list) {
- createItem(parent, desc);
- }
- }
- }
- };
- addMenu(mAccordion);
- for (CLabel headerLabel : mAccordion.getHeaderLabels()) {
- addMenu(headerLabel);
- }
- setLayout(new FillLayout());
-
- // Expand All for icon-only mode, but don't store it as the persistent auto-close mode;
- // when we enter other modes it will read back whatever persistent mode.
- if (mPaletteMode == PaletteMode.ICON_ONLY) {
- mAccordion.expandAll(true);
- mAccordion.setAutoClose(false);
- } else {
- mAccordion.setAutoClose(getSavedAutoCloseMode());
- }
-
- layout(true);
- }
-
- protected void addCustomItems(final Composite parent) {
- final CustomViewFinder finder = CustomViewFinder.get(mEditor.getProject());
- Collection<String> allViews = finder.getAllViews();
- if (allViews == null) { // Not yet initialized: trigger an async refresh
- finder.refresh(new ViewFinderListener(parent));
- return;
- }
-
- // Remove previous content
- for (Control c : parent.getChildren()) {
- c.dispose();
- }
-
- // Add new views
- for (final String fqcn : allViews) {
- CustomViewDescriptorService service = CustomViewDescriptorService.getInstance();
- ViewElementDescriptor desc = service.getDescriptor(mEditor.getProject(), fqcn);
- if (desc == null) {
- // The descriptor lookup performs validation steps of the class, and may
- // in some cases determine that this is not a view and will return null;
- // guard against that.
- continue;
- }
-
- Control item = createItem(parent, desc);
-
- // Add control-click listener on custom view items to you can warp to
- // (and double click listener too -- the more discoverable, the better.)
- if (item instanceof IconTextItem) {
- IconTextItem it = (IconTextItem) item;
- it.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseDoubleClick(MouseEvent e) {
- AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
- }
-
- @Override
- public void mouseDown(MouseEvent e) {
- if ((e.stateMask & SWT.MOD1) != 0) {
- AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
- }
- }
- });
- }
- }
- }
-
- /* package */ GraphicalEditorPart getEditor() {
- return mEditor;
- }
-
- private Control createItem(Composite parent, ViewElementDescriptor desc) {
- Control item = null;
- switch (mPaletteMode) {
- case SMALL_PREVIEW:
- case TINY_PREVIEW:
- case PREVIEW: {
- ImageDescriptor descriptor = mPreviewIconFactory.getImageDescriptor(desc);
- if (descriptor != null) {
- Image image = descriptor.createImage();
- ImageControl imageControl = new ImageControl(parent, SWT.None, image);
- if (mPaletteMode.isScaledPreview()) {
- // Try to preserve the overall size since rendering sizes typically
- // vary with the dpi - so while the scaling factor for a 160 dpi
- // rendering the scaling factor should be 0.5, for a 320 dpi one the
- // scaling factor should be half that, 0.25.
- float scale = 1.0f;
- if (mPaletteMode == PaletteMode.SMALL_PREVIEW) {
- scale = 0.75f;
- } else if (mPaletteMode == PaletteMode.TINY_PREVIEW) {
- scale = 0.5f;
- }
- ConfigurationChooser chooser = mEditor.getConfigurationChooser();
- int dpi = chooser.getConfiguration().getDensity().getDpiValue();
- while (dpi > 160) {
- scale = scale / 2;
- dpi = dpi / 2;
- }
- imageControl.setScale(scale);
- }
- imageControl.setHoverColor(getDisplay().getSystemColor(SWT.COLOR_WHITE));
- if (mBackground != null) {
- imageControl.setBackground(mBackground);
- }
- String toolTip = desc.getUiName();
- // It appears pretty much none of the descriptors have tooltips
- //String descToolTip = desc.getTooltip();
- //if (descToolTip != null && descToolTip.length() > 0) {
- // toolTip = toolTip + "\n" + descToolTip;
- //}
- imageControl.setToolTipText(toolTip);
-
- item = imageControl;
- } else {
- // Just use an Icon+Text item for these for now
- item = new IconTextItem(parent, desc);
- if (mForeground != null) {
- item.setForeground(mForeground);
- item.setBackground(mBackground);
- }
- }
- break;
- }
- case ICON_TEXT: {
- item = new IconTextItem(parent, desc);
- break;
- }
- case ICON_ONLY: {
- item = new ImageControl(parent, SWT.None, desc.getGenericIcon());
- item.setToolTipText(desc.getUiName());
- break;
- }
- default:
- throw new IllegalArgumentException("Not yet implemented");
- }
-
- final DragSource source = new DragSource(item, DND.DROP_COPY);
- source.setTransfer(new Transfer[] { SimpleXmlTransfer.getInstance() });
- source.addDragListener(new DescDragSourceListener(desc));
- item.addDisposeListener(new DisposeListener() {
- @Override
- public void widgetDisposed(DisposeEvent e) {
- source.dispose();
- }
- });
- addMenu(item);
-
- return item;
- }
-
- /**
- * An Item widget represents one {@link ElementDescriptor} that can be dropped on the
- * GLE2 canvas using drag'n'drop.
- */
- private static class IconTextItem extends CLabel implements MouseTrackListener {
-
- private boolean mMouseIn;
-
- public IconTextItem(Composite parent, ViewElementDescriptor desc) {
- super(parent, SWT.NONE);
- mMouseIn = false;
-
- setText(desc.getUiName());
- setImage(desc.getGenericIcon());
- setToolTipText(desc.getTooltip());
- addMouseTrackListener(this);
- }
-
- @Override
- public int getStyle() {
- int style = super.getStyle();
- if (mMouseIn) {
- style |= SWT.SHADOW_IN;
- }
- return style;
- }
-
- @Override
- public void mouseEnter(MouseEvent e) {
- if (!mMouseIn) {
- mMouseIn = true;
- redraw();
- }
- }
-
- @Override
- public void mouseExit(MouseEvent e) {
- if (mMouseIn) {
- mMouseIn = false;
- redraw();
- }
- }
-
- @Override
- public void mouseHover(MouseEvent e) {
- // pass
- }
- }
-
- /**
- * A {@link DragSourceListener} that deals with drag'n'drop of
- * {@link ElementDescriptor}s.
- */
- private class DescDragSourceListener implements DragSourceListener {
- private final ViewElementDescriptor mDesc;
- private SimpleElement[] mElements;
-
- public DescDragSourceListener(ViewElementDescriptor desc) {
- mDesc = desc;
- }
-
- @Override
- public void dragStart(DragSourceEvent e) {
- // See if we can find out the bounds of this element from a preview image.
- // Preview images are created before the drag source listener is notified
- // of the started drag.
- Rect bounds = null;
- Rect dragBounds = null;
-
- createDragImage(e);
- if (mImage != null && !mIsPlaceholder) {
- int width = mImageLayoutBounds.width;
- int height = mImageLayoutBounds.height;
- assert mImageLayoutBounds.x == 0;
- assert mImageLayoutBounds.y == 0;
- bounds = new Rect(0, 0, width, height);
- double scale = mEditor.getCanvasControl().getScale();
- int scaledWidth = (int) (scale * width);
- int scaledHeight = (int) (scale * height);
- int x = -scaledWidth / 2;
- int y = -scaledHeight / 2;
- dragBounds = new Rect(x, y, scaledWidth, scaledHeight);
- }
-
- SimpleElement se = new SimpleElement(
- SimpleXmlTransfer.getFqcn(mDesc),
- null /* parentFqcn */,
- bounds /* bounds */,
- null /* parentBounds */);
- if (mDesc instanceof PaletteMetadataDescriptor) {
- PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
- pm.initializeNew(se);
- }
- mElements = new SimpleElement[] { se };
-
- // Register this as the current dragged data
- GlobalCanvasDragInfo dragInfo = GlobalCanvasDragInfo.getInstance();
- dragInfo.startDrag(
- mElements,
- null /* selection */,
- null /* canvas */,
- null /* removeSource */);
- dragInfo.setDragBounds(dragBounds);
- dragInfo.setDragBaseline(mBaseline);
-
-
- e.doit = true;
- }
-
- @Override
- public void dragSetData(DragSourceEvent e) {
- // Provide the data for the drop when requested by the other side.
- if (SimpleXmlTransfer.getInstance().isSupportedType(e.dataType)) {
- e.data = mElements;
- }
- }
-
- @Override
- public void dragFinished(DragSourceEvent e) {
- // Unregister the dragged data.
- GlobalCanvasDragInfo.getInstance().stopDrag();
- mElements = null;
- if (mImage != null) {
- mImage.dispose();
- mImage = null;
- }
- }
-
- // TODO: Figure out the right dimensions to use for rendering.
- // We WILL crop this after rendering, but for performance reasons it would be good
- // not to make it much larger than necessary since to crop this we rely on
- // actually scanning pixels.
-
- /**
- * Width of the rendered preview image (before it is cropped), although the actual
- * width may be smaller (since we also take the device screen's size into account)
- */
- private static final int MAX_RENDER_HEIGHT = 400;
-
- /**
- * Height of the rendered preview image (before it is cropped), although the
- * actual width may be smaller (since we also take the device screen's size into
- * account)
- */
- private static final int MAX_RENDER_WIDTH = 500;
-
- /** Amount of alpha to multiply into the image (divided by 256) */
- private static final int IMG_ALPHA = 128;
-
- /** The image shown during the drag */
- private Image mImage;
- /** The non-effect bounds of the drag image */
- private Rectangle mImageLayoutBounds;
- private int mBaseline = -1;
-
- /**
- * If true, the image is a preview of the view, and if not it is a "fallback"
- * image of some sort, such as a rendering of the palette item itself
- */
- private boolean mIsPlaceholder;
-
- private void createDragImage(DragSourceEvent event) {
- mBaseline = -1;
- Pair<Image, Rectangle> preview = renderPreview();
- if (preview != null) {
- mImage = preview.getFirst();
- mImageLayoutBounds = preview.getSecond();
- } else {
- mImage = null;
- mImageLayoutBounds = null;
- }
-
- mIsPlaceholder = mImage == null;
- if (mIsPlaceholder) {
- // Couldn't render preview (or the preview is a blank image, such as for
- // example the preview of an empty layout), so instead create a placeholder
- // image
- // Render the palette item itself as an image
- Control control = ((DragSource) event.widget).getControl();
- GC gc = new GC(control);
- Point size = control.getSize();
- Display display = getDisplay();
- final Image image = new Image(display, size.x, size.y);
- gc.copyArea(image, 0, 0);
- gc.dispose();
-
- BufferedImage awtImage = SwtUtils.convertToAwt(image);
- if (awtImage != null) {
- awtImage = ImageUtils.createDropShadow(awtImage, 3 /* shadowSize */,
- 0.7f /* shadowAlpha */, 0x000000 /* shadowRgb */);
- mImage = SwtUtils.convertToSwt(display, awtImage, true, IMG_ALPHA);
- } else {
- ImageData data = image.getImageData();
- data.alpha = IMG_ALPHA;
-
- // Changing the ImageData -after- constructing an image on it
- // has no effect, so we have to construct a new image. Luckily these
- // are tiny images.
- mImage = new Image(display, data);
- }
- image.dispose();
- }
-
- event.image = mImage;
-
- if (!mIsPlaceholder) {
- // Shift the drag feedback image up such that it's centered under the
- // mouse pointer
- double scale = mEditor.getCanvasControl().getScale();
- event.offsetX = (int) (scale * mImageLayoutBounds.width / 2);
- event.offsetY = (int) (scale * mImageLayoutBounds.height / 2);
- }
- }
-
- /**
- * Performs the actual rendering of the descriptor into an image and returns the
- * image as well as the layout bounds of the image (not including drop shadow etc)
- */
- private Pair<Image, Rectangle> renderPreview() {
- ViewMetadataRepository repository = ViewMetadataRepository.get();
- RenderMode renderMode = repository.getRenderMode(mDesc.getFullClassName());
- if (renderMode == RenderMode.SKIP) {
- return null;
- }
-
- // Create blank XML document
- Document document = DomUtilities.createEmptyDocument();
-
- // Insert our target view's XML into it as a node
- GraphicalEditorPart editor = getEditor();
- LayoutEditorDelegate layoutEditorDelegate = editor.getEditorDelegate();
-
- String viewName = mDesc.getXmlLocalName();
- Element element = document.createElement(viewName);
-
- // Set up a proper name space
- Attr attr = document.createAttributeNS(XMLNS_URI, XMLNS_ANDROID);
- attr.setValue(ANDROID_URI);
- element.getAttributes().setNamedItemNS(attr);
-
- element.setAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
- element.setAttributeNS(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
-
- // This doesn't apply to all, but doesn't seem to cause harm and makes for a
- // better experience with text-oriented views like buttons and texts
- element.setAttributeNS(ANDROID_URI, ATTR_TEXT,
- DescriptorsUtils.getBasename(mDesc.getUiName()));
-
- // Is this a palette variation?
- if (mDesc instanceof PaletteMetadataDescriptor) {
- PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
- pm.initializeNew(element);
- }
-
- document.appendChild(element);
-
- // Construct UI model from XML
- AndroidTargetData data = layoutEditorDelegate.getEditor().getTargetData();
- DocumentDescriptor documentDescriptor;
- if (data == null) {
- documentDescriptor = new DocumentDescriptor("temp", null/*children*/);//$NON-NLS-1$
- } else {
- documentDescriptor = data.getLayoutDescriptors().getDescriptor();
- }
- UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode();
- model.setEditor(layoutEditorDelegate.getEditor());
- model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider());
- model.loadFromXmlNode(document);
-
- // Call the create-hooks such that we for example insert mandatory
- // children into views like the DialerFilter, apply image source attributes
- // to ImageButtons, etc.
- LayoutCanvas canvas = editor.getCanvasControl();
- NodeFactory nodeFactory = canvas.getNodeFactory();
- UiElementNode parent = model.getUiRoot();
- UiElementNode child = parent.getUiChildren().get(0);
- if (child instanceof UiViewElementNode) {
- UiViewElementNode childUiNode = (UiViewElementNode) child;
- NodeProxy childNode = nodeFactory.create(childUiNode);
-
- // Applying create hooks as part of palette render should
- // not trigger model updates
- layoutEditorDelegate.getEditor().setIgnoreXmlUpdate(true);
- try {
- canvas.getRulesEngine().callCreateHooks(layoutEditorDelegate.getEditor(),
- null, childNode, InsertType.CREATE_PREVIEW);
- childNode.applyPendingChanges();
- } catch (Throwable t) {
- AdtPlugin.log(t, "Failed calling creation hooks for widget %1$s", viewName);
- } finally {
- layoutEditorDelegate.getEditor().setIgnoreXmlUpdate(false);
- }
- }
-
- Integer overrideBgColor = null;
- boolean hasTransparency = false;
- LayoutLibrary layoutLibrary = editor.getLayoutLibrary();
- if (layoutLibrary != null &&
- layoutLibrary.supports(Capability.CUSTOM_BACKGROUND_COLOR)) {
- // It doesn't matter what the background color is as long as the alpha
- // is 0 (fully transparent). We're using red to make it more obvious if
- // for some reason the background is painted when it shouldn't be.
- overrideBgColor = new Integer(0x00FF0000);
- }
-
- RenderSession session = null;
- try {
- // Use at most the size of the screen for the preview render.
- // This is important since when we fill the size of certain views (like
- // a SeekBar), we want it to at most be the width of the screen, and for small
- // screens the RENDER_WIDTH was wider.
- LayoutLog silentLogger = new LayoutLog();
-
- session = RenderService.create(editor)
- .setModel(model)
- .setMaxRenderSize(MAX_RENDER_WIDTH, MAX_RENDER_HEIGHT)
- .setLog(silentLogger)
- .setOverrideBgColor(overrideBgColor)
- .setDecorations(false)
- .createRenderSession();
- } catch (Throwable t) {
- // Previews can fail for a variety of reasons -- let's not bug
- // the user with it
- return null;
- }
-
- if (session != null) {
- if (session.getResult().isSuccess()) {
- BufferedImage image = session.getImage();
- if (image != null) {
- BufferedImage cropped;
- Rect initialCrop = null;
- ViewInfo viewInfo = null;
-
- List<ViewInfo> viewInfoList = session.getRootViews();
-
- if (viewInfoList != null && viewInfoList.size() > 0) {
- viewInfo = viewInfoList.get(0);
- mBaseline = viewInfo.getBaseLine();
- }
-
- if (viewInfo != null) {
- int x1 = viewInfo.getLeft();
- int x2 = viewInfo.getRight();
- int y2 = viewInfo.getBottom();
- int y1 = viewInfo.getTop();
- initialCrop = new Rect(x1, y1, x2 - x1, y2 - y1);
- }
-
- if (hasTransparency) {
- cropped = ImageUtils.cropBlank(image, initialCrop);
- } else {
- // Find out what the "background" color is such that we can properly
- // crop it out of the image. To do this we pick out a pixel in the
- // bottom right unpainted area. Rather than pick the one in the far
- // bottom corner, we pick one as close to the bounds of the view as
- // possible (but still outside of the bounds), such that we can
- // deal with themes like the dialog theme.
- int edgeX = image.getWidth() -1;
- int edgeY = image.getHeight() -1;
- if (viewInfo != null) {
- if (viewInfo.getRight() < image.getWidth()-1) {
- edgeX = viewInfo.getRight()+1;
- }
- if (viewInfo.getBottom() < image.getHeight()-1) {
- edgeY = viewInfo.getBottom()+1;
- }
- }
- int edgeColor = image.getRGB(edgeX, edgeY);
- cropped = ImageUtils.cropColor(image, edgeColor, initialCrop);
- }
-
- if (cropped != null) {
- int width = initialCrop != null ? initialCrop.w : cropped.getWidth();
- int height = initialCrop != null ? initialCrop.h : cropped.getHeight();
- boolean needsContrast = hasTransparency
- && !ImageUtils.containsDarkPixels(cropped);
- cropped = ImageUtils.createDropShadow(cropped,
- hasTransparency ? 3 : 5 /* shadowSize */,
- !hasTransparency ? 0.6f : needsContrast ? 0.8f : 0.7f/*alpha*/,
- 0x000000 /* shadowRgb */);
-
- double scale = canvas.getScale();
- if (scale != 1L) {
- cropped = ImageUtils.scale(cropped, scale, scale);
- }
-
- Display display = getDisplay();
- int alpha = (!hasTransparency || !needsContrast) ? IMG_ALPHA : -1;
- Image swtImage = SwtUtils.convertToSwt(display, cropped, true, alpha);
- Rectangle imageBounds = new Rectangle(0, 0, width, height);
- return Pair.of(swtImage, imageBounds);
- }
- }
- }
-
- session.dispose();
- }
-
- return null;
- }
-
- /**
- * Utility method to print out the contents of the given XML document. This is
- * really useful when working on the preview code above. I'm including all the
- * code inside a constant false, which means the compiler will omit all the code,
- * but I'd like to leave it in the code base and by doing it this way rather than
- * as commented out code the code won't be accidentally broken.
- */
- @SuppressWarnings("all")
- private void dumpDocument(Document document) {
- // Diagnostics: print out the XML that we're about to render
- if (false) { // Will be omitted by the compiler
- org.apache.xml.serialize.OutputFormat outputFormat =
- new org.apache.xml.serialize.OutputFormat(
- "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$
- outputFormat.setIndent(2);
- outputFormat.setLineWidth(100);
- outputFormat.setIndenting(true);
- outputFormat.setOmitXMLDeclaration(true);
- outputFormat.setOmitDocumentType(true);
- StringWriter stringWriter = new StringWriter();
- // Using FQN here to avoid having an import above, which will result
- // in a deprecation warning, and there isn't a way to annotate a single
- // import element with a SuppressWarnings.
- org.apache.xml.serialize.XMLSerializer serializer =
- new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat);
- serializer.setNamespaces(true);
- try {
- serializer.serialize(document.getDocumentElement());
- System.out.println(stringWriter.toString());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /** Action for switching view modes via radio buttons */
- private class PaletteModeAction extends Action {
- private final PaletteMode mMode;
-
- PaletteModeAction(PaletteMode mode) {
- super(mode.getActionLabel(), IAction.AS_RADIO_BUTTON);
- mMode = mode;
- boolean selected = mMode == mPaletteMode;
- setChecked(selected);
- setEnabled(!selected);
- }
-
- @Override
- public void run() {
- if (isEnabled()) {
- mPaletteMode = mMode;
- refreshPalette();
- savePaletteMode();
- }
- }
- }
-
- /** Action for toggling various checkbox view modes - categories, sorting, etc */
- private class ToggleViewOptionAction extends Action {
- private final int mAction;
- final static int TOGGLE_CATEGORY = 1;
- final static int TOGGLE_ALPHABETICAL = 2;
- final static int TOGGLE_AUTO_CLOSE = 3;
- final static int REFRESH = 4;
- final static int RESET = 5;
-
- ToggleViewOptionAction(String title, int action, boolean checked) {
- super(title, (action == REFRESH || action == RESET) ? IAction.AS_PUSH_BUTTON
- : IAction.AS_CHECK_BOX);
- mAction = action;
- if (checked) {
- setChecked(checked);
- }
- }
-
- @Override
- public void run() {
- switch (mAction) {
- case TOGGLE_CATEGORY:
- mCategories = !mCategories;
- refreshPalette();
- break;
- case TOGGLE_ALPHABETICAL:
- mAlphabetical = !mAlphabetical;
- refreshPalette();
- break;
- case TOGGLE_AUTO_CLOSE:
- mAutoClose = !mAutoClose;
- mAccordion.setAutoClose(mAutoClose);
- break;
- case REFRESH:
- mPreviewIconFactory.refresh();
- refreshPalette();
- break;
- case RESET:
- mAlphabetical = false;
- mCategories = true;
- mAutoClose = true;
- mPaletteMode = PaletteMode.SMALL_PREVIEW;
- refreshPalette();
- break;
- }
- savePaletteMode();
- }
- }
-
- private void addMenu(Control control) {
- control.addMenuDetectListener(new MenuDetectListener() {
- @Override
- public void menuDetected(MenuDetectEvent e) {
- showMenu(e.x, e.y);
- }
- });
- }
-
- private void showMenu(int x, int y) {
- MenuManager manager = new MenuManager() {
- @Override
- public boolean isDynamic() {
- return true;
- }
- };
- boolean previews = previewsAvailable();
- for (PaletteMode mode : PaletteMode.values()) {
- if (mode.isPreview() && !previews) {
- continue;
- }
- manager.add(new PaletteModeAction(mode));
- }
- if (mPaletteMode.isPreview()) {
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Refresh Previews",
- ToggleViewOptionAction.REFRESH,
- false));
- }
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Show Categories",
- ToggleViewOptionAction.TOGGLE_CATEGORY,
- mCategories));
- manager.add(new ToggleViewOptionAction("Sort Alphabetically",
- ToggleViewOptionAction.TOGGLE_ALPHABETICAL,
- mAlphabetical));
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Auto Close Previous",
- ToggleViewOptionAction.TOGGLE_AUTO_CLOSE,
- mAutoClose));
- manager.add(new Separator());
- manager.add(new ToggleViewOptionAction("Reset",
- ToggleViewOptionAction.RESET,
- false));
-
- Menu menu = manager.createContextMenu(PaletteControl.this);
- menu.setLocation(x, y);
- menu.setVisible(true);
- }
-
- private final class ViewFinderListener implements CustomViewFinder.Listener {
- private final Composite mParent;
-
- private ViewFinderListener(Composite parent) {
- mParent = parent;
- }
-
- @Override
- public void viewsUpdated(Collection<String> customViews,
- Collection<String> thirdPartyViews) {
- addCustomItems(mParent);
- mParent.layout(true);
- }
- }
-}