| /* |
| * 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_URI; |
| import static com.android.SdkConstants.ATTR_ID; |
| |
| import com.android.annotations.NonNull; |
| import com.android.ide.common.api.INode; |
| import com.android.ide.common.api.RuleAction; |
| import com.android.ide.common.api.RuleAction.Choices; |
| import com.android.ide.common.api.RuleAction.Separator; |
| import com.android.ide.common.api.RuleAction.Toggle; |
| import com.android.ide.common.layout.BaseViewRule; |
| import com.android.ide.eclipse.adt.internal.editors.IconFactory; |
| import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; |
| import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration; |
| import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser; |
| import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; |
| import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; |
| import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; |
| import com.android.sdklib.devices.Device; |
| import com.android.sdklib.devices.Screen; |
| import com.android.sdkuilib.internal.widgets.ResolutionChooserDialog; |
| import com.google.common.base.Strings; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.PlatformUI; |
| |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Toolbar shown at the top of the layout editor, which adds a number of context-sensitive |
| * layout actions (as well as zooming controls on the right). |
| */ |
| public class LayoutActionBar extends Composite { |
| private GraphicalEditorPart mEditor; |
| private ToolBar mLayoutToolBar; |
| private ToolBar mLintToolBar; |
| private ToolBar mZoomToolBar; |
| private ToolItem mZoomRealSizeButton; |
| private ToolItem mZoomOutButton; |
| private ToolItem mZoomResetButton; |
| private ToolItem mZoomInButton; |
| private ToolItem mZoomFitButton; |
| private ToolItem mLintButton; |
| private List<RuleAction> mPrevActions; |
| |
| /** |
| * Creates a new {@link LayoutActionBar} and adds it to the given parent. |
| * |
| * @param parent the parent composite to add the actions bar to |
| * @param style the SWT style to apply |
| * @param editor the associated layout editor |
| */ |
| public LayoutActionBar(Composite parent, int style, GraphicalEditorPart editor) { |
| super(parent, style | SWT.NO_FOCUS); |
| mEditor = editor; |
| |
| GridLayout layout = new GridLayout(3, false); |
| setLayout(layout); |
| |
| mLayoutToolBar = new ToolBar(this, /*SWT.WRAP |*/ SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); |
| mLayoutToolBar.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); |
| mZoomToolBar = createZoomControls(); |
| mZoomToolBar.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); |
| mLintToolBar = createLintControls(); |
| |
| GridData lintData = new GridData(SWT.END, SWT.BEGINNING, false, false); |
| lintData.exclude = true; |
| mLintToolBar.setLayoutData(lintData); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| mPrevActions = null; |
| } |
| |
| /** Updates the layout contents based on the current selection */ |
| void updateSelection() { |
| NodeProxy parent = null; |
| LayoutCanvas canvas = mEditor.getCanvasControl(); |
| SelectionManager selectionManager = canvas.getSelectionManager(); |
| List<SelectionItem> selections = selectionManager.getSelections(); |
| if (selections.size() > 0) { |
| // TODO: better handle multi-selection -- maybe we should disable it or |
| // something. |
| // What if you select children with different parents? Of different types? |
| // etc. |
| NodeProxy node = selections.get(0).getNode(); |
| if (node != null && node.getParent() != null) { |
| parent = (NodeProxy) node.getParent(); |
| } |
| } |
| |
| if (parent == null) { |
| // Show the background's properties |
| CanvasViewInfo root = canvas.getViewHierarchy().getRoot(); |
| if (root == null) { |
| return; |
| } |
| parent = canvas.getNodeFactory().create(root); |
| selections = Collections.emptyList(); |
| } |
| |
| RulesEngine engine = mEditor.getRulesEngine(); |
| List<NodeProxy> selectedNodes = new ArrayList<NodeProxy>(); |
| for (SelectionItem item : selections) { |
| selectedNodes.add(item.getNode()); |
| } |
| List<RuleAction> actions = new ArrayList<RuleAction>(); |
| engine.callAddLayoutActions(actions, parent, selectedNodes); |
| |
| // Place actions in the correct order (the actions may come from different |
| // rules and should be merged properly via sorting keys) |
| Collections.sort(actions); |
| |
| // Add in actions for the child as well, if there is exactly one. |
| // These are not merged into the parent list of actions; they are appended |
| // at the end. |
| int index = -1; |
| String label = null; |
| if (selectedNodes.size() == 1) { |
| List<RuleAction> itemActions = new ArrayList<RuleAction>(); |
| NodeProxy selectedNode = selectedNodes.get(0); |
| engine.callAddLayoutActions(itemActions, selectedNode, null); |
| if (itemActions.size() > 0) { |
| Collections.sort(itemActions); |
| |
| if (!(itemActions.get(0) instanceof RuleAction.Separator)) { |
| actions.add(RuleAction.createSeparator(0)); |
| } |
| label = selectedNode.getStringAttr(ANDROID_URI, ATTR_ID); |
| if (label != null) { |
| label = BaseViewRule.stripIdPrefix(label); |
| index = actions.size(); |
| } |
| actions.addAll(itemActions); |
| } |
| } |
| |
| if (!updateActions(actions)) { |
| updateToolbar(actions, index, label); |
| } |
| mPrevActions = actions; |
| } |
| |
| /** Update the toolbar widgets */ |
| private void updateToolbar(final List<RuleAction> actions, final int labelIndex, |
| final String label) { |
| if (mLayoutToolBar == null || mLayoutToolBar.isDisposed()) { |
| return; |
| } |
| for (ToolItem c : mLayoutToolBar.getItems()) { |
| c.dispose(); |
| } |
| mLayoutToolBar.pack(); |
| addActions(actions, labelIndex, label); |
| mLayoutToolBar.pack(); |
| mLayoutToolBar.layout(); |
| } |
| |
| /** |
| * Attempts to update the existing toolbar actions, if the action list is |
| * similar to the current list. Returns false if this cannot be done and the |
| * contents must be replaced. |
| */ |
| private boolean updateActions(@NonNull List<RuleAction> actions) { |
| List<RuleAction> before = mPrevActions; |
| List<RuleAction> after = actions; |
| |
| if (before == null) { |
| return false; |
| } |
| |
| if (!before.equals(after) || after.size() > mLayoutToolBar.getItemCount()) { |
| return false; |
| } |
| |
| int actionIndex = 0; |
| for (int i = 0, max = mLayoutToolBar.getItemCount(); i < max; i++) { |
| ToolItem item = mLayoutToolBar.getItem(i); |
| int style = item.getStyle(); |
| Object data = item.getData(); |
| if (data != null) { |
| // One action can result in multiple toolbar items (e.g. a choice action |
| // can result in multiple radio buttons), so we've have to replace all of |
| // them with the corresponding new action |
| RuleAction prevAction = before.get(actionIndex); |
| while (prevAction != data) { |
| actionIndex++; |
| if (actionIndex == before.size()) { |
| return false; |
| } |
| prevAction = before.get(actionIndex); |
| if (prevAction == data) { |
| break; |
| } else if (!(prevAction instanceof RuleAction.Separator)) { |
| return false; |
| } |
| } |
| RuleAction newAction = after.get(actionIndex); |
| assert newAction.equals(prevAction); // Maybe I can do this lazily instead? |
| |
| // Update action binding to the new action |
| item.setData(newAction); |
| |
| // Sync button states: the checked state is not considered part of |
| // RuleAction equality |
| if ((style & SWT.CHECK) != 0) { |
| assert newAction instanceof Toggle; |
| Toggle toggle = (Toggle) newAction; |
| item.setSelection(toggle.isChecked()); |
| } else if ((style & SWT.RADIO) != 0) { |
| assert newAction instanceof Choices; |
| Choices choices = (Choices) newAction; |
| String current = choices.getCurrent(); |
| String id = (String) item.getData(ATTR_ID); |
| boolean selected = Strings.nullToEmpty(current).equals(id); |
| item.setSelection(selected); |
| } |
| } else { |
| // Must be a separator, or a label (which we insert for nested widgets) |
| assert (style & SWT.SEPARATOR) != 0 || !item.getText().isEmpty() : item; |
| } |
| } |
| |
| return true; |
| } |
| |
| private void addActions(List<RuleAction> actions, int labelIndex, String label) { |
| if (actions.size() > 0) { |
| // Flag used to indicate that if there are any actions -after- this, it |
| // should be separated from this current action (we don't unconditionally |
| // add a separator at the end of these groups in case there are no more |
| // actions at the end so that we don't have a trailing separator) |
| boolean needSeparator = false; |
| |
| int index = 0; |
| for (RuleAction action : actions) { |
| if (index == labelIndex) { |
| final ToolItem button = new ToolItem(mLayoutToolBar, SWT.PUSH); |
| button.setText(label); |
| needSeparator = false; |
| } |
| index++; |
| |
| if (action instanceof Separator) { |
| addSeparator(mLayoutToolBar); |
| needSeparator = false; |
| continue; |
| } else if (needSeparator) { |
| addSeparator(mLayoutToolBar); |
| needSeparator = false; |
| } |
| |
| if (action instanceof RuleAction.Choices) { |
| RuleAction.Choices choices = (Choices) action; |
| if (!choices.isRadio()) { |
| addDropdown(choices); |
| } else { |
| addSeparator(mLayoutToolBar); |
| addRadio(choices); |
| needSeparator = true; |
| } |
| } else if (action instanceof RuleAction.Toggle) { |
| addToggle((Toggle) action); |
| } else { |
| addPlainAction(action); |
| } |
| } |
| } |
| } |
| |
| /** Add a separator to the toolbar, unless there already is one there at the end already */ |
| private static void addSeparator(ToolBar toolBar) { |
| int n = toolBar.getItemCount(); |
| if (n > 0 && (toolBar.getItem(n - 1).getStyle() & SWT.SEPARATOR) == 0) { |
| ToolItem separator = new ToolItem(toolBar, SWT.SEPARATOR); |
| separator.setWidth(15); |
| } |
| } |
| |
| private void addToggle(Toggle toggle) { |
| final ToolItem button = new ToolItem(mLayoutToolBar, SWT.CHECK); |
| |
| URL iconUrl = toggle.getIconUrl(); |
| String title = toggle.getTitle(); |
| if (iconUrl != null) { |
| button.setImage(IconFactory.getInstance().getIcon(iconUrl)); |
| button.setToolTipText(title); |
| } else { |
| button.setText(title); |
| } |
| button.setData(toggle); |
| |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| Toggle toggle = (Toggle) button.getData(); |
| toggle.getCallback().action(toggle, getSelectedNodes(), |
| toggle.getId(), button.getSelection()); |
| updateSelection(); |
| } |
| }); |
| if (toggle.isChecked()) { |
| button.setSelection(true); |
| } |
| } |
| |
| private List<INode> getSelectedNodes() { |
| List<SelectionItem> selections = |
| mEditor.getCanvasControl().getSelectionManager().getSelections(); |
| List<INode> nodes = new ArrayList<INode>(selections.size()); |
| for (SelectionItem item : selections) { |
| nodes.add(item.getNode()); |
| } |
| |
| return nodes; |
| } |
| |
| |
| private void addPlainAction(RuleAction menuAction) { |
| final ToolItem button = new ToolItem(mLayoutToolBar, SWT.PUSH); |
| |
| URL iconUrl = menuAction.getIconUrl(); |
| String title = menuAction.getTitle(); |
| if (iconUrl != null) { |
| button.setImage(IconFactory.getInstance().getIcon(iconUrl)); |
| button.setToolTipText(title); |
| } else { |
| button.setText(title); |
| } |
| button.setData(menuAction); |
| |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| RuleAction menuAction = (RuleAction) button.getData(); |
| menuAction.getCallback().action(menuAction, getSelectedNodes(), menuAction.getId(), |
| false); |
| updateSelection(); |
| } |
| }); |
| } |
| |
| private void addRadio(RuleAction.Choices choices) { |
| List<URL> icons = choices.getIconUrls(); |
| List<String> titles = choices.getTitles(); |
| List<String> ids = choices.getIds(); |
| String current = choices.getCurrent() != null ? choices.getCurrent() : ""; //$NON-NLS-1$ |
| |
| assert icons != null; |
| assert icons.size() == titles.size(); |
| |
| for (int i = 0; i < icons.size(); i++) { |
| URL iconUrl = icons.get(i); |
| String title = titles.get(i); |
| final String id = ids.get(i); |
| final ToolItem item = new ToolItem(mLayoutToolBar, SWT.RADIO); |
| item.setToolTipText(title); |
| item.setImage(IconFactory.getInstance().getIcon(iconUrl)); |
| item.setData(choices); |
| item.setData(ATTR_ID, id); |
| item.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (item.getSelection()) { |
| RuleAction.Choices choices = (Choices) item.getData(); |
| choices.getCallback().action(choices, getSelectedNodes(), id, null); |
| updateSelection(); |
| } |
| } |
| }); |
| boolean selected = current.equals(id); |
| if (selected) { |
| item.setSelection(true); |
| } |
| } |
| } |
| |
| private void addDropdown(RuleAction.Choices choices) { |
| final ToolItem combo = new ToolItem(mLayoutToolBar, SWT.DROP_DOWN); |
| URL iconUrl = choices.getIconUrl(); |
| if (iconUrl != null) { |
| combo.setImage(IconFactory.getInstance().getIcon(iconUrl)); |
| combo.setToolTipText(choices.getTitle()); |
| } else { |
| combo.setText(choices.getTitle()); |
| } |
| combo.setData(choices); |
| |
| Listener menuListener = new Listener() { |
| @Override |
| public void handleEvent(Event event) { |
| Menu menu = new Menu(mLayoutToolBar.getShell(), SWT.POP_UP); |
| RuleAction.Choices choices = (Choices) combo.getData(); |
| List<URL> icons = choices.getIconUrls(); |
| List<String> titles = choices.getTitles(); |
| List<String> ids = choices.getIds(); |
| String current = choices.getCurrent() != null ? choices.getCurrent() : ""; //$NON-NLS-1$ |
| |
| for (int i = 0; i < titles.size(); i++) { |
| String title = titles.get(i); |
| final String id = ids.get(i); |
| URL itemIconUrl = icons != null && icons.size() > 0 ? icons.get(i) : null; |
| MenuItem item = new MenuItem(menu, SWT.CHECK); |
| item.setText(title); |
| if (itemIconUrl != null) { |
| Image itemIcon = IconFactory.getInstance().getIcon(itemIconUrl); |
| item.setImage(itemIcon); |
| } |
| |
| boolean selected = id.equals(current); |
| if (selected) { |
| item.setSelection(true); |
| } |
| |
| item.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| RuleAction.Choices choices = (Choices) combo.getData(); |
| choices.getCallback().action(choices, getSelectedNodes(), id, null); |
| updateSelection(); |
| } |
| }); |
| } |
| |
| Rectangle bounds = combo.getBounds(); |
| Point location = new Point(bounds.x, bounds.y + bounds.height); |
| location = combo.getParent().toDisplay(location); |
| menu.setLocation(location.x, location.y); |
| menu.setVisible(true); |
| } |
| }; |
| combo.addListener(SWT.Selection, menuListener); |
| } |
| |
| // ---- Zoom Controls ---- |
| |
| @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused |
| private ToolBar createZoomControls() { |
| ToolBar toolBar = new ToolBar(this, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); |
| |
| IconFactory iconFactory = IconFactory.getInstance(); |
| mZoomRealSizeButton = new ToolItem(toolBar, SWT.CHECK); |
| mZoomRealSizeButton.setToolTipText("Emulate Real Size"); |
| mZoomRealSizeButton.setImage(iconFactory.getIcon("zoomreal")); //$NON-NLS-1$); |
| mZoomRealSizeButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| boolean newState = mZoomRealSizeButton.getSelection(); |
| if (rescaleToReal(newState)) { |
| mZoomOutButton.setEnabled(!newState); |
| mZoomResetButton.setEnabled(!newState); |
| mZoomInButton.setEnabled(!newState); |
| mZoomFitButton.setEnabled(!newState); |
| } else { |
| mZoomRealSizeButton.setSelection(!newState); |
| } |
| } |
| }); |
| |
| mZoomFitButton = new ToolItem(toolBar, SWT.PUSH); |
| mZoomFitButton.setToolTipText("Zoom to Fit (0)"); |
| mZoomFitButton.setImage(iconFactory.getIcon("zoomfit")); //$NON-NLS-1$); |
| mZoomFitButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| rescaleToFit(true); |
| } |
| }); |
| |
| mZoomResetButton = new ToolItem(toolBar, SWT.PUSH); |
| mZoomResetButton.setToolTipText("Reset Zoom to 100% (1)"); |
| mZoomResetButton.setImage(iconFactory.getIcon("zoom100")); //$NON-NLS-1$); |
| mZoomResetButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| resetScale(); |
| } |
| }); |
| |
| // Group zoom in/out separately |
| new ToolItem(toolBar, SWT.SEPARATOR); |
| |
| mZoomOutButton = new ToolItem(toolBar, SWT.PUSH); |
| mZoomOutButton.setToolTipText("Zoom Out (-)"); |
| mZoomOutButton.setImage(iconFactory.getIcon("zoomminus")); //$NON-NLS-1$); |
| mZoomOutButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| rescale(-1); |
| } |
| }); |
| |
| mZoomInButton = new ToolItem(toolBar, SWT.PUSH); |
| mZoomInButton.setToolTipText("Zoom In (+)"); |
| mZoomInButton.setImage(iconFactory.getIcon("zoomplus")); //$NON-NLS-1$); |
| mZoomInButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| rescale(+1); |
| } |
| }); |
| |
| return toolBar; |
| } |
| |
| @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused |
| private ToolBar createLintControls() { |
| ToolBar toolBar = new ToolBar(this, SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); |
| |
| // Separate from adjacent toolbar |
| new ToolItem(toolBar, SWT.SEPARATOR); |
| |
| ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); |
| mLintButton = new ToolItem(toolBar, SWT.PUSH); |
| mLintButton.setToolTipText("Show Lint Warnings for this Layout"); |
| mLintButton.setImage(sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK)); |
| mLintButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| CommonXmlEditor editor = mEditor.getEditorDelegate().getEditor(); |
| IFile file = editor.getInputFile(); |
| if (file != null) { |
| EclipseLintClient.showErrors(getShell(), file, editor); |
| } |
| } |
| }); |
| |
| return toolBar; |
| } |
| |
| /** |
| * Updates the lint indicator state in the given layout editor |
| */ |
| public void updateErrorIndicator() { |
| updateErrorIndicator(mEditor.getEditedFile()); |
| } |
| |
| /** |
| * Updates the lint indicator state for the given file |
| * |
| * @param file the file to show the indicator status for |
| */ |
| public void updateErrorIndicator(IFile file) { |
| IMarker[] markers = EclipseLintClient.getMarkers(file); |
| updateErrorIndicator(markers.length); |
| } |
| |
| /** |
| * Sets whether the action bar should show the "lint warnings" button |
| * |
| * @param hasLintWarnings whether there are lint errors to be shown |
| */ |
| private void updateErrorIndicator(final int markerCount) { |
| Display display = getDisplay(); |
| if (display.getThread() != Thread.currentThread()) { |
| display.asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (!isDisposed()) { |
| updateErrorIndicator(markerCount); |
| } |
| } |
| }); |
| return; |
| } |
| |
| GridData layoutData = (GridData) mLintToolBar.getLayoutData(); |
| Integer existing = (Integer) mLintToolBar.getData(); |
| Integer current = Integer.valueOf(markerCount); |
| if (!current.equals(existing)) { |
| mLintToolBar.setData(current); |
| boolean layout = false; |
| boolean hasLintWarnings = markerCount > 0 && AdtPrefs.getPrefs().isLintOnSave(); |
| if (layoutData.exclude == hasLintWarnings) { |
| layoutData.exclude = !hasLintWarnings; |
| mLintToolBar.setVisible(hasLintWarnings); |
| layout = true; |
| } |
| if (markerCount > 0) { |
| String iconName = ""; |
| switch (markerCount) { |
| case 1: iconName = "lint1"; break; //$NON-NLS-1$ |
| case 2: iconName = "lint2"; break; //$NON-NLS-1$ |
| case 3: iconName = "lint3"; break; //$NON-NLS-1$ |
| case 4: iconName = "lint4"; break; //$NON-NLS-1$ |
| case 5: iconName = "lint5"; break; //$NON-NLS-1$ |
| case 6: iconName = "lint6"; break; //$NON-NLS-1$ |
| case 7: iconName = "lint7"; break; //$NON-NLS-1$ |
| case 8: iconName = "lint8"; break; //$NON-NLS-1$ |
| case 9: iconName = "lint9"; break; //$NON-NLS-1$ |
| default: iconName = "lint9p"; break;//$NON-NLS-1$ |
| } |
| mLintButton.setImage(IconFactory.getInstance().getIcon(iconName)); |
| } |
| if (layout) { |
| layout(); |
| } |
| redraw(); |
| } |
| } |
| |
| /** |
| * Returns true if zooming in/out/to-fit/etc is allowed (which is not the case while |
| * emulating real size) |
| * |
| * @return true if zooming is allowed |
| */ |
| boolean isZoomingAllowed() { |
| return mZoomInButton.isEnabled(); |
| } |
| |
| boolean isZoomingRealSize() { |
| return mZoomRealSizeButton.getSelection(); |
| } |
| |
| /** |
| * Rescales canvas. |
| * @param direction +1 for zoom in, -1 for zoom out |
| */ |
| void rescale(int direction) { |
| LayoutCanvas canvas = mEditor.getCanvasControl(); |
| double s = canvas.getScale(); |
| |
| if (direction > 0) { |
| s = s * 1.2; |
| } else { |
| s = s / 1.2; |
| } |
| |
| // Some operations are faster if the zoom is EXACTLY 1.0 rather than ALMOST 1.0. |
| // (This is because there is a fast-path when image copying and the scale is 1.0; |
| // in that case it does not have to do any scaling). |
| // |
| // If you zoom out 10 times and then back in 10 times, small rounding errors mean |
| // that you end up with a scale=1.0000000000000004. In the cases, when you get close |
| // to 1.0, just make the zoom an exact 1.0. |
| if (Math.abs(s-1.0) < 0.0001) { |
| s = 1.0; |
| } |
| |
| canvas.setScale(s, true /*redraw*/); |
| } |
| |
| /** |
| * Reset the canvas scale to 100% |
| */ |
| void resetScale() { |
| mEditor.getCanvasControl().setScale(1, true /*redraw*/); |
| } |
| |
| /** |
| * Reset the canvas scale to best fit (so content is as large as possible without scrollbars) |
| */ |
| void rescaleToFit(boolean onlyZoomOut) { |
| mEditor.getCanvasControl().setFitScale(onlyZoomOut, true /*allowZoomIn*/); |
| } |
| |
| boolean rescaleToReal(boolean real) { |
| if (real) { |
| return computeAndSetRealScale(true /*redraw*/); |
| } else { |
| // reset the scale to 100% |
| mEditor.getCanvasControl().setScale(1, true /*redraw*/); |
| return true; |
| } |
| } |
| |
| boolean computeAndSetRealScale(boolean redraw) { |
| // compute average dpi of X and Y |
| ConfigurationChooser chooser = mEditor.getConfigurationChooser(); |
| Configuration config = chooser.getConfiguration(); |
| Device device = config.getDevice(); |
| Screen screen = device.getDefaultHardware().getScreen(); |
| double dpi = (screen.getXdpi() + screen.getYdpi()) / 2.; |
| |
| // get the monitor dpi |
| float monitor = AdtPrefs.getPrefs().getMonitorDensity(); |
| if (monitor == 0.f) { |
| ResolutionChooserDialog dialog = new ResolutionChooserDialog(chooser.getShell()); |
| if (dialog.open() == Window.OK) { |
| monitor = dialog.getDensity(); |
| AdtPrefs.getPrefs().setMonitorDensity(monitor); |
| } else { |
| return false; |
| } |
| } |
| |
| mEditor.getCanvasControl().setScale(monitor / dpi, redraw); |
| return true; |
| } |
| } |