KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > actions > QuickMenuAction


1 /*******************************************************************************
2  * Copyright (c) 2000, 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 package org.eclipse.jdt.internal.ui.actions;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.List JavaDoc;
15
16 import org.eclipse.swt.custom.StyledText;
17 import org.eclipse.swt.graphics.GC;
18 import org.eclipse.swt.graphics.Point;
19 import org.eclipse.swt.graphics.Rectangle;
20 import org.eclipse.swt.widgets.Control;
21 import org.eclipse.swt.widgets.Display;
22 import org.eclipse.swt.widgets.Menu;
23 import org.eclipse.swt.widgets.Table;
24 import org.eclipse.swt.widgets.TableItem;
25 import org.eclipse.swt.widgets.Tree;
26 import org.eclipse.swt.widgets.TreeItem;
27
28 import org.eclipse.jface.action.Action;
29 import org.eclipse.jface.action.IMenuManager;
30 import org.eclipse.jface.action.MenuManager;
31
32 import org.eclipse.ui.PlatformUI;
33 import org.eclipse.ui.keys.IBindingService;
34
35 /**
36  * A quick menu actions provides support to assign short cuts
37  * to sub menus.
38  *
39  * @since 3.0
40  */

41 public abstract class QuickMenuAction extends Action {
42
43     private static final int CHAR_INDENT= 3;
44     
45     /**
46      * Creates a new quick menu action with the given command id.
47      *
48      * @param commandId the command id of the short cut used to open
49      * the sub menu
50      */

51     public QuickMenuAction(String JavaDoc commandId) {
52         setActionDefinitionId(commandId);
53     }
54
55     /**
56      * {@inheritDoc}
57      */

58     public void run() {
59         Display display= Display.getCurrent();
60         if (display == null)
61             return;
62         Control focus= display.getFocusControl();
63         if (focus == null || focus.isDisposed())
64             return;
65         
66         MenuManager menu= new MenuManager();
67         fillMenu(menu);
68         final Menu widget= menu.createContextMenu(focus.getShell());
69         Point location= computeMenuLocation(focus, widget);
70         if (location == null)
71             return;
72         widget.setLocation(location);
73         widget.setVisible(true);
74         while (!widget.isDisposed() && widget.isVisible()) {
75             if (!display.readAndDispatch())
76                 display.sleep();
77         }
78         if (!widget.isDisposed()) {
79             widget.dispose();
80         }
81     }
82     
83     /**
84      * Hook to fill a menu manager with the items of the sub menu.
85      *
86      * @param menu the sub menu to fill
87      */

88     protected abstract void fillMenu(IMenuManager menu);
89     
90     /**
91      * Adds the shortcut to the given menu text and returns it.
92      *
93      * @param menuText the menu text
94      * @return the menu text with the shortcut
95      * @since 3.1
96      */

97     public String JavaDoc addShortcut(String JavaDoc menuText) {
98         String JavaDoc shortcut= getShortcutString();
99         if (menuText == null || shortcut == null)
100             return menuText;
101         
102         return menuText + '\t' + shortcut;
103     }
104     
105     /**
106      * Returns the shortcut assigned to the sub menu or <code>null</code> if
107      * no short cut is assigned.
108      *
109      * @return the shortcut as a human readable string or <code>null</code>
110      */

111     private String JavaDoc getShortcutString() {
112         IBindingService bindingService= (IBindingService)PlatformUI.getWorkbench().getAdapter(IBindingService.class);
113         if (bindingService == null)
114             return null;
115         return bindingService.getBestActiveBindingFormattedFor(getActionDefinitionId());
116     }
117
118     private Point computeMenuLocation(Control focus, Menu menu) {
119         Point cursorLocation= focus.getDisplay().getCursorLocation();
120         Rectangle clientArea= null;
121         Point result= null;
122         if (focus instanceof StyledText) {
123             StyledText styledText= (StyledText)focus;
124             clientArea= styledText.getClientArea();
125             result= computeMenuLocation(styledText);
126         } else if (focus instanceof Tree) {
127             Tree tree= (Tree)focus;
128             clientArea= tree.getClientArea();
129             result= computeMenuLocation(tree);
130         } else if (focus instanceof Table) {
131             Table table= (Table)focus;
132             clientArea= table.getClientArea();
133             result= computeMenuLocation(table);
134         }
135         if (result == null) {
136             result= focus.toControl(cursorLocation);
137         }
138         if (clientArea != null && !clientArea.contains(result)) {
139             result= new Point(
140                 clientArea.x + clientArea.width / 2,
141                 clientArea.y + clientArea.height / 2);
142         }
143         Rectangle shellArea= focus.getShell().getClientArea();
144         if (!shellArea.contains(focus.getShell().toControl(focus.toDisplay(result)))) {
145             result= new Point(
146                 shellArea.x + shellArea.width / 2,
147                 shellArea.y + shellArea.height / 2);
148         }
149         return focus.toDisplay(result);
150     }
151     
152     /**
153      * Hook to compute the menu location if the focus widget is
154      * a styled text widget.
155      *
156      * @param text the styled text widget that has the focus
157      *
158      * @return a widget relative position of the menu to pop up or
159      * <code>null</code> if now position inside the widget can
160      * be computed
161      */

162     protected Point computeMenuLocation(StyledText text) {
163         int offset= text.getCaretOffset();
164         Point result= text.getLocationAtOffset(offset);
165         result.y+= text.getLineHeight(offset);
166         if (!text.getClientArea().contains(result))
167             return null;
168         return result;
169     }
170     
171     /**
172      * Hook to compute the menu location if the focus widget is
173      * a tree widget.
174      *
175      * @param tree the tree widget that has the focus
176      *
177      * @return a widget relative position of the menu to pop up or
178      * <code>null</code> if now position inside the widget can
179      * be computed
180      */

181     protected Point computeMenuLocation(Tree tree) {
182         TreeItem[] items= tree.getSelection();
183         Rectangle clientArea= tree.getClientArea();
184         switch (items.length) {
185             case 0:
186                 return null;
187             case 1:
188                 Rectangle bounds= items[0].getBounds();
189                 Rectangle intersect= clientArea.intersection(bounds);
190                 if (intersect != null && intersect.height == bounds.height) {
191                     return new Point(
192                         Math.max(0, bounds.x + getAvarageCharWith(tree) * CHAR_INDENT),
193                         bounds.y + bounds.height);
194                 } else {
195                     return null;
196                 }
197             default:
198                 Rectangle[] rectangles= new Rectangle[items.length];
199                 for (int i= 0; i < rectangles.length; i++) {
200                     rectangles[i]= items[i].getBounds();
201                 }
202                 Point cursorLocation= tree.getDisplay().getCursorLocation();
203                 Point result= findBestLocation(getIncludedPositions(rectangles, clientArea),
204                     tree.toControl(cursorLocation));
205                 if (result != null)
206                     result.x= result.x + getAvarageCharWith(tree) * CHAR_INDENT;
207                 return result;
208         }
209     }
210     
211     /**
212      * Hook to compute the menu location if the focus widget is
213      * a table widget.
214      *
215      * @param table the table widget that has the focus
216      *
217      * @return a widget relative position of the menu to pop up or
218      * <code>null</code> if now position inside the widget can
219      * be computed
220      */

221     protected Point computeMenuLocation(Table table) {
222         TableItem[] items= table.getSelection();
223         Rectangle clientArea= table.getClientArea();
224         switch (items.length) {
225             case 0: {
226                 return null;
227             } case 1: {
228                 Rectangle bounds= items[0].getBounds(0);
229                 Rectangle iBounds= items[0].getImageBounds(0);
230                 Rectangle intersect= clientArea.intersection(bounds);
231                 if (intersect != null && intersect.height == bounds.height) {
232                     return new Point(
233                         Math.max(0, bounds.x + iBounds.width + getAvarageCharWith(table) * CHAR_INDENT),
234                         bounds.y + bounds.height);
235                 } else {
236                     return null;
237                 }
238             } default: {
239                 Rectangle[] rectangles= new Rectangle[items.length];
240                 for (int i= 0; i < rectangles.length; i++) {
241                     rectangles[i]= items[i].getBounds(0);
242                 }
243                 Rectangle iBounds= items[0].getImageBounds(0);
244                 Point cursorLocation= table.getDisplay().getCursorLocation();
245                 Point result= findBestLocation(getIncludedPositions(rectangles, clientArea),
246                     table.toControl(cursorLocation));
247                 if (result != null)
248                     result.x= result.x + iBounds.width + getAvarageCharWith(table) * CHAR_INDENT;
249                 return result;
250             }
251         }
252     }
253     
254     private Point[] getIncludedPositions(Rectangle[] rectangles, Rectangle widgetBounds) {
255         List JavaDoc result= new ArrayList JavaDoc();
256         for (int i= 0; i < rectangles.length; i++) {
257             Rectangle rectangle= rectangles[i];
258             Rectangle intersect= widgetBounds.intersection(rectangle);
259             if (intersect != null && intersect.height == rectangle.height) {
260                 result.add(new Point(intersect.x, intersect.y + intersect.height));
261             }
262         }
263         return (Point[]) result.toArray(new Point[result.size()]);
264     }
265     
266     private Point findBestLocation(Point[] points, Point relativeCursor) {
267         Point result= null;
268         double bestDist= Double.MAX_VALUE;
269         for (int i= 0; i < points.length; i++) {
270             Point point= points[i];
271             int a= 0;
272             int b= 0;
273             if (point.x > relativeCursor.x) {
274                 a= point.x - relativeCursor.x;
275             } else {
276                 a= relativeCursor.x - point.x;
277             }
278             if (point.y > relativeCursor.y) {
279                 b= point.y - relativeCursor.y;
280             } else {
281                 b= relativeCursor.y - point.y;
282             }
283             double dist= Math.sqrt(a * a + b * b);
284             if (dist < bestDist) {
285                 result= point;
286                 bestDist= dist;
287             }
288         }
289         return result;
290     }
291     
292     private int getAvarageCharWith(Control control) {
293         GC gc= null;
294         try {
295             gc= new GC(control);
296             return gc.getFontMetrics().getAverageCharWidth();
297         } finally {
298             if (gc != null)
299                 gc.dispose();
300         }
301     }
302 }
303
Popular Tags