1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 // generated automatically - do not change 21 // find conversion definition on APILookup.txt 22 // implement new conversion functionalities on the wrap.utils pakage 23 24 25 module gtk.MenuShell; 26 27 private import gio.MenuModel; 28 private import glib.Str; 29 private import gobject.ObjectG; 30 private import gobject.Signals; 31 private import gtk.Container; 32 private import gtk.MenuItem; 33 private import gtk.Widget; 34 private import gtkc.gtk; 35 public import gtkc.gtktypes; 36 private import std.algorithm; 37 38 39 /** 40 * A #GtkMenuShell is the abstract base class used to derive the 41 * #GtkMenu and #GtkMenuBar subclasses. 42 * 43 * A #GtkMenuShell is a container of #GtkMenuItem objects arranged 44 * in a list which can be navigated, selected, and activated by the 45 * user to perform application functions. A #GtkMenuItem can have a 46 * submenu associated with it, allowing for nested hierarchical menus. 47 * 48 * # Terminology 49 * 50 * A menu item can be “selected”, this means that it is displayed 51 * in the prelight state, and if it has a submenu, that submenu 52 * will be popped up. 53 * 54 * A menu is “active” when it is visible onscreen and the user 55 * is selecting from it. A menubar is not active until the user 56 * clicks on one of its menuitems. When a menu is active, 57 * passing the mouse over a submenu will pop it up. 58 * 59 * There is also is a concept of the current menu and a current 60 * menu item. The current menu item is the selected menu item 61 * that is furthest down in the hierarchy. (Every active menu shell 62 * does not necessarily contain a selected menu item, but if 63 * it does, then the parent menu shell must also contain 64 * a selected menu item.) The current menu is the menu that 65 * contains the current menu item. It will always have a GTK 66 * grab and receive all key presses. 67 */ 68 public class MenuShell : Container 69 { 70 /** the main Gtk struct */ 71 protected GtkMenuShell* gtkMenuShell; 72 73 /** Get the main Gtk struct */ 74 public GtkMenuShell* getMenuShellStruct() 75 { 76 return gtkMenuShell; 77 } 78 79 /** the main Gtk struct as a void* */ 80 protected override void* getStruct() 81 { 82 return cast(void*)gtkMenuShell; 83 } 84 85 protected override void setStruct(GObject* obj) 86 { 87 gtkMenuShell = cast(GtkMenuShell*)obj; 88 super.setStruct(obj); 89 } 90 91 /** 92 * Sets our main struct and passes it to the parent class. 93 */ 94 public this (GtkMenuShell* gtkMenuShell, bool ownedRef = false) 95 { 96 this.gtkMenuShell = gtkMenuShell; 97 super(cast(GtkContainer*)gtkMenuShell, ownedRef); 98 } 99 100 101 /** */ 102 public static GType getType() 103 { 104 return gtk_menu_shell_get_type(); 105 } 106 107 /** 108 * Activates the menu item within the menu shell. 109 * 110 * Params: 111 * menuItem = the #GtkMenuItem to activate 112 * forceDeactivate = if %TRUE, force the deactivation of the 113 * menu shell after the menu item is activated 114 */ 115 public void activateItem(Widget menuItem, bool forceDeactivate) 116 { 117 gtk_menu_shell_activate_item(gtkMenuShell, (menuItem is null) ? null : menuItem.getWidgetStruct(), forceDeactivate); 118 } 119 120 /** 121 * Adds a new #GtkMenuItem to the end of the menu shell's 122 * item list. 123 * 124 * Params: 125 * child = The #GtkMenuItem to add 126 */ 127 public void append(MenuItem child) 128 { 129 gtk_menu_shell_append(gtkMenuShell, (child is null) ? null : cast(GtkWidget*)child.getMenuItemStruct()); 130 } 131 132 /** 133 * Establishes a binding between a #GtkMenuShell and a #GMenuModel. 134 * 135 * The contents of @shell are removed and then refilled with menu items 136 * according to @model. When @model changes, @shell is updated. 137 * Calling this function twice on @shell with different @model will 138 * cause the first binding to be replaced with a binding to the new 139 * model. If @model is %NULL then any previous binding is undone and 140 * all children are removed. 141 * 142 * @with_separators determines if toplevel items (eg: sections) have 143 * separators inserted between them. This is typically desired for 144 * menus but doesn’t make sense for menubars. 145 * 146 * If @action_namespace is non-%NULL then the effect is as if all 147 * actions mentioned in the @model have their names prefixed with the 148 * namespace, plus a dot. For example, if the action “quit” is 149 * mentioned and @action_namespace is “app” then the effective action 150 * name is “app.quit”. 151 * 152 * This function uses #GtkActionable to define the action name and 153 * target values on the created menu items. If you want to use an 154 * action group other than “app” and “win”, or if you want to use a 155 * #GtkMenuShell outside of a #GtkApplicationWindow, then you will need 156 * to attach your own action group to the widget hierarchy using 157 * gtk_widget_insert_action_group(). As an example, if you created a 158 * group with a “quit” action and inserted it with the name “mygroup” 159 * then you would use the action name “mygroup.quit” in your 160 * #GMenuModel. 161 * 162 * For most cases you are probably better off using 163 * gtk_menu_new_from_model() or gtk_menu_bar_new_from_model() or just 164 * directly passing the #GMenuModel to gtk_application_set_app_menu() or 165 * gtk_application_set_menubar(). 166 * 167 * Params: 168 * model = the #GMenuModel to bind to or %NULL to remove 169 * binding 170 * actionNamespace = the namespace for actions in @model 171 * withSeparators = %TRUE if toplevel items in @shell should have 172 * separators between them 173 * 174 * Since: 3.6 175 */ 176 public void bindModel(MenuModel model, string actionNamespace, bool withSeparators) 177 { 178 gtk_menu_shell_bind_model(gtkMenuShell, (model is null) ? null : model.getMenuModelStruct(), Str.toStringz(actionNamespace), withSeparators); 179 } 180 181 /** 182 * Cancels the selection within the menu shell. 183 * 184 * Since: 2.4 185 */ 186 public void cancel() 187 { 188 gtk_menu_shell_cancel(gtkMenuShell); 189 } 190 191 /** 192 * Deactivates the menu shell. 193 * 194 * Typically this results in the menu shell being erased 195 * from the screen. 196 */ 197 public void deactivate() 198 { 199 gtk_menu_shell_deactivate(gtkMenuShell); 200 } 201 202 /** 203 * Deselects the currently selected item from the menu shell, 204 * if any. 205 */ 206 public void deselect() 207 { 208 gtk_menu_shell_deselect(gtkMenuShell); 209 } 210 211 /** 212 * Gets the parent menu shell. 213 * 214 * The parent menu shell of a submenu is the #GtkMenu or #GtkMenuBar 215 * from which it was opened up. 216 * 217 * Returns: the parent #GtkMenuShell 218 * 219 * Since: 3.0 220 */ 221 public Widget getParentShell() 222 { 223 auto p = gtk_menu_shell_get_parent_shell(gtkMenuShell); 224 225 if(p is null) 226 { 227 return null; 228 } 229 230 return ObjectG.getDObject!(Widget)(cast(GtkWidget*) p); 231 } 232 233 /** 234 * Gets the currently selected item. 235 * 236 * Returns: the currently selected item 237 * 238 * Since: 3.0 239 */ 240 public Widget getSelectedItem() 241 { 242 auto p = gtk_menu_shell_get_selected_item(gtkMenuShell); 243 244 if(p is null) 245 { 246 return null; 247 } 248 249 return ObjectG.getDObject!(Widget)(cast(GtkWidget*) p); 250 } 251 252 /** 253 * Returns %TRUE if the menu shell will take the keyboard focus on popup. 254 * 255 * Returns: %TRUE if the menu shell will take the keyboard focus on popup. 256 * 257 * Since: 2.8 258 */ 259 public bool getTakeFocus() 260 { 261 return gtk_menu_shell_get_take_focus(gtkMenuShell) != 0; 262 } 263 264 /** 265 * Adds a new #GtkMenuItem to the menu shell’s item list 266 * at the position indicated by @position. 267 * 268 * Params: 269 * child = The #GtkMenuItem to add 270 * position = The position in the item list where @child 271 * is added. Positions are numbered from 0 to n-1 272 */ 273 public void insert(Widget child, int position) 274 { 275 gtk_menu_shell_insert(gtkMenuShell, (child is null) ? null : child.getWidgetStruct(), position); 276 } 277 278 /** 279 * Adds a new #GtkMenuItem to the beginning of the menu shell's 280 * item list. 281 * 282 * Params: 283 * child = The #GtkMenuItem to add 284 */ 285 public void prepend(Widget child) 286 { 287 gtk_menu_shell_prepend(gtkMenuShell, (child is null) ? null : child.getWidgetStruct()); 288 } 289 290 /** 291 * Select the first visible or selectable child of the menu shell; 292 * don’t select tearoff items unless the only item is a tearoff 293 * item. 294 * 295 * Params: 296 * searchSensitive = if %TRUE, search for the first selectable 297 * menu item, otherwise select nothing if 298 * the first item isn’t sensitive. This 299 * should be %FALSE if the menu is being 300 * popped up initially. 301 * 302 * Since: 2.2 303 */ 304 public void selectFirst(bool searchSensitive) 305 { 306 gtk_menu_shell_select_first(gtkMenuShell, searchSensitive); 307 } 308 309 /** 310 * Selects the menu item from the menu shell. 311 * 312 * Params: 313 * menuItem = The #GtkMenuItem to select 314 */ 315 public void selectItem(Widget menuItem) 316 { 317 gtk_menu_shell_select_item(gtkMenuShell, (menuItem is null) ? null : menuItem.getWidgetStruct()); 318 } 319 320 /** 321 * If @take_focus is %TRUE (the default) the menu shell will take 322 * the keyboard focus so that it will receive all keyboard events 323 * which is needed to enable keyboard navigation in menus. 324 * 325 * Setting @take_focus to %FALSE is useful only for special applications 326 * like virtual keyboard implementations which should not take keyboard 327 * focus. 328 * 329 * The @take_focus state of a menu or menu bar is automatically 330 * propagated to submenus whenever a submenu is popped up, so you 331 * don’t have to worry about recursively setting it for your entire 332 * menu hierarchy. Only when programmatically picking a submenu and 333 * popping it up manually, the @take_focus property of the submenu 334 * needs to be set explicitly. 335 * 336 * Note that setting it to %FALSE has side-effects: 337 * 338 * If the focus is in some other app, it keeps the focus and keynav in 339 * the menu doesn’t work. Consequently, keynav on the menu will only 340 * work if the focus is on some toplevel owned by the onscreen keyboard. 341 * 342 * To avoid confusing the user, menus with @take_focus set to %FALSE 343 * should not display mnemonics or accelerators, since it cannot be 344 * guaranteed that they will work. 345 * 346 * See also gdk_keyboard_grab() 347 * 348 * Params: 349 * takeFocus = %TRUE if the menu shell should take the keyboard 350 * focus on popup 351 * 352 * Since: 2.8 353 */ 354 public void setTakeFocus(bool takeFocus) 355 { 356 gtk_menu_shell_set_take_focus(gtkMenuShell, takeFocus); 357 } 358 359 protected class OnActivateCurrentDelegateWrapper 360 { 361 static OnActivateCurrentDelegateWrapper[] listeners; 362 void delegate(bool, MenuShell) dlg; 363 gulong handlerId; 364 365 this(void delegate(bool, MenuShell) dlg) 366 { 367 this.dlg = dlg; 368 this.listeners ~= this; 369 } 370 371 void remove(OnActivateCurrentDelegateWrapper source) 372 { 373 foreach(index, wrapper; listeners) 374 { 375 if (wrapper.handlerId == source.handlerId) 376 { 377 listeners[index] = null; 378 listeners = std.algorithm.remove(listeners, index); 379 break; 380 } 381 } 382 } 383 } 384 385 /** 386 * An action signal that activates the current menu item within 387 * the menu shell. 388 * 389 * Params: 390 * forceHide = if %TRUE, hide the menu after activating the menu item 391 */ 392 gulong addOnActivateCurrent(void delegate(bool, MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 393 { 394 auto wrapper = new OnActivateCurrentDelegateWrapper(dlg); 395 wrapper.handlerId = Signals.connectData( 396 this, 397 "activate-current", 398 cast(GCallback)&callBackActivateCurrent, 399 cast(void*)wrapper, 400 cast(GClosureNotify)&callBackActivateCurrentDestroy, 401 connectFlags); 402 return wrapper.handlerId; 403 } 404 405 extern(C) static void callBackActivateCurrent(GtkMenuShell* menushellStruct, bool forceHide, OnActivateCurrentDelegateWrapper wrapper) 406 { 407 wrapper.dlg(forceHide, wrapper.outer); 408 } 409 410 extern(C) static void callBackActivateCurrentDestroy(OnActivateCurrentDelegateWrapper wrapper, GClosure* closure) 411 { 412 wrapper.remove(wrapper); 413 } 414 415 protected class OnCancelDelegateWrapper 416 { 417 static OnCancelDelegateWrapper[] listeners; 418 void delegate(MenuShell) dlg; 419 gulong handlerId; 420 421 this(void delegate(MenuShell) dlg) 422 { 423 this.dlg = dlg; 424 this.listeners ~= this; 425 } 426 427 void remove(OnCancelDelegateWrapper source) 428 { 429 foreach(index, wrapper; listeners) 430 { 431 if (wrapper.handlerId == source.handlerId) 432 { 433 listeners[index] = null; 434 listeners = std.algorithm.remove(listeners, index); 435 break; 436 } 437 } 438 } 439 } 440 441 /** 442 * An action signal which cancels the selection within the menu shell. 443 * Causes the #GtkMenuShell::selection-done signal to be emitted. 444 */ 445 gulong addOnCancel(void delegate(MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 446 { 447 auto wrapper = new OnCancelDelegateWrapper(dlg); 448 wrapper.handlerId = Signals.connectData( 449 this, 450 "cancel", 451 cast(GCallback)&callBackCancel, 452 cast(void*)wrapper, 453 cast(GClosureNotify)&callBackCancelDestroy, 454 connectFlags); 455 return wrapper.handlerId; 456 } 457 458 extern(C) static void callBackCancel(GtkMenuShell* menushellStruct, OnCancelDelegateWrapper wrapper) 459 { 460 wrapper.dlg(wrapper.outer); 461 } 462 463 extern(C) static void callBackCancelDestroy(OnCancelDelegateWrapper wrapper, GClosure* closure) 464 { 465 wrapper.remove(wrapper); 466 } 467 468 protected class OnCycleFocusDelegateWrapper 469 { 470 static OnCycleFocusDelegateWrapper[] listeners; 471 void delegate(GtkDirectionType, MenuShell) dlg; 472 gulong handlerId; 473 474 this(void delegate(GtkDirectionType, MenuShell) dlg) 475 { 476 this.dlg = dlg; 477 this.listeners ~= this; 478 } 479 480 void remove(OnCycleFocusDelegateWrapper source) 481 { 482 foreach(index, wrapper; listeners) 483 { 484 if (wrapper.handlerId == source.handlerId) 485 { 486 listeners[index] = null; 487 listeners = std.algorithm.remove(listeners, index); 488 break; 489 } 490 } 491 } 492 } 493 494 /** 495 * A keybinding signal which moves the focus in the 496 * given @direction. 497 * 498 * Params: 499 * direction = the direction to cycle in 500 */ 501 gulong addOnCycleFocus(void delegate(GtkDirectionType, MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 502 { 503 auto wrapper = new OnCycleFocusDelegateWrapper(dlg); 504 wrapper.handlerId = Signals.connectData( 505 this, 506 "cycle-focus", 507 cast(GCallback)&callBackCycleFocus, 508 cast(void*)wrapper, 509 cast(GClosureNotify)&callBackCycleFocusDestroy, 510 connectFlags); 511 return wrapper.handlerId; 512 } 513 514 extern(C) static void callBackCycleFocus(GtkMenuShell* menushellStruct, GtkDirectionType direction, OnCycleFocusDelegateWrapper wrapper) 515 { 516 wrapper.dlg(direction, wrapper.outer); 517 } 518 519 extern(C) static void callBackCycleFocusDestroy(OnCycleFocusDelegateWrapper wrapper, GClosure* closure) 520 { 521 wrapper.remove(wrapper); 522 } 523 524 protected class OnDeactivateDelegateWrapper 525 { 526 static OnDeactivateDelegateWrapper[] listeners; 527 void delegate(MenuShell) dlg; 528 gulong handlerId; 529 530 this(void delegate(MenuShell) dlg) 531 { 532 this.dlg = dlg; 533 this.listeners ~= this; 534 } 535 536 void remove(OnDeactivateDelegateWrapper source) 537 { 538 foreach(index, wrapper; listeners) 539 { 540 if (wrapper.handlerId == source.handlerId) 541 { 542 listeners[index] = null; 543 listeners = std.algorithm.remove(listeners, index); 544 break; 545 } 546 } 547 } 548 } 549 550 /** 551 * This signal is emitted when a menu shell is deactivated. 552 */ 553 gulong addOnDeactivate(void delegate(MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 554 { 555 auto wrapper = new OnDeactivateDelegateWrapper(dlg); 556 wrapper.handlerId = Signals.connectData( 557 this, 558 "deactivate", 559 cast(GCallback)&callBackDeactivate, 560 cast(void*)wrapper, 561 cast(GClosureNotify)&callBackDeactivateDestroy, 562 connectFlags); 563 return wrapper.handlerId; 564 } 565 566 extern(C) static void callBackDeactivate(GtkMenuShell* menushellStruct, OnDeactivateDelegateWrapper wrapper) 567 { 568 wrapper.dlg(wrapper.outer); 569 } 570 571 extern(C) static void callBackDeactivateDestroy(OnDeactivateDelegateWrapper wrapper, GClosure* closure) 572 { 573 wrapper.remove(wrapper); 574 } 575 576 protected class OnInsertDelegateWrapper 577 { 578 static OnInsertDelegateWrapper[] listeners; 579 void delegate(Widget, int, MenuShell) dlg; 580 gulong handlerId; 581 582 this(void delegate(Widget, int, MenuShell) dlg) 583 { 584 this.dlg = dlg; 585 this.listeners ~= this; 586 } 587 588 void remove(OnInsertDelegateWrapper source) 589 { 590 foreach(index, wrapper; listeners) 591 { 592 if (wrapper.handlerId == source.handlerId) 593 { 594 listeners[index] = null; 595 listeners = std.algorithm.remove(listeners, index); 596 break; 597 } 598 } 599 } 600 } 601 602 /** 603 * The ::insert signal is emitted when a new #GtkMenuItem is added to 604 * a #GtkMenuShell. A separate signal is used instead of 605 * GtkContainer::add because of the need for an additional position 606 * parameter. 607 * 608 * The inverse of this signal is the GtkContainer::removed signal. 609 * 610 * Params: 611 * child = the #GtkMenuItem that is being inserted 612 * position = the position at which the insert occurs 613 * 614 * Since: 3.2 615 */ 616 gulong addOnInsert(void delegate(Widget, int, MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 617 { 618 auto wrapper = new OnInsertDelegateWrapper(dlg); 619 wrapper.handlerId = Signals.connectData( 620 this, 621 "insert", 622 cast(GCallback)&callBackInsert, 623 cast(void*)wrapper, 624 cast(GClosureNotify)&callBackInsertDestroy, 625 connectFlags); 626 return wrapper.handlerId; 627 } 628 629 extern(C) static void callBackInsert(GtkMenuShell* menushellStruct, GtkWidget* child, int position, OnInsertDelegateWrapper wrapper) 630 { 631 wrapper.dlg(ObjectG.getDObject!(Widget)(child), position, wrapper.outer); 632 } 633 634 extern(C) static void callBackInsertDestroy(OnInsertDelegateWrapper wrapper, GClosure* closure) 635 { 636 wrapper.remove(wrapper); 637 } 638 639 protected class OnMoveCurrentDelegateWrapper 640 { 641 static OnMoveCurrentDelegateWrapper[] listeners; 642 void delegate(GtkMenuDirectionType, MenuShell) dlg; 643 gulong handlerId; 644 645 this(void delegate(GtkMenuDirectionType, MenuShell) dlg) 646 { 647 this.dlg = dlg; 648 this.listeners ~= this; 649 } 650 651 void remove(OnMoveCurrentDelegateWrapper source) 652 { 653 foreach(index, wrapper; listeners) 654 { 655 if (wrapper.handlerId == source.handlerId) 656 { 657 listeners[index] = null; 658 listeners = std.algorithm.remove(listeners, index); 659 break; 660 } 661 } 662 } 663 } 664 665 /** 666 * An keybinding signal which moves the current menu item 667 * in the direction specified by @direction. 668 * 669 * Params: 670 * direction = the direction to move 671 */ 672 gulong addOnMoveCurrent(void delegate(GtkMenuDirectionType, MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 673 { 674 auto wrapper = new OnMoveCurrentDelegateWrapper(dlg); 675 wrapper.handlerId = Signals.connectData( 676 this, 677 "move-current", 678 cast(GCallback)&callBackMoveCurrent, 679 cast(void*)wrapper, 680 cast(GClosureNotify)&callBackMoveCurrentDestroy, 681 connectFlags); 682 return wrapper.handlerId; 683 } 684 685 extern(C) static void callBackMoveCurrent(GtkMenuShell* menushellStruct, GtkMenuDirectionType direction, OnMoveCurrentDelegateWrapper wrapper) 686 { 687 wrapper.dlg(direction, wrapper.outer); 688 } 689 690 extern(C) static void callBackMoveCurrentDestroy(OnMoveCurrentDelegateWrapper wrapper, GClosure* closure) 691 { 692 wrapper.remove(wrapper); 693 } 694 695 protected class OnMoveSelectedDelegateWrapper 696 { 697 static OnMoveSelectedDelegateWrapper[] listeners; 698 bool delegate(int, MenuShell) dlg; 699 gulong handlerId; 700 701 this(bool delegate(int, MenuShell) dlg) 702 { 703 this.dlg = dlg; 704 this.listeners ~= this; 705 } 706 707 void remove(OnMoveSelectedDelegateWrapper source) 708 { 709 foreach(index, wrapper; listeners) 710 { 711 if (wrapper.handlerId == source.handlerId) 712 { 713 listeners[index] = null; 714 listeners = std.algorithm.remove(listeners, index); 715 break; 716 } 717 } 718 } 719 } 720 721 /** 722 * The ::move-selected signal is emitted to move the selection to 723 * another item. 724 * 725 * Params: 726 * distance = +1 to move to the next item, -1 to move to the previous 727 * 728 * Returns: %TRUE to stop the signal emission, %FALSE to continue 729 * 730 * Since: 2.12 731 */ 732 gulong addOnMoveSelected(bool delegate(int, MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 733 { 734 auto wrapper = new OnMoveSelectedDelegateWrapper(dlg); 735 wrapper.handlerId = Signals.connectData( 736 this, 737 "move-selected", 738 cast(GCallback)&callBackMoveSelected, 739 cast(void*)wrapper, 740 cast(GClosureNotify)&callBackMoveSelectedDestroy, 741 connectFlags); 742 return wrapper.handlerId; 743 } 744 745 extern(C) static int callBackMoveSelected(GtkMenuShell* menushellStruct, int distance, OnMoveSelectedDelegateWrapper wrapper) 746 { 747 return wrapper.dlg(distance, wrapper.outer); 748 } 749 750 extern(C) static void callBackMoveSelectedDestroy(OnMoveSelectedDelegateWrapper wrapper, GClosure* closure) 751 { 752 wrapper.remove(wrapper); 753 } 754 755 protected class OnSelectionDoneDelegateWrapper 756 { 757 static OnSelectionDoneDelegateWrapper[] listeners; 758 void delegate(MenuShell) dlg; 759 gulong handlerId; 760 761 this(void delegate(MenuShell) dlg) 762 { 763 this.dlg = dlg; 764 this.listeners ~= this; 765 } 766 767 void remove(OnSelectionDoneDelegateWrapper source) 768 { 769 foreach(index, wrapper; listeners) 770 { 771 if (wrapper.handlerId == source.handlerId) 772 { 773 listeners[index] = null; 774 listeners = std.algorithm.remove(listeners, index); 775 break; 776 } 777 } 778 } 779 } 780 781 /** 782 * This signal is emitted when a selection has been 783 * completed within a menu shell. 784 */ 785 gulong addOnSelectionDone(void delegate(MenuShell) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 786 { 787 auto wrapper = new OnSelectionDoneDelegateWrapper(dlg); 788 wrapper.handlerId = Signals.connectData( 789 this, 790 "selection-done", 791 cast(GCallback)&callBackSelectionDone, 792 cast(void*)wrapper, 793 cast(GClosureNotify)&callBackSelectionDoneDestroy, 794 connectFlags); 795 return wrapper.handlerId; 796 } 797 798 extern(C) static void callBackSelectionDone(GtkMenuShell* menushellStruct, OnSelectionDoneDelegateWrapper wrapper) 799 { 800 wrapper.dlg(wrapper.outer); 801 } 802 803 extern(C) static void callBackSelectionDoneDestroy(OnSelectionDoneDelegateWrapper wrapper, GClosure* closure) 804 { 805 wrapper.remove(wrapper); 806 } 807 }