1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 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  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.widgets.Menu;
14 
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.internal.gtk.OS;
17 import org.eclipse.swt.events.MenuListener;
18 import org.eclipse.swt.events.HelpListener;
19 import org.eclipse.swt.graphics.Point;
20 import org.eclipse.swt.graphics.Rectangle;
21 import org.eclipse.swt.widgets.ImageList;
22 import org.eclipse.swt.widgets.TypedListener;
23 import org.eclipse.swt.widgets.Event;
24 import org.eclipse.swt.widgets.MenuItem;
25 import org.eclipse.swt.widgets.Widget;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.widgets.Decorations;
28 import org.eclipse.swt.widgets.Shell;
29 import java.lang.all;
30 
31 /**
32  * Instances of this class are user interface objects that contain
33  * menu items.
34  * <dl>
35  * <dt><b>Styles:</b></dt>
36  * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd>
37  * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
38  * <dt><b>Events:</b></dt>
39  * <dd>Help, Hide, Show </dd>
40  * </dl>
41  * <p>
42  * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified.
43  * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
44  * </p><p>
45  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
46  * </p>
47  *
48  * @see <a href="http://www.eclipse.org/swt/snippets/#menu">Menu snippets</a>
49  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
50  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
51  */
52 public class Menu : Widget {
53     int x, y;
54     bool hasLocation;
55     MenuItem cascade, selectedItem;
56     Decorations parent;
57     GtkWidget* imItem;
58     GtkWidget* imSeparator;
59     GtkIMContext* imHandle;
60     ImageList imageList;
61 
62 /**
63  * Constructs a new instance of this class given its parent,
64  * and sets the style for the instance so that the instance
65  * will be a popup menu on the given parent's shell.
66  * <p>
67  * After constructing a menu, it can be set into its parent
68  * using <code>parent.setMenu(menu)</code>.  In this case, the parent may
69  * be any control in the same widget tree as the parent.
70  * </p>
71  *
72  * @param parent a control which will be the parent of the new instance (cannot be null)
73  *
74  * @exception IllegalArgumentException <ul>
75  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
76  * </ul>
77  * @exception SWTException <ul>
78  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
79  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
80  * </ul>
81  *
82  * @see SWT#POP_UP
83  * @see Widget#checkSubclass
84  * @see Widget#getStyle
85  */
86 public this (Control parent) {
87     this (checkNull (parent).menuShell (), SWT.POP_UP);
88 }
89 
90 /**
91  * Constructs a new instance of this class given its parent
92  * (which must be a <code>Decorations</code>) and a style value
93  * describing its behavior and appearance.
94  * <p>
95  * The style value is either one of the style constants defined in
96  * class <code>SWT</code> which is applicable to instances of this
97  * class, or must be built by <em>bitwise OR</em>'ing together
98  * (that is, using the <code>int</code> "|" operator) two or more
99  * of those <code>SWT</code> style constants. The class description
100  * lists the style constants that are applicable to the class.
101  * Style bits are also inherited from superclasses.
102  * </p><p>
103  * After constructing a menu or menuBar, it can be set into its parent
104  * using <code>parent.setMenu(menu)</code> or <code>parent.setMenuBar(menuBar)</code>.
105  * </p>
106  *
107  * @param parent a decorations control which will be the parent of the new instance (cannot be null)
108  * @param style the style of menu to construct
109  *
110  * @exception IllegalArgumentException <ul>
111  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
112  * </ul>
113  * @exception SWTException <ul>
114  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
115  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
116  * </ul>
117  *
118  * @see SWT#BAR
119  * @see SWT#DROP_DOWN
120  * @see SWT#POP_UP
121  * @see Widget#checkSubclass
122  * @see Widget#getStyle
123  */
124 public this (Decorations parent, int style) {
125     super (parent, checkStyle (style));
126     this.parent = parent;
127     createWidget (0);
128 }
129 
130 /**
131  * Constructs a new instance of this class given its parent
132  * (which must be a <code>Menu</code>) and sets the style
133  * for the instance so that the instance will be a drop-down
134  * menu on the given parent's parent.
135  * <p>
136  * After constructing a drop-down menu, it can be set into its parentMenu
137  * using <code>parentMenu.setMenu(menu)</code>.
138  * </p>
139  *
140  * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
141  *
142  * @exception IllegalArgumentException <ul>
143  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
144  * </ul>
145  * @exception SWTException <ul>
146  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
147  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
148  * </ul>
149  *
150  * @see SWT#DROP_DOWN
151  * @see Widget#checkSubclass
152  * @see Widget#getStyle
153  */
154 public this (Menu parentMenu) {
155     this (checkNull (parentMenu).parent, SWT.DROP_DOWN);
156 }
157 
158 /**
159  * Constructs a new instance of this class given its parent
160  * (which must be a <code>MenuItem</code>) and sets the style
161  * for the instance so that the instance will be a drop-down
162  * menu on the given parent's parent menu.
163  * <p>
164  * After constructing a drop-down menu, it can be set into its parentItem
165  * using <code>parentItem.setMenu(menu)</code>.
166  * </p>
167  *
168  * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
169  *
170  * @exception IllegalArgumentException <ul>
171  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
172  * </ul>
173  * @exception SWTException <ul>
174  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
175  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
176  * </ul>
177  *
178  * @see SWT#DROP_DOWN
179  * @see Widget#checkSubclass
180  * @see Widget#getStyle
181  */
182 public this (MenuItem parentItem) {
183     this (checkNull (parentItem).parent);
184 }
185 
186 static Control checkNull (Control control) {
187     if (control is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
188     return control;
189 }
190 
191 static Menu checkNull (Menu menu) {
192     if (menu is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
193     return menu;
194 }
195 
196 static MenuItem checkNull (MenuItem item) {
197     if (item is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
198     return item;
199 }
200 
201 static int checkStyle (int style) {
202     return checkBits (style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0);
203 }
204 
205 void _setVisible (bool visible) {
206     if (visible is OS.GTK_WIDGET_MAPPED (handle)) return;
207     if (visible) {
208         sendEvent (SWT.Show);
209         if (getItemCount () !is 0) {
210             if ((OS.GTK_VERSION >=  OS.buildVERSION (2, 8, 0))) {
211                 /*
212                 * Feature in GTK. ON_TOP shells will send out
213                 * SWT.Deactivate whenever a context menu is shown.
214                 * The fix is to prevent the menu from taking focus
215                 * when it is being shown in an ON_TOP shell.
216                 */
217                 if ((parent._getShell ().style & SWT.ON_TOP) !is 0) {
218                     OS.gtk_menu_shell_set_take_focus (cast(GtkMenuShell*)handle, false);
219                 }
220             }
221             /*
222             * Bug in GTK.  The timestamp passed into gtk_menu_popup is used
223             * to perform an X pointer grab.  It cannot be zero, else the grab
224             * will fail.  The fix is to ensure that the timestamp of the last
225             * event processed is used.
226             */
227             getDisplay().doMenuPositionProc( cast(GtkMenu*)handle, hasLocation );
228         } else {
229             sendEvent (SWT.Hide);
230         }
231     } else {
232         OS.gtk_menu_popdown (cast(GtkMenu*)handle);
233     }
234 }
235 
236 void addAccelerators (GtkAccelGroup* accelGroup) {
237     MenuItem [] items = getItems ();
238     for (int i = 0; i < items.length; i++) {
239         MenuItem item = items[i];
240         item.addAccelerators (accelGroup);
241     }
242 }
243 
244 /**
245  * Adds the listener to the collection of listeners who will
246  * be notified when menus are hidden or shown, by sending it
247  * one of the messages defined in the <code>MenuListener</code>
248  * interface.
249  *
250  * @param listener the listener which should be notified
251  *
252  * @exception IllegalArgumentException <ul>
253  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
254  * </ul>
255  * @exception SWTException <ul>
256  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
257  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
258  * </ul>
259  *
260  * @see MenuListener
261  * @see #removeMenuListener
262  */
263 public void addMenuListener (MenuListener listener) {
264     checkWidget();
265     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
266     TypedListener typedListener = new TypedListener (listener);
267     addListener (SWT.Hide,typedListener);
268     addListener (SWT.Show,typedListener);
269 }
270 
271 /**
272  * Adds the listener to the collection of listeners who will
273  * be notified when help events are generated for the control,
274  * by sending it one of the messages defined in the
275  * <code>HelpListener</code> interface.
276  *
277  * @param listener the listener which should be notified
278  *
279  * @exception IllegalArgumentException <ul>
280  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
281  * </ul>
282  * @exception SWTException <ul>
283  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
284  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
285  * </ul>
286  *
287  * @see HelpListener
288  * @see #removeHelpListener
289  */
290 public void addHelpListener (HelpListener listener) {
291     checkWidget();
292     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
293     TypedListener typedListener = new TypedListener (listener);
294     addListener (SWT.Help, typedListener);
295 }
296 
297 override void createHandle (int index) {
298     state |= HANDLE;
299     if ((style & SWT.BAR) !is 0) {
300         handle = OS.gtk_menu_bar_new ();
301         if (handle is null) error (SWT.ERROR_NO_HANDLES);
302         auto vboxHandle = parent.vboxHandle;
303         OS.gtk_container_add (cast(GtkContainer*)vboxHandle, handle);
304         OS.gtk_box_set_child_packing (cast(GtkBox*)vboxHandle, handle, false, true, 0, OS.GTK_PACK_START);
305     } else {
306         handle = OS.gtk_menu_new ();
307         if (handle is null) error (SWT.ERROR_NO_HANDLES);
308     }
309 }
310 
311 void createIMMenu (GtkIMContext* imHandle) {
312     if (this.imHandle is imHandle) return;
313     this.imHandle = imHandle;
314     if (imHandle is null) {
315         if (imItem !is null) {
316             OS.gtk_widget_destroy (imItem);
317             imItem = null;
318         }
319         if (imSeparator !is null) {
320             OS.gtk_widget_destroy (imSeparator);
321             imSeparator = null;
322         }
323         return;
324     }
325     if (imSeparator is null) {
326         imSeparator = OS.gtk_separator_menu_item_new ();
327         OS.gtk_widget_show (imSeparator);
328         OS.gtk_menu_shell_insert (cast(GtkMenuShell*)handle, imSeparator, -1);
329     }
330     if (imItem is null) {
331         char* buffer = toStringz( SWT.getMessage("SWT_InputMethods"));
332         imItem = OS.gtk_image_menu_item_new_with_label (buffer);
333         OS.gtk_widget_show (imItem);
334         OS.gtk_menu_shell_insert (cast(GtkMenuShell*)handle, imItem, -1);
335     }
336     auto imSubmenu = OS.gtk_menu_new ();
337     OS.gtk_im_multicontext_append_menuitems (cast(GtkIMMulticontext*)imHandle, cast(GtkMenuShell*)imSubmenu);
338     OS.gtk_menu_item_set_submenu (cast(GtkMenuItem*)imItem, imSubmenu);
339 }
340 
341 override void createWidget (int index) {
342     checkOrientation (parent);
343     super.createWidget (index);
344     parent.addMenu (this);
345 }
346 
347 void fixMenus (Decorations newParent) {
348     MenuItem [] items = getItems ();
349     for (int i=0; i<items.length; i++) {
350         items [i].fixMenus (newParent);
351     }
352     parent.removeMenu (this);
353     newParent.addMenu (this);
354     this.parent = newParent;
355 }
356 
357 /*public*/ Rectangle getBounds () {
358     checkWidget();
359     if (!OS.GTK_WIDGET_MAPPED (handle)) {
360         return new Rectangle (0, 0, 0, 0);
361     }
362     auto window = OS.GTK_WIDGET_WINDOW (handle);
363     int origin_x, origin_y;
364     OS.gdk_window_get_origin (window, &origin_x, &origin_y);
365     int x = origin_x + OS.GTK_WIDGET_X (handle);
366     int y = origin_y + OS.GTK_WIDGET_Y (handle);
367     int width = OS.GTK_WIDGET_WIDTH (handle);
368     int height = OS.GTK_WIDGET_HEIGHT (handle);
369     return new Rectangle (x, y, width, height);
370 }
371 
372 /**
373  * Returns the default menu item or null if none has
374  * been previously set.
375  *
376  * @return the default menu item.
377  *
378  * </ul>
379  * @exception SWTException <ul>
380  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
381  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
382  * </ul>
383  */
384 public MenuItem getDefaultItem () {
385     checkWidget();
386     return null;
387 }
388 
389 /**
390  * Returns <code>true</code> if the receiver is enabled, and
391  * <code>false</code> otherwise. A disabled menu is typically
392  * not selectable from the user interface and draws with an
393  * inactive or "grayed" look.
394  *
395  * @return the receiver's enabled state
396  *
397  * @exception SWTException <ul>
398  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
399  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
400  * </ul>
401  *
402  * @see #isEnabled
403  */
404 public bool getEnabled () {
405     checkWidget();
406     return OS.GTK_WIDGET_SENSITIVE (handle);
407 }
408 
409 /**
410  * Returns the item at the given, zero-relative index in the
411  * receiver. Throws an exception if the index is out of range.
412  *
413  * @param index the index of the item to return
414  * @return the item at the given index
415  *
416  * @exception IllegalArgumentException <ul>
417  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
418  * </ul>
419  * @exception SWTException <ul>
420  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
421  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
422  * </ul>
423  */
424 public MenuItem getItem (int index) {
425     checkWidget();
426     auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle);
427     if (list is null) error (SWT.ERROR_CANNOT_GET_ITEM);
428     int count = OS.g_list_length (list);
429     if (imSeparator !is null) count--;
430     if (imItem !is null) count--;
431     if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
432     auto data = cast(GtkWidget*)OS.g_list_nth_data (list, index);
433     OS.g_list_free (list);
434     if (data is null) error (SWT.ERROR_CANNOT_GET_ITEM);
435     return cast(MenuItem) display.getWidget (data);
436 }
437 
438 /**
439  * Returns the number of items contained in the receiver.
440  *
441  * @return the number of items
442  *
443  * @exception SWTException <ul>
444  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
445  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
446  * </ul>
447  */
448 public int getItemCount () {
449     checkWidget();
450     auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle);
451     if (list is null) return 0;
452     int count = OS.g_list_length (list);
453     OS.g_list_free (list);
454     if (imSeparator !is null) count--;
455     if (imItem !is null) count--;
456     return count;
457 }
458 
459 /**
460  * Returns a (possibly empty) array of <code>MenuItem</code>s which
461  * are the items in the receiver.
462  * <p>
463  * Note: This is not the actual structure used by the receiver
464  * to maintain its list of items, so modifying the array will
465  * not affect the receiver.
466  * </p>
467  *
468  * @return the items in the receiver
469  *
470  * @exception SWTException <ul>
471  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
472  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
473  * </ul>
474  */
475 public MenuItem [] getItems () {
476     checkWidget();
477     auto list = OS.gtk_container_get_children (cast(GtkContainer*)handle);
478     if (list is null) return new MenuItem [0];
479     int count = OS.g_list_length (list);
480     if (imSeparator !is null) count--;
481     if (imItem !is null) count--;
482     MenuItem [] items = new MenuItem [count];
483     int index = 0;
484     for (int i=0; i<count; i++) {
485         auto data = cast(GtkWidget*)OS.g_list_nth_data (list, i);
486         MenuItem item = cast(MenuItem) display.getWidget (data);
487         if (item !is null) items [index++] = item;
488     }
489     OS.g_list_free (list);
490     if (index !is items.length) {
491         MenuItem [] newItems = new MenuItem[index];
492         System.arraycopy(items, 0, newItems, 0, index);
493         items = newItems;
494     }
495     return items;
496 }
497 
498 override String getNameText () {
499     String result = "";
500     MenuItem [] items = getItems ();
501     ptrdiff_t length_ = items.length;
502     if (length_ > 0) {
503         for (int i=0; i<length_-1; i++) {
504             result = result ~ items [i].getNameText() ~ ", ";
505         }
506         result = result ~ items [length_-1].getNameText ();
507     }
508     return result;
509 }
510 
511 /**
512  * Returns the receiver's parent, which must be a <code>Decorations</code>.
513  *
514  * @return the receiver's parent
515  *
516  * @exception SWTException <ul>
517  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
518  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
519  * </ul>
520  */
521 public Decorations getParent () {
522     checkWidget();
523     return parent;
524 }
525 
526 /**
527  * Returns the receiver's parent item, which must be a
528  * <code>MenuItem</code> or null when the receiver is a
529  * root.
530  *
531  * @return the receiver's parent item
532  *
533  * @exception SWTException <ul>
534  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
535  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
536  * </ul>
537  */
538 public MenuItem getParentItem () {
539     checkWidget();
540     return cascade;
541 }
542 
543 /**
544  * Returns the receiver's parent item, which must be a
545  * <code>Menu</code> or null when the receiver is a
546  * root.
547  *
548  * @return the receiver's parent item
549  *
550  * @exception SWTException <ul>
551  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
552  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
553  * </ul>
554  */
555 public Menu getParentMenu () {
556     checkWidget();
557     if (cascade is null) return null;
558     return cascade.getParent ();
559 }
560 
561 /**
562  * Returns the receiver's shell. For all controls other than
563  * shells, this simply returns the control's nearest ancestor
564  * shell. Shells return themselves, even if they are children
565  * of other shells.
566  *
567  * @return the receiver's shell
568  *
569  * @exception SWTException <ul>
570  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
571  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
572  * </ul>
573  *
574  * @see #getParent
575  */
576 public Shell getShell () {
577     checkWidget();
578     return parent.getShell ();
579 }
580 
581 /**
582  * Returns <code>true</code> if the receiver is visible, and
583  * <code>false</code> otherwise.
584  * <p>
585  * If one of the receiver's ancestors is not visible or some
586  * other condition makes the receiver not visible, this method
587  * may still indicate that it is considered visible even though
588  * it may not actually be showing.
589  * </p>
590  *
591  * @return the receiver's visibility state
592  *
593  * @exception SWTException <ul>
594  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
595  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
596  * </ul>
597  */
598 public bool getVisible () {
599     checkWidget();
600     if ((style & SWT.POP_UP) !is 0) {
601         Menu [] popups = display.popups;
602         if (popups !is null) {
603             for (int i=0; i<popups.length; i++) {
604                 if (popups [i] is this) return true;
605             }
606         }
607     }
608     return OS.GTK_WIDGET_MAPPED (handle);
609 }
610 
611 override int gtk_hide (GtkWidget* widget) {
612     if ((style & SWT.POP_UP) !is 0) {
613         display.activeShell = getShell ();
614     }
615     if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 0)) {
616         sendEvent (SWT.Hide);
617     } else {
618         /*
619         * Bug in GTK.  In GTK 2.4 and earlier
620         * a crash could occur if a menu item
621         * was disposed within gtk_hide.  The
622         * workaroud is to post the event instead
623         * of send it on these platforms
624         */
625         postEvent (SWT.Hide);
626     }
627     return 0;
628 }
629 
630 override int gtk_show (GtkWidget* widget) {
631     if ((style & SWT.POP_UP) !is 0) {
632         display.activeShell = getShell ();
633         return 0;
634     }
635     sendEvent (SWT.Show);
636     return 0;
637 }
638 
639 
640 override int gtk_show_help (GtkWidget* widget, ptrdiff_t helpType) {
641     if (sendHelpEvent (helpType)) {
642         OS.gtk_menu_shell_deactivate (cast(GtkMenuShell*)handle);
643         return 1;
644     }
645     return 0;
646 }
647 
648 override void hookEvents () {
649     super.hookEvents ();
650     OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW], 0, display.closures [SHOW], false);
651     OS.g_signal_connect_closure_by_id (handle, display.signalIds [HIDE], 0, display.closures [HIDE], false);
652     OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW_HELP], 0, display.closures [SHOW_HELP], false);
653 }
654 
655 /**
656  * Searches the receiver's list starting at the first item
657  * (index 0) until an item is found that is equal to the
658  * argument, and returns the index of that item. If no item
659  * is found, returns -1.
660  *
661  * @param item the search item
662  * @return the index of the item
663  *
664  * @exception IllegalArgumentException <ul>
665  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
666  * </ul>
667  * @exception SWTException <ul>
668  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
669  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
670  * </ul>
671  */
672 public int indexOf (MenuItem item) {
673     checkWidget();
674     if (item is null) error (SWT.ERROR_NULL_ARGUMENT);
675     MenuItem [] items = getItems ();
676     for (int i=0; i<items.length; i++) {
677         if (items [i] is item) return i;
678     }
679     return -1;
680 }
681 
682 /**
683  * Returns <code>true</code> if the receiver is enabled and all
684  * of the receiver's ancestors are enabled, and <code>false</code>
685  * otherwise. A disabled menu is typically not selectable from the
686  * user interface and draws with an inactive or "grayed" look.
687  *
688  * @return the receiver's enabled state
689  *
690  * @exception SWTException <ul>
691  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
692  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
693  * </ul>
694  *
695  * @see #getEnabled
696  */
697 public bool isEnabled () {
698     checkWidget();
699     Menu parentMenu = getParentMenu ();
700     if (parentMenu is null) {
701         return getEnabled () && parent.isEnabled ();
702     }
703     return getEnabled () && parentMenu.isEnabled ();
704 }
705 
706 /**
707  * Returns <code>true</code> if the receiver is visible and all
708  * of the receiver's ancestors are visible and <code>false</code>
709  * otherwise.
710  *
711  * @return the receiver's visibility state
712  *
713  * @exception SWTException <ul>
714  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
715  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
716  * </ul>
717  *
718  * @see #getVisible
719  */
720 public bool isVisible () {
721     checkWidget();
722     return getVisible ();
723 }
724 
725 override void menuPositionProc (GtkMenu* menu, int* x, int* y, int* push_in, void* user_data) {
726     /*
727     * Feature in GTK.  The menu position function sets the position of the
728     * top-left pixel of the menu.  If the menu would be off-screen, GTK will
729     * add a scroll arrow at the bottom and position the first menu entry at
730     * the specified position.  The fix is to flip the menu location to be
731     * completely inside the screen.
732     *
733     * NOTE: This code doesn't work for multiple monitors.
734     */
735     GtkRequisition requisition;
736     OS.gtk_widget_size_request (cast(GtkWidget*)menu, &requisition);
737     int screenHeight = OS.gdk_screen_height ();
738     int reqy = this.y;
739     if (reqy + requisition.height > screenHeight) {
740         reqy = Math.max (0, reqy - requisition.height);
741     }
742     int screenWidth = OS.gdk_screen_width ();
743     int reqx = this.x;
744     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
745         if (reqx - requisition.width >= 0) reqx -= requisition.width;
746     } else {
747         if (reqx + requisition.width > screenWidth) reqx -= requisition.width;
748     }
749     if (x !is null) *x = reqx;
750     if (y !is null) *y = reqy;
751     if (push_in !is null) *push_in = 1;
752 }
753 
754 override void releaseChildren (bool destroy) {
755     MenuItem [] items = getItems ();
756     for (int i=0; i<items.length; i++) {
757         MenuItem item = items [i];
758         if (item !is null && !item.isDisposed ()) {
759             item.release (false);
760         }
761     }
762     super.releaseChildren (destroy);
763 }
764 
765 override void releaseParent () {
766     super.releaseParent ();
767     if (cascade !is null) cascade.setMenu (null);
768     if ((style & SWT.BAR) !is 0 && this is parent.menuBar) {
769         parent.setMenuBar (null);
770     }  else {
771         if ((style & SWT.POP_UP) !is 0) {
772             display.removePopup (this);
773         }
774     }
775 }
776 
777 override void releaseWidget () {
778     super.releaseWidget ();
779     if (parent !is null) parent.removeMenu (this);
780     parent = null;
781     cascade = null;
782     imItem = null;
783     imSeparator = null;
784     imHandle = null;
785     if (imageList !is null) imageList.dispose ();
786     imageList = null;
787 }
788 
789 /**
790  * Removes the listener from the collection of listeners who will
791  * be notified when the menu events are generated for the control.
792  *
793  * @param listener the listener which should no longer be notified
794  *
795  * @exception IllegalArgumentException <ul>
796  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
797  * </ul>
798  * @exception SWTException <ul>
799  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
800  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
801  * </ul>
802  *
803  * @see MenuListener
804  * @see #addMenuListener
805  */
806 public void removeMenuListener (MenuListener listener) {
807     checkWidget();
808     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
809     if (eventTable is null) return;
810     eventTable.unhook (SWT.Hide, listener);
811     eventTable.unhook (SWT.Show, listener);
812 }
813 
814 void removeAccelerators (GtkAccelGroup* accelGroup) {
815     MenuItem [] items = getItems ();
816     for (int i = 0; i < items.length; i++) {
817         MenuItem item = items[i];
818         item.removeAccelerators (accelGroup);
819     }
820 }
821 
822 /**
823  * Removes the listener from the collection of listeners who will
824  * be notified when the help events are generated for the control.
825  *
826  * @param listener the listener which should no longer be notified
827  *
828  * @exception IllegalArgumentException <ul>
829  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
830  * </ul>
831  * @exception SWTException <ul>
832  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
833  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
834  * </ul>
835  *
836  * @see HelpListener
837  * @see #addHelpListener
838  */
839 public void removeHelpListener (HelpListener listener) {
840     checkWidget();
841     if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
842     if (eventTable is null) return;
843     eventTable.unhook (SWT.Help, listener);
844 }
845 
846 bool sendHelpEvent (ptrdiff_t helpType) {
847     if (selectedItem !is null && !selectedItem.isDisposed()) {
848         if (selectedItem.hooks (SWT.Help)) {
849             selectedItem.postEvent (SWT.Help);
850             return true;
851         }
852     }
853     if (hooks (SWT.Help)) {
854         postEvent (SWT.Help);
855         return true;
856     }
857     return parent.sendHelpEvent (helpType);
858 }
859 
860 /**
861  * Sets the default menu item to the argument or removes
862  * the default emphasis when the argument is <code>null</code>.
863  *
864  * @param item the default menu item or null
865  *
866  * @exception IllegalArgumentException <ul>
867  *    <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li>
868  * </ul>
869  * @exception SWTException <ul>
870  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
871  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
872  * </ul>
873  */
874 public void setDefaultItem (MenuItem item) {
875     checkWidget();
876 }
877 
878 /**
879  * Enables the receiver if the argument is <code>true</code>,
880  * and disables it otherwise. A disabled menu is typically
881  * not selectable from the user interface and draws with an
882  * inactive or "grayed" look.
883  *
884  * @param enabled the new enabled state
885  *
886  * @exception SWTException <ul>
887  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
888  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
889  * </ul>
890  */
891 public void setEnabled (bool enabled) {
892     checkWidget();
893     if (enabled) {
894         OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_SENSITIVE);
895     } else {
896         OS.GTK_WIDGET_UNSET_FLAGS (handle, OS.GTK_SENSITIVE);
897     }
898 }
899 
900 /**
901  * Sets the location of the receiver, which must be a popup,
902  * to the point specified by the arguments which are relative
903  * to the display.
904  * <p>
905  * Note that this is different from most widgets where the
906  * location of the widget is relative to the parent.
907  * </p><p>
908  * Note that the platform window manager ultimately has control
909  * over the location of popup menus.
910  * </p>
911  *
912  * @param x the new x coordinate for the receiver
913  * @param y the new y coordinate for the receiver
914  *
915  * @exception SWTException <ul>
916  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
917  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
918  * </ul>
919  */
920 public void setLocation (int x, int y) {
921     checkWidget();
922     if ((style & (SWT.BAR | SWT.DROP_DOWN)) !is 0) return;
923     this.x = x;
924     this.y = y;
925     hasLocation = true;
926 }
927 
928 /**
929  * Sets the location of the receiver, which must be a popup,
930  * to the point specified by the argument which is relative
931  * to the display.
932  * <p>
933  * Note that this is different from most widgets where the
934  * location of the widget is relative to the parent.
935  * </p><p>
936  * Note that the platform window manager ultimately has control
937  * over the location of popup menus.
938  * </p>
939  *
940  * @param location the new location for the receiver
941  *
942  * @exception IllegalArgumentException <ul>
943  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
944  * </ul>
945  * @exception SWTException <ul>
946  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
947  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
948  * </ul>
949  *
950  * @since 2.1
951  */
952 public void setLocation (Point location) {
953     checkWidget();
954     if (location is null) error (SWT.ERROR_NULL_ARGUMENT);
955     setLocation (location.x, location.y);
956 }
957 
958 override void setOrientation() {
959     if ((parent.style & SWT.RIGHT_TO_LEFT) !is 0) {
960         if (handle !is null) OS.gtk_widget_set_direction (handle, OS.GTK_TEXT_DIR_RTL);
961     }
962 }
963 
964 /**
965  * Marks the receiver as visible if the argument is <code>true</code>,
966  * and marks it invisible otherwise.
967  * <p>
968  * If one of the receiver's ancestors is not visible or some
969  * other condition makes the receiver not visible, marking
970  * it visible may not actually cause it to be displayed.
971  * </p>
972  *
973  * @param visible the new visibility state
974  *
975  * @exception SWTException <ul>
976  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
977  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
978  * </ul>
979  */
980 public void setVisible (bool visible) {
981     checkWidget();
982     if ((style & (SWT.BAR | SWT.DROP_DOWN)) !is 0) return;
983     if (visible) {
984         display.addPopup (this);
985     } else {
986         display.removePopup (this);
987         _setVisible (false);
988     }
989 }
990 }