Mercurial > projects > dwt-linux
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 '&' 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 '&' can be | |
806 * escaped by doubling it in the string, causing a single | |
807 * '&' 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 } |