| /* |
| * Copyright (C) 2012 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.ide.eclipse.adt.internal.editors.layout.gle2.LintOverlay.ICON_SIZE; |
| |
| import com.android.annotations.Nullable; |
| import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Shell; |
| import org.w3c.dom.Node; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** Tooltip in the layout editor showing lint errors under the cursor */ |
| class LintTooltipManager implements Listener { |
| private final LayoutCanvas mCanvas; |
| private Shell mTip = null; |
| private List<UiViewElementNode> mShowingNodes; |
| |
| /** |
| * Sets up a custom tooltip when hovering over tree items. It currently displays the error |
| * message for the lint warning associated with each node, if any (and only if the hover |
| * is over the icon portion). |
| */ |
| LintTooltipManager(LayoutCanvas canvas) { |
| mCanvas = canvas; |
| } |
| |
| void register() { |
| mCanvas.addListener(SWT.Dispose, this); |
| mCanvas.addListener(SWT.KeyDown, this); |
| mCanvas.addListener(SWT.MouseMove, this); |
| mCanvas.addListener(SWT.MouseHover, this); |
| } |
| |
| void unregister() { |
| if (!mCanvas.isDisposed()) { |
| mCanvas.removeListener(SWT.Dispose, this); |
| mCanvas.removeListener(SWT.KeyDown, this); |
| mCanvas.removeListener(SWT.MouseMove, this); |
| mCanvas.removeListener(SWT.MouseHover, this); |
| } |
| } |
| |
| @Override |
| public void handleEvent(Event event) { |
| switch(event.type) { |
| case SWT.MouseMove: |
| // See if we're still overlapping this or *other* errors; if so, keep the |
| // tip up (or update it). |
| if (mShowingNodes != null) { |
| List<UiViewElementNode> nodes = computeNodes(event); |
| if (nodes != null && !nodes.isEmpty()) { |
| if (nodes.equals(mShowingNodes)) { |
| return; |
| } else { |
| show(nodes); |
| } |
| break; |
| } |
| } |
| |
| // If not, fall through and hide the tooltip |
| |
| //$FALL-THROUGH$ |
| case SWT.Dispose: |
| case SWT.FocusOut: |
| case SWT.KeyDown: |
| case SWT.MouseExit: |
| case SWT.MouseDown: |
| hide(); |
| break; |
| case SWT.MouseHover: |
| hide(); |
| show(event); |
| break; |
| } |
| } |
| |
| void hide() { |
| if (mTip != null) { |
| mTip.dispose(); |
| mTip = null; |
| } |
| mShowingNodes = null; |
| } |
| |
| private void show(Event event) { |
| List<UiViewElementNode> nodes = computeNodes(event); |
| if (nodes != null && !nodes.isEmpty()) { |
| show(nodes); |
| } |
| } |
| |
| /** Show a tooltip listing the lint errors for the given nodes */ |
| private void show(List<UiViewElementNode> nodes) { |
| hide(); |
| |
| if (!AdtPrefs.getPrefs().isLintOnSave()) { |
| return; |
| } |
| |
| mTip = new LintTooltip(mCanvas, nodes); |
| Rectangle rect = mCanvas.getBounds(); |
| Point size = mTip.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| Point pos = mCanvas.toDisplay(rect.x, rect.y + rect.height); |
| if (size.x > rect.width) { |
| size = mTip.computeSize(rect.width, SWT.DEFAULT); |
| } |
| mTip.setBounds(pos.x, pos.y, size.x, size.y); |
| |
| mShowingNodes = nodes; |
| mTip.setVisible(true); |
| } |
| |
| /** |
| * Compute the list of nodes which have lint warnings near the given mouse |
| * coordinates |
| * |
| * @param event the mouse cursor event |
| * @return a list of nodes, possibly empty |
| */ |
| @Nullable |
| private List<UiViewElementNode> computeNodes(Event event) { |
| LayoutPoint p = ControlPoint.create(mCanvas, event.x, event.y).toLayout(); |
| LayoutEditorDelegate delegate = mCanvas.getEditorDelegate(); |
| ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy(); |
| CanvasTransform mHScale = mCanvas.getHorizontalTransform(); |
| CanvasTransform mVScale = mCanvas.getVerticalTransform(); |
| |
| int layoutIconSize = mHScale.inverseScale(ICON_SIZE); |
| int slop = mVScale.inverseScale(10); // extra space around icon where tip triggers |
| |
| Collection<Node> xmlNodes = delegate.getLintNodes(); |
| if (xmlNodes == null) { |
| return null; |
| } |
| List<UiViewElementNode> nodes = new ArrayList<UiViewElementNode>(); |
| for (Node xmlNode : xmlNodes) { |
| CanvasViewInfo v = viewHierarchy.findViewInfoFor(xmlNode); |
| if (v != null) { |
| Rectangle b = v.getAbsRect(); |
| int x2 = b.x + b.width; |
| int y2 = b.y + b.height; |
| if (p.x < x2 - layoutIconSize - slop |
| || p.x > x2 + slop |
| || p.y < y2 - layoutIconSize - slop |
| || p.y > y2 + slop) { |
| continue; |
| } |
| |
| nodes.add(v.getUiViewNode()); |
| } |
| } |
| |
| return nodes; |
| } |
| } |