KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > actions > QuickMenuCreator


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 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
12 package org.eclipse.ui.actions;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.jface.action.IMenuManager;
18 import org.eclipse.jface.action.MenuManager;
19 import org.eclipse.swt.custom.StyledText;
20 import org.eclipse.swt.graphics.GC;
21 import org.eclipse.swt.graphics.Point;
22 import org.eclipse.swt.graphics.Rectangle;
23 import org.eclipse.swt.widgets.Control;
24 import org.eclipse.swt.widgets.Display;
25 import org.eclipse.swt.widgets.Menu;
26 import org.eclipse.swt.widgets.Table;
27 import org.eclipse.swt.widgets.TableItem;
28 import org.eclipse.swt.widgets.Tree;
29 import org.eclipse.swt.widgets.TreeItem;
30
31 /**
32  * Abstract class that is capable of creating a context menu under the mouse
33  * pointer.
34  *
35  * @since 3.3
36  */

37 public abstract class QuickMenuCreator {
38
39     private static final int CHAR_INDENT = 3;
40
41     private Menu quickMenu;
42
43     /**
44      * Create the context menu.
45      */

46     public void createMenu() {
47         Display display = Display.getCurrent();
48         if (display == null) {
49             return;
50         }
51         Control focus = display.getFocusControl();
52         if (focus == null || focus.isDisposed()) {
53             return;
54         }
55
56         MenuManager menu = new MenuManager();
57         fillMenu(menu);
58         if (quickMenu != null) {
59             quickMenu.dispose();
60             quickMenu = null;
61         }
62         quickMenu = menu.createContextMenu(focus.getShell());
63         Point location = computeMenuLocation(focus);
64         if (location == null) {
65             return;
66         }
67         quickMenu.setLocation(location);
68         quickMenu.setVisible(true);
69     }
70
71     /**
72      * Create the contents of the context menu.
73      *
74      * @param menu
75      * the menu to fill
76      */

77     protected abstract void fillMenu(IMenuManager menu);
78
79     /**
80      * Determine the optimal point for this menu to appear.
81      *
82      * @param focus
83      * the focus control
84      * @return the optimal placement
85      */

86     private Point computeMenuLocation(Control focus) {
87         Point cursorLocation = focus.getDisplay().getCursorLocation();
88         Rectangle clientArea = null;
89         Point result = null;
90         if (focus instanceof StyledText) {
91             StyledText styledText = (StyledText) focus;
92             clientArea = styledText.getClientArea();
93             result = computeMenuLocation(styledText);
94         } else if (focus instanceof Tree) {
95             Tree tree = (Tree) focus;
96             clientArea = tree.getClientArea();
97             result = computeMenuLocation(tree);
98         } else if (focus instanceof Table) {
99             Table table = (Table) focus;
100             clientArea = table.getClientArea();
101             result = computeMenuLocation(table);
102         }
103         if (result == null) {
104             result = focus.toControl(cursorLocation);
105         }
106         if (clientArea != null && !clientArea.contains(result)) {
107             result = new Point(clientArea.x + clientArea.width / 2,
108                     clientArea.y + clientArea.height / 2);
109         }
110         Rectangle shellArea = focus.getShell().getClientArea();
111         if (!shellArea.contains(focus.getShell().toControl(
112                 focus.toDisplay(result)))) {
113             result = new Point(shellArea.x + shellArea.width / 2, shellArea.y
114                     + shellArea.height / 2);
115         }
116         return focus.toDisplay(result);
117     }
118
119     /**
120      * Hook to compute the menu location if the focus widget is a styled text
121      * widget.
122      *
123      * @param text
124      * the styled text widget that has the focus
125      *
126      * @return a widget relative position of the menu to pop up or
127      * <code>null</code> if now position inside the widget can be
128      * computed
129      */

130     private Point computeMenuLocation(StyledText text) {
131         Point result = text.getLocationAtOffset(text.getCaretOffset());
132         result.y += text.getLineHeight();
133         if (!text.getClientArea().contains(result)) {
134             return null;
135         }
136         return result;
137     }
138
139     /**
140      * Hook to compute the menu location if the focus widget is a tree widget.
141      *
142      * @param tree
143      * the tree widget that has the focus
144      *
145      * @return a widget relative position of the menu to pop up or
146      * <code>null</code> if now position inside the widget can be
147      * computed
148      */

149     private Point computeMenuLocation(Tree tree) {
150         TreeItem[] items = tree.getSelection();
151         Rectangle clientArea = tree.getClientArea();
152         switch (items.length) {
153         case 0:
154             return null;
155         case 1:
156             Rectangle bounds = items[0].getBounds();
157             Rectangle intersect = clientArea.intersection(bounds);
158             if (intersect != null && intersect.height == bounds.height) {
159                 return new Point(Math.max(0, bounds.x
160                         + getAvarageCharWith(tree) * CHAR_INDENT), bounds.y
161                         + bounds.height);
162             }
163             return null;
164
165         default:
166             Rectangle[] rectangles = new Rectangle[items.length];
167             for (int i = 0; i < rectangles.length; i++) {
168                 rectangles[i] = items[i].getBounds();
169             }
170             Point cursorLocation = tree.getDisplay().getCursorLocation();
171             Point result = findBestLocation(getIncludedPositions(rectangles,
172                     clientArea), tree.toControl(cursorLocation));
173             if (result != null) {
174                 result.x = result.x + getAvarageCharWith(tree) * CHAR_INDENT;
175             }
176             return result;
177         }
178     }
179
180     /**
181      * Hook to compute the menu location if the focus widget is a table widget.
182      *
183      * @param table
184      * the table widget that has the focus
185      *
186      * @return a widget relative position of the menu to pop up or
187      * <code>null</code> if now position inside the widget can be
188      * computed
189      */

190     private Point computeMenuLocation(Table table) {
191         TableItem[] items = table.getSelection();
192         Rectangle clientArea = table.getClientArea();
193         switch (items.length) {
194         case 0: {
195             return null;
196         }
197         case 1: {
198             Rectangle bounds = items[0].getBounds(0);
199             Rectangle iBounds = items[0].getImageBounds(0);
200             Rectangle intersect = clientArea.intersection(bounds);
201             if (intersect != null && intersect.height == bounds.height) {
202                 return new Point(Math.max(0, bounds.x + iBounds.width
203                         + getAvarageCharWith(table) * CHAR_INDENT), bounds.y
204                         + bounds.height);
205             }
206             return null;
207
208         }
209         default: {
210             Rectangle[] rectangles = new Rectangle[items.length];
211             for (int i = 0; i < rectangles.length; i++) {
212                 rectangles[i] = items[i].getBounds(0);
213             }
214             Rectangle iBounds = items[0].getImageBounds(0);
215             Point cursorLocation = table.getDisplay().getCursorLocation();
216             Point result = findBestLocation(getIncludedPositions(rectangles,
217                     clientArea), table.toControl(cursorLocation));
218             if (result != null) {
219                 result.x = result.x + iBounds.width + getAvarageCharWith(table)
220                         * CHAR_INDENT;
221             }
222             return result;
223         }
224         }
225     }
226
227     private Point[] getIncludedPositions(Rectangle[] rectangles,
228             Rectangle widgetBounds) {
229         List JavaDoc result = new ArrayList JavaDoc();
230         for (int i = 0; i < rectangles.length; i++) {
231             Rectangle rectangle = rectangles[i];
232             Rectangle intersect = widgetBounds.intersection(rectangle);
233             if (intersect != null && intersect.height == rectangle.height) {
234                 result.add(new Point(intersect.x, intersect.y
235                         + intersect.height));
236             }
237         }
238         return (Point[]) result.toArray(new Point[result.size()]);
239     }
240
241     private Point findBestLocation(Point[] points, Point relativeCursor) {
242         Point result = null;
243         double bestDist = Double.MAX_VALUE;
244         for (int i = 0; i < points.length; i++) {
245             Point point = points[i];
246             int a = 0;
247             int b = 0;
248             if (point.x > relativeCursor.x) {
249                 a = point.x - relativeCursor.x;
250             } else {
251                 a = relativeCursor.x - point.x;
252             }
253             if (point.y > relativeCursor.y) {
254                 b = point.y - relativeCursor.y;
255             } else {
256                 b = relativeCursor.y - point.y;
257             }
258             double dist = Math.sqrt(a * a + b * b);
259             if (dist < bestDist) {
260                 result = point;
261                 bestDist = dist;
262             }
263         }
264         return result;
265     }
266
267     private int getAvarageCharWith(Control control) {
268         GC gc = null;
269         try {
270             gc = new GC(control);
271             return gc.getFontMetrics().getAverageCharWidth();
272         } finally {
273             if (gc != null) {
274                 gc.dispose();
275             }
276         }
277     }
278
279     /**
280      * Dispose of this quick menu creator. Subclasses should ensure that they
281      * call this method.
282      */

283     public void dispose() {
284         if (quickMenu != null) {
285             quickMenu.dispose();
286             quickMenu = null;
287         }
288     }
289 }
290
Popular Tags