diff options
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.java | 1265 |
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); - } - } -} |