comparison dwt/widgets/MenuItem.d @ 48:8e9ea24111fd

Menu, MenuItem
author Frank Benoit <benoit@tionex.de>
date Fri, 11 Jan 2008 10:04:18 +0100
parents
children 93981635e709
comparison
equal deleted inserted replaced
47:f646579f309c 48:8e9ea24111fd
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 module dwt.widgets.MenuItem;
12
13
14 import dwt.widgets.Menu;
15 import dwt.widgets.Item;
16 import dwt.widgets.Decorations;
17 import dwt.graphics.Rectangle;
18 import dwt.graphics.Image;
19 import dwt.widgets.ImageList;
20 import dwt.SWT;
21 import dwt.internal.gtk.OS;
22 import dwt.events.ArmListener;
23 import dwt.events.HelpListener;
24 import dwt.events.SelectionListener;
25 import dwt.events.SelectionEvent;
26 import dwt.widgets.TypedListener;
27 import dwt.widgets.Event;
28 import dwt.widgets.Display;
29
30 import tango.stdc.stringz;
31 import tango.text.Util;
32
33 /**
34 * Instances of this class represent a selectable user interface object
35 * that issues notification when pressed and released.
36 * <dl>
37 * <dt><b>Styles:</b></dt>
38 * <dd>CHECK, CASCADE, PUSH, RADIO, SEPARATOR</dd>
39 * <dt><b>Events:</b></dt>
40 * <dd>Arm, Help, Selection</dd>
41 * </dl>
42 * <p>
43 * Note: Only one of the styles CHECK, CASCADE, PUSH, RADIO and SEPARATOR
44 * may be specified.
45 * </p><p>
46 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
47 * </p>
48 */
49 public class MenuItem : Item {
50 Menu parent, menu;
51 GtkAccelGroup* groupHandle;
52 int accelerator;
53
54 /**
55 * Constructs a new instance of this class given its parent
56 * (which must be a <code>Menu</code>) and a style value
57 * describing its behavior and appearance. The item is added
58 * to the end of the items maintained by its parent.
59 * <p>
60 * The style value is either one of the style constants defined in
61 * class <code>SWT</code> which is applicable to instances of this
62 * class, or must be built by <em>bitwise OR</em>'ing together
63 * (that is, using the <code>int</code> "|" operator) two or more
64 * of those <code>SWT</code> style constants. The class description
65 * lists the style constants that are applicable to the class.
66 * Style bits are also inherited from superclasses.
67 * </p>
68 *
69 * @param parent a menu control which will be the parent of the new instance (cannot be null)
70 * @param style the style of control to construct
71 *
72 * @exception IllegalArgumentException <ul>
73 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
74 * </ul>
75 * @exception SWTException <ul>
76 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
77 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
78 * </ul>
79 *
80 * @see SWT#CHECK
81 * @see SWT#CASCADE
82 * @see SWT#PUSH
83 * @see SWT#RADIO
84 * @see SWT#SEPARATOR
85 * @see Widget#checkSubclass
86 * @see Widget#getStyle
87 */
88 public this (Menu parent, int style) {
89 super (parent, checkStyle (style));
90 this.parent = parent;
91 createWidget (parent.getItemCount ());
92 }
93
94 /**
95 * Constructs a new instance of this class given its parent
96 * (which must be a <code>Menu</code>), a style value
97 * describing its behavior and appearance, and the index
98 * at which to place it in the items maintained by its parent.
99 * <p>
100 * The style value is either one of the style constants defined in
101 * class <code>SWT</code> which is applicable to instances of this
102 * class, or must be built by <em>bitwise OR</em>'ing together
103 * (that is, using the <code>int</code> "|" operator) two or more
104 * of those <code>SWT</code> style constants. The class description
105 * lists the style constants that are applicable to the class.
106 * Style bits are also inherited from superclasses.
107 * </p>
108 *
109 * @param parent a menu control which will be the parent of the new instance (cannot be null)
110 * @param style the style of control to construct
111 * @param index the zero-relative index to store the receiver in its parent
112 *
113 * @exception IllegalArgumentException <ul>
114 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
115 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
116 * </ul>
117 * @exception SWTException <ul>
118 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
119 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
120 * </ul>
121 *
122 * @see SWT#CHECK
123 * @see SWT#CASCADE
124 * @see SWT#PUSH
125 * @see SWT#RADIO
126 * @see SWT#SEPARATOR
127 * @see Widget#checkSubclass
128 * @see Widget#getStyle
129 */
130 public this (Menu parent, int style, int index) {
131 super (parent, checkStyle (style));
132 this.parent = parent;
133 int count = parent.getItemCount ();
134 if (!(0 <= index && index <= count)) {
135 error (SWT.ERROR_INVALID_RANGE);
136 }
137 createWidget (index);
138 }
139
140 void addAccelerator (GtkAccelGroup* accelGroup) {
141 updateAccelerator (accelGroup, true);
142 }
143
144 void addAccelerators (GtkAccelGroup* accelGroup) {
145 addAccelerator (accelGroup);
146 if (menu !is null) menu.addAccelerators (accelGroup);
147 }
148
149 /**
150 * Adds the listener to the collection of listeners who will
151 * be notified when the arm events are generated for the control, by sending
152 * it one of the messages defined in the <code>ArmListener</code>
153 * interface.
154 *
155 * @param listener the listener which should be notified
156 *
157 * @exception IllegalArgumentException <ul>
158 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
159 * </ul>
160 * @exception SWTException <ul>
161 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
162 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
163 * </ul>
164 *
165 * @see ArmListener
166 * @see #removeArmListener
167 */
168 public void addArmListener (ArmListener listener) {
169 checkWidget();
170 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
171 TypedListener typedListener = new TypedListener (listener);
172 addListener (SWT.Arm, typedListener);
173 }
174
175 /**
176 * Adds the listener to the collection of listeners who will
177 * be notified when the help events are generated for the control, by sending
178 * it one of the messages defined in the <code>HelpListener</code>
179 * interface.
180 *
181 * @param listener the listener which should be notified
182 *
183 * @exception IllegalArgumentException <ul>
184 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
185 * </ul>
186 * @exception SWTException <ul>
187 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
188 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
189 * </ul>
190 *
191 * @see HelpListener
192 * @see #removeHelpListener
193 */
194 public void addHelpListener (HelpListener listener) {
195 checkWidget();
196 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
197 TypedListener typedListener = new TypedListener (listener);
198 addListener (SWT.Help, typedListener);
199 }
200
201 /**
202 * Adds the listener to the collection of listeners who will
203 * be notified when the menu item is selected by the user, by sending
204 * it one of the messages defined in the <code>SelectionListener</code>
205 * interface.
206 * <p>
207 * When <code>widgetSelected</code> is called, the stateMask field of the event object is valid.
208 * <code>widgetDefaultSelected</code> is not called.
209 * </p>
210 *
211 * @param listener the listener which should be notified when the menu item is selected by the user
212 *
213 * @exception IllegalArgumentException <ul>
214 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
215 * </ul>
216 * @exception SWTException <ul>
217 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
218 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
219 * </ul>
220 *
221 * @see SelectionListener
222 * @see #removeSelectionListener
223 * @see SelectionEvent
224 */
225 public void addSelectionListener (SelectionListener listener) {
226 checkWidget();
227 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
228 TypedListener typedListener = new TypedListener(listener);
229 addListener (SWT.Selection,typedListener);
230 addListener (SWT.DefaultSelection,typedListener);
231 }
232
233 static int checkStyle (int style) {
234 return checkBits (style, SWT.PUSH, SWT.CHECK, SWT.RADIO, SWT.SEPARATOR, SWT.CASCADE, 0);
235 }
236
237 protected void checkSubclass () {
238 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
239 }
240
241 void createHandle (int index) {
242 state |= HANDLE;
243 char [] buffer = "\0";
244 int bits = SWT.CHECK | SWT.RADIO | SWT.PUSH | SWT.SEPARATOR;
245 switch (style & bits) {
246 case SWT.SEPARATOR:
247 handle = OS.gtk_separator_menu_item_new ();
248 break;
249 case SWT.RADIO:
250 /*
251 * Feature in GTK. In GTK, radio button must always be part of
252 * a radio button group. In a GTK radio group, one button is always
253 * selected. This means that it is not possible to have a single
254 * radio button that is unselected. This is necessary to allow
255 * applications to implement their own radio behavior or use radio
256 * buttons outside of radio groups. The fix is to create a hidden
257 * radio button for each radio button we create and add them
258 * to the same group. This allows the visible button to be
259 * unselected.
260 */
261 groupHandle = cast(GtkAccelGroup*) OS.gtk_radio_menu_item_new (null);
262 if (groupHandle is null) error (SWT.ERROR_NO_HANDLES);
263 OS.g_object_ref (groupHandle);
264 OS.gtk_object_sink (cast(GtkObject*)groupHandle);
265 auto group = OS.gtk_radio_menu_item_get_group (cast(GtkRadioMenuItem*) groupHandle);
266 handle = OS.gtk_radio_menu_item_new_with_label (group, buffer.ptr);
267 break;
268 case SWT.CHECK:
269 handle = OS.gtk_check_menu_item_new_with_label (buffer.ptr);
270 break;
271 case SWT.PUSH:
272 default:
273 handle = OS.gtk_image_menu_item_new_with_label (buffer.ptr);
274 break;
275 }
276 if (handle is null) error (SWT.ERROR_NO_HANDLES);
277 if ((style & SWT.SEPARATOR) is 0) {
278 auto label = OS.gtk_bin_get_child (cast(GtkBin*)handle);
279 OS.gtk_accel_label_set_accel_widget (cast(GtkAccelLabel*)label, null);
280 }
281 auto parentHandle = parent.handle;
282 bool enabled = OS.GTK_WIDGET_SENSITIVE (parentHandle);
283 if (!enabled) OS.GTK_WIDGET_SET_FLAGS (parentHandle, OS.GTK_SENSITIVE);
284 OS.gtk_menu_shell_insert (cast(GtkMenuShell*)parentHandle, handle, index);
285 if (!enabled) OS.GTK_WIDGET_UNSET_FLAGS (parentHandle, OS.GTK_SENSITIVE);
286 OS.gtk_widget_show (handle);
287 }
288
289 void fixMenus (Decorations newParent) {
290 if (menu !is null) menu.fixMenus (newParent);
291 }
292
293 /**
294 * Returns the widget accelerator. An accelerator is the bit-wise
295 * OR of zero or more modifier masks and a key. Examples:
296 * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>.
297 * The default value is zero, indicating that the menu item does
298 * not have an accelerator.
299 *
300 * @return the accelerator or 0
301 *
302 * </ul>
303 * @exception SWTException <ul>
304 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
305 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
306 * </ul>
307 */
308 public int getAccelerator () {
309 checkWidget();
310 return accelerator;
311 }
312
313 GtkAccelGroup* getAccelGroup () {
314 Menu menu = parent;
315 while (menu !is null && menu.cascade !is null) {
316 menu = menu.cascade.parent;
317 }
318 if (menu is null) return null;
319 Decorations shell = menu.parent;
320 return shell.menuBar is menu ? shell.accelGroup : null;
321 }
322
323 /*public*/ Rectangle getBounds () {
324 checkWidget();
325 if (!OS.GTK_WIDGET_MAPPED (handle)) {
326 return new Rectangle (0, 0, 0, 0);
327 }
328 int x = OS.GTK_WIDGET_X (handle);
329 int y = OS.GTK_WIDGET_Y (handle);
330 int width = OS.GTK_WIDGET_WIDTH (handle);
331 int height = OS.GTK_WIDGET_HEIGHT (handle);
332 return new Rectangle (x, y, width, height);
333 }
334
335 /**
336 * Returns <code>true</code> if the receiver is enabled, and
337 * <code>false</code> otherwise. A disabled menu item is typically
338 * not selectable from the user interface and draws with an
339 * inactive or "grayed" look.
340 *
341 * @return the receiver's enabled state
342 *
343 * @exception SWTException <ul>
344 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
345 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
346 * </ul>
347 *
348 * @see #isEnabled
349 */
350 public bool getEnabled () {
351 checkWidget();
352 return OS.GTK_WIDGET_SENSITIVE (handle);
353 }
354
355 /**
356 * Returns the receiver's cascade menu if it has one or null
357 * if it does not. Only <code>CASCADE</code> menu items can have
358 * a pull down menu. The sequence of key strokes, button presses
359 * and/or button releases that are used to request a pull down
360 * menu is platform specific.
361 *
362 * @return the receiver's menu
363 *
364 * @exception SWTException <ul>
365 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
366 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
367 * </ul>
368 */
369 public Menu getMenu () {
370 checkWidget();
371 return menu;
372 }
373
374 /**
375 * Returns the receiver's parent, which must be a <code>Menu</code>.
376 *
377 * @return the receiver's parent
378 *
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 Menu getParent () {
385 checkWidget();
386 return parent;
387 }
388
389 /**
390 * Returns <code>true</code> if the receiver is selected,
391 * and false otherwise.
392 * <p>
393 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
394 * it is selected when it is checked.
395 *
396 * @return the selection state
397 *
398 * @exception SWTException <ul>
399 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
400 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
401 * </ul>
402 */
403 public bool getSelection () {
404 checkWidget();
405 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return false;
406 return cast(bool)OS.gtk_check_menu_item_get_active(cast(GtkCheckMenuItem*)handle);
407 }
408
409 override int /*long*/ gtk_activate (GtkWidget* widget) {
410 if ((style & SWT.CASCADE) !is 0 && menu !is null) return 0;
411 /*
412 * Bug in GTK. When an ancestor menu is disabled and
413 * the user types an accelerator key, GTK delivers the
414 * the activate signal even though the menu item cannot
415 * be invoked using the mouse. The fix is to ignore
416 * activate signals when an ancestor menu is disabled.
417 */
418 if (!isEnabled ()) return 0;
419 Event event = new Event ();
420 auto ptr = OS.gtk_get_current_event ();
421 if (ptr !is null) {
422 GdkEvent* gdkEvent = ptr;
423 switch (gdkEvent.type) {
424 case OS.GDK_KEY_PRESS:
425 case OS.GDK_KEY_RELEASE:
426 case OS.GDK_BUTTON_PRESS:
427 case OS.GDK_2BUTTON_PRESS:
428 case OS.GDK_BUTTON_RELEASE: {
429 int state;
430 OS.gdk_event_get_state (ptr, &state);
431 setInputState (event, state);
432 break;
433 }
434 }
435 OS.gdk_event_free (ptr);
436 }
437 if ((style & SWT.RADIO) !is 0) {
438 if ((parent.getStyle () & SWT.NO_RADIO_GROUP) is 0) {
439 selectRadio ();
440 }
441 }
442 postEvent (SWT.Selection, event);
443 return 0;
444 }
445
446 override int /*long*/ gtk_select (int /*long*/ item) {
447 parent.selectedItem = this;
448 sendEvent (SWT.Arm);
449 return 0;
450 }
451
452 override int /*long*/ gtk_show_help (GtkWidget* widget, int /*long*/ helpType) {
453 bool handled = hooks (SWT.Help);
454 if (handled) {
455 postEvent (SWT.Help);
456 } else {
457 handled = parent.sendHelpEvent (helpType);
458 }
459 if (handled) {
460 OS.gtk_menu_shell_deactivate (cast(GtkMenuShell*)parent.handle);
461 return 1;
462 }
463 return 0;
464 }
465
466 void hookEvents () {
467 super.hookEvents ();
468 OS.g_signal_connect_closure (handle, OS.activate.ptr, display.closures [ACTIVATE], false);
469 OS.g_signal_connect_closure (handle, OS.select.ptr, display.closures [SELECT], false);
470 OS.g_signal_connect_closure_by_id (handle, display.signalIds [SHOW_HELP], 0, display.closures [SHOW_HELP], false);
471 }
472
473 /**
474 * Returns <code>true</code> if the receiver is enabled and all
475 * of the receiver's ancestors are enabled, and <code>false</code>
476 * otherwise. A disabled menu item is typically not selectable from the
477 * user interface and draws with an inactive or "grayed" look.
478 *
479 * @return the receiver's enabled state
480 *
481 * @exception SWTException <ul>
482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
484 * </ul>
485 *
486 * @see #getEnabled
487 */
488 public bool isEnabled () {
489 return getEnabled () && parent.isEnabled ();
490 }
491
492 void releaseChildren (bool destroy) {
493 if (menu !is null) {
494 menu.release (false);
495 menu = null;
496 }
497 super.releaseChildren (destroy);
498 }
499
500 void releaseParent () {
501 super.releaseParent ();
502 if (menu !is null) {
503 if (menu.selectedItem is this) menu.selectedItem = null;
504 menu.dispose ();
505 }
506 menu = null;
507 }
508
509 void releaseWidget () {
510 super.releaseWidget ();
511 auto accelGroup = getAccelGroup ();
512 if (accelGroup !is null) removeAccelerator (accelGroup);
513 if (groupHandle !is null) OS.g_object_unref (groupHandle);
514 groupHandle = null;
515 accelerator = 0;
516 parent = null;
517 }
518
519 void removeAccelerator (GtkAccelGroup* accelGroup) {
520 updateAccelerator (accelGroup, false);
521 }
522
523 void removeAccelerators (GtkAccelGroup* accelGroup) {
524 removeAccelerator (accelGroup);
525 if (menu !is null) menu.removeAccelerators (accelGroup);
526 }
527
528 /**
529 * Removes the listener from the collection of listeners who will
530 * be notified when the arm events are generated for the control.
531 *
532 * @param listener the listener which should no longer be notified
533 *
534 * @exception IllegalArgumentException <ul>
535 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
536 * </ul>
537 * @exception SWTException <ul>
538 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
539 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
540 * </ul>
541 *
542 * @see ArmListener
543 * @see #addArmListener
544 */
545 public void removeArmListener (ArmListener listener) {
546 checkWidget();
547 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
548 if (eventTable is null) return;
549 eventTable.unhook (SWT.Arm, listener);
550 }
551
552 /**
553 * Removes the listener from the collection of listeners who will
554 * be notified when the help events are generated for the control.
555 *
556 * @param listener the listener which should no longer be notified
557 *
558 * @exception IllegalArgumentException <ul>
559 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
560 * </ul>
561 * @exception SWTException <ul>
562 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
563 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
564 * </ul>
565 *
566 * @see HelpListener
567 * @see #addHelpListener
568 */
569 public void removeHelpListener (HelpListener listener) {
570 checkWidget();
571 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
572 if (eventTable is null) return;
573 eventTable.unhook (SWT.Help, listener);
574 }
575
576 /**
577 * Removes the listener from the collection of listeners who will
578 * be notified when the control is selected by the user.
579 *
580 * @param listener the listener which should no longer be notified
581 *
582 * @exception IllegalArgumentException <ul>
583 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
584 * </ul>
585 * @exception SWTException <ul>
586 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
587 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
588 * </ul>
589 *
590 * @see SelectionListener
591 * @see #addSelectionListener
592 */
593 public void removeSelectionListener (SelectionListener listener) {
594 checkWidget();
595 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT);
596 if (eventTable is null) return;
597 eventTable.unhook (SWT.Selection, listener);
598 eventTable.unhook (SWT.DefaultSelection,listener);
599 }
600 void selectRadio () {
601 int index = 0;
602 MenuItem [] items = parent.getItems ();
603 while (index < items.length && items [index] !is this) index++;
604 int i = index - 1;
605 while (i >= 0 && items [i].setRadioSelection (false)) --i;
606 int j = index + 1;
607 while (j < items.length && items [j].setRadioSelection (false)) j++;
608 setSelection (true);
609 }
610 /**
611 * Sets the widget accelerator. An accelerator is the bit-wise
612 * OR of zero or more modifier masks and a key. Examples:
613 * <code>SWT.MOD1 | SWT.MOD2 | 'T', SWT.MOD3 | SWT.F2</code>.
614 * <code>SWT.CONTROL | SWT.SHIFT | 'T', SWT.ALT | SWT.F2</code>.
615 * The default value is zero, indicating that the menu item does
616 * not have an accelerator.
617 *
618 * @param accelerator an integer that is the bit-wise OR of masks and a key
619 *
620 * </ul>
621 * @exception SWTException <ul>
622 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
623 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
624 * </ul>
625 */
626 public void setAccelerator (int accelerator) {
627 checkWidget();
628 if (this.accelerator is accelerator) return;
629 auto accelGroup = getAccelGroup ();
630 if (accelGroup !is null) removeAccelerator (accelGroup);
631 this.accelerator = accelerator;
632 if (accelGroup !is null) addAccelerator (accelGroup);
633 }
634
635 /**
636 * Enables the receiver if the argument is <code>true</code>,
637 * and disables it otherwise. A disabled menu item is typically
638 * not selectable from the user interface and draws with an
639 * inactive or "grayed" look.
640 *
641 * @param enabled the new enabled state
642 *
643 * @exception SWTException <ul>
644 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
645 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
646 * </ul>
647 */
648 public void setEnabled (bool enabled) {
649 checkWidget();
650 if (OS.GTK_WIDGET_SENSITIVE (handle) is enabled) return;
651 auto accelGroup = getAccelGroup ();
652 if (accelGroup !is null) removeAccelerator (accelGroup);
653 OS.gtk_widget_set_sensitive (handle, enabled);
654 if (accelGroup !is null) addAccelerator (accelGroup);
655 }
656
657 /**
658 * Sets the image the receiver will display to the argument.
659 * <p>
660 * Note: This operation is a hint and is not supported on
661 * platforms that do not have this concept (for example, Windows NT).
662 * </p>
663 *
664 * @param image the image to display
665 *
666 * @exception SWTException <ul>
667 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
668 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
669 * </ul>
670 */
671 public void setImage (Image image) {
672 checkWidget();
673 if ((style & SWT.SEPARATOR) !is 0) return;
674 super.setImage (image);
675 if (!OS.GTK_IS_IMAGE_MENU_ITEM (cast(GTypeInstance*)handle)) return;
676 if (image !is null) {
677 ImageList imageList = parent.imageList;
678 if (imageList is null) imageList = parent.imageList = new ImageList ();
679 int imageIndex = imageList.indexOf (image);
680 if (imageIndex is -1) {
681 imageIndex = imageList.add (image);
682 } else {
683 imageList.put (imageIndex, image);
684 }
685 auto pixbuf = imageList.getPixbuf (imageIndex);
686 auto imageHandle = OS.gtk_image_new_from_pixbuf (pixbuf);
687 OS.gtk_image_menu_item_set_image (cast(GtkImageMenuItem*)handle, imageHandle);
688 OS.gtk_widget_show (imageHandle);
689 } else {
690 OS.gtk_image_menu_item_set_image (cast(GtkImageMenuItem*)handle, null);
691 }
692 }
693
694 /**
695 * Sets the receiver's pull down menu to the argument.
696 * Only <code>CASCADE</code> menu items can have a
697 * pull down menu. The sequence of key strokes, button presses
698 * and/or button releases that are used to request a pull down
699 * menu is platform specific.
700 * <p>
701 * Note: Disposing of a menu item that has a pull down menu
702 * will dispose of the menu. To avoid this behavior, set the
703 * menu to null before the menu item is disposed.
704 * </p>
705 *
706 * @param menu the new pull down menu
707 *
708 * @exception IllegalArgumentException <ul>
709 * <li>ERROR_MENU_NOT_DROP_DOWN - if the menu is not a drop down menu</li>
710 * <li>ERROR_MENUITEM_NOT_CASCADE - if the menu item is not a <code>CASCADE</code></li>
711 * <li>ERROR_INVALID_ARGUMENT - if the menu has been disposed</li>
712 * <li>ERROR_INVALID_PARENT - if the menu is not in the same widget tree</li>
713 * </ul>
714 * @exception SWTException <ul>
715 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
716 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
717 * </ul>
718 */
719 public void setMenu (Menu menu) {
720 checkWidget ();
721
722 /* Check to make sure the new menu is valid */
723 if ((style & SWT.CASCADE) is 0) {
724 error (SWT.ERROR_MENUITEM_NOT_CASCADE);
725 }
726 if (menu !is null) {
727 if ((menu.style & SWT.DROP_DOWN) is 0) {
728 error (SWT.ERROR_MENU_NOT_DROP_DOWN);
729 }
730 if (menu.parent !is parent.parent) {
731 error (SWT.ERROR_INVALID_PARENT);
732 }
733 }
734
735 /* Assign the new menu */
736 Menu oldMenu = this.menu;
737 if (oldMenu is menu) return;
738 auto accelGroup = getAccelGroup ();
739 if (accelGroup !is null) removeAccelerators (accelGroup);
740 if (oldMenu !is null) {
741 oldMenu.cascade = null;
742 /*
743 * Add a reference to the menu we are about
744 * to replace or GTK will destroy it.
745 */
746 OS.g_object_ref (oldMenu.handle);
747 OS.gtk_menu_item_remove_submenu (cast(GtkMenuItem*)handle);
748 }
749 if ((this.menu = menu) !is null) {
750 menu.cascade = this;
751 OS.gtk_menu_item_set_submenu (cast(GtkMenuItem*)handle, menu.handle);
752 }
753 if (accelGroup !is null) addAccelerators (accelGroup);
754 }
755
756 void setOrientation() {
757 if ((parent.style & SWT.RIGHT_TO_LEFT) !is 0) {
758 if (handle !is null) {
759 OS.gtk_widget_set_direction (handle, OS.GTK_TEXT_DIR_RTL);
760 OS.gtk_container_forall (cast(GtkContainer*)handle, cast(GtkCallback)&Display.setDirectionProcFunc, cast(void*)OS.GTK_TEXT_DIR_RTL);
761 }
762 }
763 }
764
765 bool setRadioSelection (bool value) {
766 if ((style & SWT.RADIO) is 0) return false;
767 if (getSelection () !is value) {
768 setSelection (value);
769 postEvent (SWT.Selection);
770 }
771 return true;
772 }
773
774 /**
775 * Sets the selection state of the receiver.
776 * <p>
777 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
778 * it is selected when it is checked.
779 *
780 * @param selected the new selection state
781 *
782 * @exception SWTException <ul>
783 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
784 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
785 * </ul>
786 */
787 public void setSelection (bool selected) {
788 checkWidget();
789 if ((style & (SWT.CHECK | SWT.RADIO)) is 0) return;
790 OS.g_signal_handlers_block_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, cast(void*)ACTIVATE);
791 OS.gtk_check_menu_item_set_active (cast(GtkCheckMenuItem*)handle, selected);
792 if ((style & SWT.RADIO) !is 0) OS.gtk_check_menu_item_set_active (cast(GtkCheckMenuItem*)groupHandle, !selected);
793 OS.g_signal_handlers_unblock_matched (handle, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, cast(void*)ACTIVATE);
794 }
795
796 /**
797 * Sets the receiver's text. The string may include
798 * the mnemonic character and accelerator text.
799 * <p>
800 * Mnemonics are indicated by an '&amp;' that causes the next
801 * character to be the mnemonic. When the user presses a
802 * key sequence that matches the mnemonic, a selection
803 * event occurs. On most platforms, the mnemonic appears
804 * underlined but may be emphasised in a platform specific
805 * manner. The mnemonic indicator character '&amp;' can be
806 * escaped by doubling it in the string, causing a single
807 * '&amp;' to be displayed.
808 * </p>
809 * <p>
810 * Accelerator text is indicated by the '\t' character.
811 * On platforms that support accelerator text, the text
812 * that follows the '\t' character is displayed to the user,
813 * typically indicating the key stroke that will cause
814 * the item to become selected. On most platforms, the
815 * accelerator text appears right aligned in the menu.
816 * Setting the accelerator text does not install the
817 * accelerator key sequence. The accelerator key sequence
818 * is installed using #setAccelerator.
819 * </p>
820 *
821 * @param string the new text
822 *
823 * @exception IllegalArgumentException <ul>
824 * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
825 * </ul>
826 * @exception SWTException <ul>
827 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
828 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
829 * </ul>
830 *
831 * @see #setAccelerator
832 */
833 public void setText (char[] string) {
834 checkWidget();
835 if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
836 if ((style & SWT.SEPARATOR) !is 0) return;
837 if (text ==/*eq*/ string) return;
838 super.setText (string);
839 char[] accelString = "";
840 int index = locate( string, '\t');
841 if( index == string.length ) index = -1;
842 if (index !is -1) {
843 accelString = string[ index .. string.length ];
844 string = string[ 0 .. index ];
845 }
846 char [] chars = fixMnemonic (string);
847 char* buffer = toStringz( chars );
848 auto label = OS.gtk_bin_get_child (cast(GtkBin*)handle);
849 OS.gtk_label_set_text_with_mnemonic (cast(GtkLabel*)label, buffer);
850
851 auto ptr = cast(char*)OS.g_malloc (accelString.length + 1);
852 ptr[ 0 .. accelString.length ] = accelString;
853 ptr[ accelString.length ] = '\0';
854
855 auto oldPtr = OS.GTK_ACCEL_LABEL_GET_ACCEL_STRING (cast(GtkAccelLabel*)label);
856 OS.GTK_ACCEL_LABEL_SET_ACCEL_STRING (cast(GtkAccelLabel*)label, ptr);
857 if (oldPtr !is null) OS.g_free (oldPtr);
858 }
859
860 void updateAccelerator (GtkAccelGroup* accelGroup, bool add) {
861 if (accelerator is 0 || !getEnabled ()) return;
862 int mask = 0;
863 if ((accelerator & SWT.ALT) !is 0) mask |= OS.GDK_MOD1_MASK;
864 if ((accelerator & SWT.SHIFT) !is 0) mask |= OS.GDK_SHIFT_MASK;
865 if ((accelerator & SWT.CONTROL) !is 0) mask |= OS.GDK_CONTROL_MASK;
866 int keysym = accelerator & SWT.KEY_MASK;
867 int newKey = Display.untranslateKey (keysym);
868 if (newKey !is 0) {
869 keysym = newKey;
870 } else {
871 switch (keysym) {
872 case '\r': keysym = OS.GDK_Return; break;
873 default: keysym = Display.wcsToMbcs (cast(char) keysym);
874 }
875 }
876 /* When accel_key is zero, it causes GTK warnings */
877 if (keysym !is 0) {
878 if (add) {
879 OS.gtk_widget_add_accelerator (handle, OS.activate.ptr, accelGroup, keysym, mask, OS.GTK_ACCEL_VISIBLE);
880 } else {
881 OS.gtk_widget_remove_accelerator (handle, accelGroup, keysym, mask);
882 }
883 }
884 }
885 }