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