| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2004, 2006 IBM Corporation and others. |
| 3 | * All rights reserved. This program and the accompanying materials |
| 4 | * are made available under the terms of the Eclipse Public License v1.0 |
| 5 | * which accompanies this distribution, and is available at |
| 6 | * http://www.eclipse.org/legal/epl-v10.html |
| 7 | * |
| 8 | * Contributors: |
| 9 | * IBM Corporation - initial API and implementation |
| 10 | *******************************************************************************/ |
| 11 | package org.eclipse.ui.internal.dnd; |
| 12 | |
| 13 | import org.eclipse.core.runtime.Assert; |
| 14 | import org.eclipse.swt.graphics.Point; |
| 15 | import org.eclipse.swt.graphics.Rectangle; |
| 16 | import org.eclipse.swt.widgets.Composite; |
| 17 | import org.eclipse.swt.widgets.Control; |
| 18 | import org.eclipse.swt.widgets.Display; |
| 19 | import org.eclipse.swt.widgets.Monitor; |
| 20 | import org.eclipse.swt.widgets.Shell; |
| 21 | |
| 22 | /** |
| 23 | * Contains static methods for manipulating SWT controls |
| 24 | * |
| 25 | * @since 3.0 |
| 26 | */ |
| 27 | public class SwtUtil { |
| 28 | |
| 29 | private SwtUtil() { |
| 30 | |
| 31 | } |
| 32 | |
| 33 | /** |
| 34 | * Returns true if the given control is null or has been disposed |
| 35 | * |
| 36 | * @param toTest the control to test |
| 37 | * @return false if it is safe to invoke methods on the given control |
| 38 | */ |
| 39 | public static boolean isDisposed(Control toTest) { |
| 40 | return toTest == null || toTest.isDisposed(); |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | * Returns the control that is covering the given control, or null if none. |
| 45 | * |
| 46 | * @param toTest control to test |
| 47 | * @return a control that obscures the test control or null if none |
| 48 | */ |
| 49 | public static Control controlThatCovers(Control toTest) { |
| 50 | return controlThatCovers(toTest, DragUtil.getDisplayBounds(toTest)); |
| 51 | } |
| 52 | |
| 53 | private static Control controlThatCovers(Control toTest, Rectangle testRegion) { |
| 54 | Composite parent = toTest.getParent(); |
| 55 | |
| 56 | if (parent == null || toTest instanceof Shell) { |
| 57 | return null; |
| 58 | } |
| 59 | |
| 60 | Control[] children = parent.getChildren(); |
| 61 | for (int i = 0; i < children.length; i++) { |
| 62 | Control control = children[i]; |
| 63 | |
| 64 | if (control == toTest) { |
| 65 | break; |
| 66 | } |
| 67 | |
| 68 | if (!control.isVisible()) { |
| 69 | continue; |
| 70 | } |
| 71 | |
| 72 | Rectangle nextBounds = DragUtil.getDisplayBounds(control); |
| 73 | |
| 74 | if (nextBounds.intersects(testRegion)) { |
| 75 | return control; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | return controlThatCovers(parent, testRegion); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Determines if one control is a child of another. Returns true iff the second |
| 84 | * argument is a child of the first (or the same object). |
| 85 | * |
| 86 | * @param potentialParent |
| 87 | * @param childToTest |
| 88 | * @return |
| 89 | */ |
| 90 | public static boolean isChild(Control potentialParent, Control childToTest) { |
| 91 | if (childToTest == null) { |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | if (childToTest == potentialParent) { |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | return isChild(potentialParent, childToTest.getParent()); |
| 100 | } |
| 101 | |
| 102 | public static boolean isFocusAncestor(Control potentialParent) { |
| 103 | Assert.isNotNull(potentialParent); |
| 104 | Control focusControl = Display.getCurrent().getFocusControl(); |
| 105 | if (focusControl == null) { |
| 106 | return false; |
| 107 | } |
| 108 | return isChild(potentialParent, focusControl); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Finds and returns the most specific SWT control at the given location. |
| 113 | * (Note: this does a DFS on the SWT widget hierarchy, which is slow). |
| 114 | * |
| 115 | * @param displayToSearch |
| 116 | * @param locationToFind |
| 117 | * @return |
| 118 | */ |
| 119 | public static Control findControl(Display displayToSearch, |
| 120 | Point locationToFind) { |
| 121 | Shell[] shells = displayToSearch.getShells(); |
| 122 | |
| 123 | return findControl(shells, locationToFind); |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Searches the given list of controls for a control containing the given point. |
| 128 | * If the array contains any composites, those composites will be recursively |
| 129 | * searched to find the most specific child that contains the point. |
| 130 | * |
| 131 | * @param toSearch an array of composites |
| 132 | * @param locationToFind a point (in display coordinates) |
| 133 | * @return the most specific Control that overlaps the given point, or null if none |
| 134 | */ |
| 135 | public static Control findControl(Control[] toSearch, Point locationToFind) { |
| 136 | for (int idx = toSearch.length - 1; idx >= 0; idx--) { |
| 137 | Control next = toSearch[idx]; |
| 138 | |
| 139 | if (!next.isDisposed() && next.isVisible()) { |
| 140 | |
| 141 | Rectangle bounds = DragUtil.getDisplayBounds(next); |
| 142 | |
| 143 | if (bounds.contains(locationToFind)) { |
| 144 | if (next instanceof Composite) { |
| 145 | Control result = findControl((Composite) next, |
| 146 | locationToFind); |
| 147 | |
| 148 | if (result != null) { |
| 149 | return result; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | return next; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | return null; |
| 159 | } |
| 160 | |
| 161 | public static Control[] getAncestors(Control theControl) { |
| 162 | return getAncestors(theControl, 1); |
| 163 | } |
| 164 | |
| 165 | private static Control[] getAncestors(Control theControl, int children) { |
| 166 | Control[] result; |
| 167 | |
| 168 | if (theControl.getParent() == null) { |
| 169 | result = new Control[children]; |
| 170 | } else { |
| 171 | result = getAncestors(theControl.getParent(), children + 1); |
| 172 | } |
| 173 | |
| 174 | result[result.length - children] = theControl; |
| 175 | |
| 176 | return result; |
| 177 | } |
| 178 | |
| 179 | public static Control findCommonAncestor(Control control1, Control control2) { |
| 180 | Control[] control1Ancestors = getAncestors(control1); |
| 181 | Control[] control2Ancestors = getAncestors(control2); |
| 182 | |
| 183 | Control mostSpecific = null; |
| 184 | |
| 185 | for (int idx = 0; idx < Math.min(control1Ancestors.length, control2Ancestors.length); idx++) { |
| 186 | Control control1Ancestor = control1Ancestors[idx]; |
| 187 | if (control1Ancestor == control2Ancestors[idx]) { |
| 188 | mostSpecific = control1Ancestor; |
| 189 | } else { |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | return mostSpecific; |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * Finds the control in the given location |
| 199 | * |
| 200 | * @param toSearch |
| 201 | * @param locationToFind location (in display coordinates) |
| 202 | * @return |
| 203 | */ |
| 204 | public static Control findControl(Composite toSearch, Point locationToFind) { |
| 205 | Control[] children = toSearch.getChildren(); |
| 206 | |
| 207 | return findControl(children, locationToFind); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * |
| 212 | * Returns true iff the given rectangle is located in the client area of any |
| 213 | * monitor. |
| 214 | * |
| 215 | * @param someRectangle a rectangle in display coordinates (not null) |
| 216 | * @return true iff the given point can be seen on any monitor |
| 217 | */ |
| 218 | public static boolean intersectsAnyMonitor(Display display, |
| 219 | Rectangle someRectangle) { |
| 220 | Monitor[] monitors = display.getMonitors(); |
| 221 | |
| 222 | for (int idx = 0; idx < monitors.length; idx++) { |
| 223 | Monitor mon = monitors[idx]; |
| 224 | |
| 225 | if (mon.getClientArea().intersects(someRectangle)) { |
| 226 | return true; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | } |