"Fossies" - the Fresh Open Source Software Archive

Member "gambas-3.16.3/gb.gtk/src/gmenu.cpp" (7 Sep 2021, 26732 Bytes) of package /linux/misc/gambas-3.16.3.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gmenu.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.16.2_vs_3.16.3.

    1 /***************************************************************************
    2 
    3   gmenu.cpp
    4 
    5   (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com>
    6 
    7   This program is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU General Public License as published by
    9   the Free Software Foundation; either version 2, or (at your option)
   10   any later version.
   11 
   12   This program is distributed in the hope that it will be useful,
   13   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15   GNU General Public License for more details.
   16 
   17   You should have received a copy of the GNU General Public License
   18   along with this program; if not, write to the Free Software
   19   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   20   MA 02110-1301, USA.
   21 
   22 ***************************************************************************/
   23 
   24 #include "widgets.h"
   25 #include "gmainwindow.h"
   26 #include "gapplication.h"
   27 #include "gdesktop.h"
   28 #include "gmouse.h"
   29 #include "gmenu.h"
   30 #include "CStyle.h"
   31 
   32 //#define DEBUG_DELETE 1
   33 
   34 typedef
   35     struct {
   36         int x;
   37         int y;
   38         }
   39     MenuPosition;
   40 
   41 gMenu *gMenu::_current_popup = NULL;
   42 int gMenu::_in_popup = 0;
   43 int gMenu::_popup_count = 0;
   44 
   45 static GList *menus = NULL;
   46 static GList *menus_destroyed = NULL;
   47 
   48 static gint my_menu_shell_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
   49 {
   50     GtkWidgetClass *klass = GTK_WIDGET_GET_CLASS(widget);
   51     GtkWidget *menu_item;
   52     gMenu *menu;
   53 
   54     if (event->mode == GDK_CROSSING_GTK_GRAB ||
   55             event->mode == GDK_CROSSING_GTK_UNGRAB ||
   56             event->mode == GDK_CROSSING_STATE_CHANGED)
   57         goto __PREVIOUS;
   58 
   59     menu_item = gtk_get_event_widget((GdkEvent*) event);
   60     if (!menu_item)
   61         goto __PREVIOUS;
   62     
   63     menu = (gMenu *)g_object_get_data(G_OBJECT(menu_item), "gambas-menu");
   64     if (menu)
   65         menu->ensureChildMenu();
   66     
   67 __PREVIOUS:
   68 
   69     if (klass->_gtk_reserved6)
   70         return ((gint (*)(GtkWidget *, GdkEventCrossing *))klass->_gtk_reserved6)(widget, event);
   71     else
   72         return 0;
   73 }
   74 
   75 static void patch_classes(void)
   76 {
   77     GtkWidgetClass *klass;
   78     
   79     klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_SHELL);
   80     if (klass->enter_notify_event != my_menu_shell_enter_notify)
   81     {
   82         //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event);
   83         klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event;
   84         klass->enter_notify_event = my_menu_shell_enter_notify;
   85     }
   86     
   87     klass = (GtkWidgetClass *)g_type_class_peek(GTK_TYPE_MENU_BAR);
   88     if (klass->enter_notify_event != my_menu_shell_enter_notify)
   89     {
   90         //fprintf(stderr, "patch_class: %p\n", klass->enter_notify_event);
   91         klass->_gtk_reserved6 = (void (*)())klass->enter_notify_event;
   92         klass->enter_notify_event = my_menu_shell_enter_notify;
   93     }
   94 }
   95 
   96 
   97 static void cb_destroy(GtkWidget *object, gMenu *data)
   98 {
   99     if (data->ignoreDestroy()) 
  100         return;
  101     
  102     delete data;
  103 }
  104 
  105 static void cb_activate(GtkMenuItem *menuitem, gMenu *data)
  106 {
  107     if (data->ignoreActivate()) 
  108         return;
  109     
  110     if (data->_popup)
  111         return;
  112 
  113     if (data->radio())
  114         data->updateRadio();
  115     else if (data->toggle())
  116         data->updateChecked();
  117     else if (data->checked())
  118     {
  119         data->_ignore_activate = true;
  120         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), true);
  121     }
  122     
  123     if (data->onClick)
  124     {
  125         //fprintf(stderr, "cb_activate: %s\n", data->name());
  126         data->onClick(data);
  127     }
  128 }
  129 
  130 static void cb_size_allocate(GtkWidget *menu, GdkRectangle *allocation, gMenu *data)
  131 {
  132     //fprintf(stderr, "cb_size_allocate: %s %d x %d (%d)\n", data->name(), allocation->width, allocation->height, gtk_widget_get_mapped(menu));
  133     
  134     if (!data->_opened)
  135     {
  136         data->_opened = true;
  137         if (data->onShow) (*data->onShow)(data);
  138         //data->hideSeparators();
  139     }
  140 }
  141 
  142 static gboolean cb_map(GtkWidget *menu, gMenu *data)
  143 {
  144     //fprintf(stderr, "cb_map: >>> %s %d\n", data->name(), data->_mapping);
  145 
  146     if (data->_mapping)
  147         return false;
  148 
  149     data->_mapping = true;
  150     
  151     data->hideSeparators();
  152     gtk_widget_hide(menu);
  153     gtk_widget_show(menu);
  154     //gtk_menu_reposition(GTK_MENU(menu));
  155     
  156     data->_mapping = false;
  157 
  158     //fprintf(stderr, "cb_map: <<<\n");
  159     return false;
  160 }
  161 
  162 static gboolean cb_unmap(GtkWidget *menu, gMenu *data)
  163 {
  164     //fprintf(stderr, "cb_unmap: >>> %s %d\n", data->name(), data->_mapping);
  165     
  166     if (data->_mapping)
  167         return false;
  168 
  169     data->_opened = false;
  170     if (data->onHide) (*data->onHide)(data);
  171 
  172     //fprintf(stderr, "cb_unmap: <<<\n");
  173     return false;
  174 }
  175 
  176 static int get_menu_pos(GtkWidget *menu)
  177 {
  178     GList *children, *iter;
  179     int pos;
  180     
  181     if (!gtk_widget_get_parent(menu))
  182     {
  183         //g_debug("get_menu_pos: no parent for menu %p", menu);
  184         return -1;
  185     }
  186     
  187     children = gtk_container_get_children(GTK_CONTAINER(gtk_widget_get_parent(menu)));
  188   iter = g_list_first(children);
  189   
  190   for(pos = 0;; pos++) 
  191   {
  192     if (iter->data == (gpointer)menu) 
  193       break; 
  194     iter = g_list_next(iter);
  195   }
  196   
  197   g_list_free(children);
  198 
  199   return pos;
  200 }
  201 
  202 
  203 void gMenu::update()
  204 {
  205     GtkMenuShell *shell = NULL;
  206     gint pos;
  207     int size;
  208     int ds = gDesktop::scale();
  209     char *buf;
  210     
  211     if (!_text || !*_text)
  212         _style = SEPARATOR;
  213     else if (!_popup && (_radio || _toggle || _checked))
  214         _style = CHECK;
  215     else
  216         _style = NORMAL;
  217     
  218     if (_no_update)
  219         return;
  220     
  221     //g_debug("%p: START UPDATE (menu = %p _popup = %p parent = %p)", this, menu);
  222     
  223     if (_style != _oldstyle)
  224     {
  225         //fprintf(stderr, "update %s\n", name());
  226     
  227         if (_popup)
  228         {
  229             g_object_ref(G_OBJECT(_popup));
  230             if (_style == NORMAL)
  231                 gtk_menu_item_set_submenu(menu, NULL);
  232         }
  233 
  234         if (menu)
  235         {
  236             pos = get_menu_pos(GTK_WIDGET(menu));
  237             //shell = (GtkMenuShell*)GTK_WIDGET(menu)->parent;
  238             if (_style != NOTHING)
  239                 _ignore_destroy = true;
  240             gtk_widget_hide(GTK_WIDGET(menu));
  241             gtk_widget_destroy(GTK_WIDGET(menu));
  242             _shortcut_key = 0;
  243             _shortcut_mods = (GdkModifierType)0;
  244             //g_debug("%p: delete old menu/separator", this);
  245         }
  246         else
  247         {
  248             pos = -1;
  249             //shell = NULL;
  250         }
  251         
  252         if (_style != NOTHING)
  253         {
  254             if (_style == SEPARATOR)
  255             {
  256                 menu = (GtkMenuItem *)gtk_separator_menu_item_new();
  257 
  258                 hbox = NULL;
  259                 label = NULL;
  260                 shlabel = NULL;
  261                 image = NULL;
  262                 
  263                 #ifdef GTK3
  264                 #else
  265                     GtkRequisition req;
  266                     gtk_widget_size_request(GTK_WIDGET(menu), &req);
  267                     if (req.height > 5)
  268                         gtk_widget_set_size_request(GTK_WIDGET(menu), -1, 5);
  269                 #endif
  270                 //g_debug("%p: create new separator %p", this, menu);
  271             }
  272             else
  273             {
  274                 if (_style == CHECK)
  275                 {
  276                     menu = (GtkMenuItem *)gtk_check_menu_item_new();
  277                     if (radio())
  278                         gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(menu), true);
  279                     if (checked())
  280                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), true);
  281                 }
  282                 else
  283                 {
  284                     menu = (GtkMenuItem *)gtk_menu_item_new();
  285                 }
  286                 //g_debug("%p: create new menu %p", this, menu);
  287                 
  288                 if (!_toplevel)
  289                 {
  290                     #ifdef GTK3
  291                     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, ds);
  292                     #else
  293                     hbox = gtk_hbox_new(false, ds);
  294                     #endif
  295                     //set_gdk_bg_color(hbox, 0xFF0000);
  296                     
  297                     image = gtk_image_new();
  298                     //check = gtk_fixed_new();
  299                     label = gtk_label_new_with_mnemonic("");
  300                     shlabel = gtk_label_new("");
  301                     
  302                     #if GTK_CHECK_VERSION(3, 16, 0)
  303                     gtk_label_set_xalign(GTK_LABEL(shlabel), 0);
  304                     #else
  305                     gtk_misc_set_alignment(GTK_MISC(shlabel), 0, 0.5);
  306                     #endif
  307                     gtk_size_group_add_widget(parentMenu()->getSizeGroup(), shlabel);
  308                     
  309                     size = window()->font()->height();
  310 
  311                     //gtk_widget_set_size_request(check, size, size);
  312                     //ON_DRAW(check, this, cb_check_expose, cb_check_draw);
  313                     //g_signal_connect_after(G_OBJECT(check), "expose-event", G_CALLBACK(cb_check_expose), (gpointer)this);
  314 
  315                     gtk_widget_set_size_request(image, size, size);
  316                     
  317                     gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(hbox));
  318                     //gtk_box_pack_start(GTK_BOX(hbox), check, false, false, 0);
  319                     gtk_box_pack_start(GTK_BOX(hbox), image, false, false, 0);
  320                     gtk_box_pack_start(GTK_BOX(hbox), label, false, false, 0);
  321                     gtk_box_pack_end(GTK_BOX(hbox), shlabel, false, false, 0);
  322                 }
  323                 else
  324                 {
  325                     hbox = NULL;
  326                     shlabel = NULL;
  327                     image = NULL;
  328                     //check = NULL;
  329                     label = gtk_label_new_with_mnemonic("");
  330                     gtk_container_add(GTK_CONTAINER(menu), label);
  331                 }
  332                 
  333                 if (_popup)
  334                 {
  335                     gtk_menu_item_set_submenu(menu, GTK_WIDGET(_popup));
  336                     g_object_unref(G_OBJECT(_popup));
  337                 }
  338                     
  339                 
  340                 //set_gdk_fg_color(label, get_gdk_fg_color(GTK_WIDGET(shell)));
  341                 //set_gdk_bg_color(label, get_gdk_bg_color(GTK_WIDGET(shell)));
  342             }
  343             
  344             gtk_widget_show_all(GTK_WIDGET(menu));
  345             
  346             if (_toplevel)
  347             {
  348                 gMainWindow *win = (gMainWindow *)pr;
  349                 
  350                 //set_gdk_fg_color(GTK_WIDGET(menu), win->foreground());
  351                 //set_gdk_bg_color(GTK_WIDGET(menu), win->background());    
  352                 
  353                 //gtk_menu_shell_append(GTK_MENU_SHELL(win->menuBar), GTK_WIDGET(menu));
  354                 shell = GTK_MENU_SHELL(win->menuBar);
  355             }
  356             else
  357             {
  358                 gMenu *parent = parentMenu();
  359                 
  360                 if (!parent->_popup)
  361                 {
  362                     parent->_popup = (GtkMenu *)gtk_menu_new();
  363                     g_object_ref_sink(parent->_popup);
  364                     
  365                     //fprintf(stderr, "creates a new child menu container in parent %s\n", parent->name());
  366                     
  367                     g_signal_connect(G_OBJECT(parent->_popup), "size-allocate", G_CALLBACK(cb_size_allocate), (gpointer)parent);
  368                     g_signal_connect(G_OBJECT(parent->_popup), "map", G_CALLBACK(cb_map), (gpointer)parent);
  369                     g_signal_connect(G_OBJECT(parent->_popup), "unmap", G_CALLBACK(cb_unmap), (gpointer)parent);
  370                     gtk_widget_show_all(GTK_WIDGET(parent->_popup));
  371                     
  372                     parent->update();
  373                     
  374                     if (parent->style() == NORMAL)
  375                         gtk_menu_item_set_submenu(parent->menu, GTK_WIDGET(parent->_popup));
  376                     
  377                     //parent->setColor();
  378                 }
  379                 shell = GTK_MENU_SHELL(parent->_popup);
  380             }
  381     
  382             if (shell)
  383             {
  384                 patch_classes();
  385                 
  386                 if (pos < 0)
  387                 {
  388                     gtk_menu_shell_append(shell, GTK_WIDGET(menu));
  389                     //g_debug("%p: append to parent %p", this, shell);
  390                 }
  391                 else
  392                 {
  393                     gtk_menu_shell_insert(shell, GTK_WIDGET(menu), pos);
  394                     //g_debug("%p: insert into parent %p", this, shell);
  395                 }
  396             }
  397             
  398             g_signal_connect(G_OBJECT(menu), "destroy", G_CALLBACK(cb_destroy), (gpointer)this);
  399             g_signal_connect(G_OBJECT(menu), "activate", G_CALLBACK(cb_activate), (gpointer)this);
  400             
  401             g_object_set_data(G_OBJECT(menu), "gambas-menu", this);
  402         }
  403         
  404         _oldstyle = _style;
  405         updateVisible();
  406     }
  407     
  408     if (_style == NORMAL || _style == CHECK)
  409     {
  410         gMnemonic_correctText(_text, &buf);
  411         gtk_label_set_text_with_mnemonic(GTK_LABEL(label), buf);
  412         g_free(buf);
  413         
  414         if (!_toplevel)
  415         {
  416             if (_shortcut)
  417             {
  418                 buf = g_strconcat("\t", _shortcut, "  ",(void *)NULL);
  419                 gtk_label_set_text(GTK_LABEL(shlabel), buf);
  420                 g_free(buf);
  421             }
  422             else
  423                 gtk_label_set_text(GTK_LABEL(shlabel), "\t");
  424 
  425             updatePicture();
  426         }
  427         
  428         //setColor();
  429         setFont();
  430     }
  431 
  432     //g_debug("%p: END UPDATE", this);  
  433 }
  434 
  435 void gMenu::updatePicture()
  436 {
  437     int size;
  438     gPicture *pic;
  439     
  440     if (!image || isTopLevel())
  441         return;
  442     
  443     if (!_picture)
  444     {
  445         gtk_image_set_from_pixbuf(GTK_IMAGE(image), NULL);
  446         return;
  447     }
  448     
  449     gtk_widget_get_size_request(image, NULL, &size);
  450     size = size & ~3;
  451     
  452     pic = _picture->stretch(size, size, true);
  453     if (_disabled)
  454         pic->makeGray();
  455     
  456     gtk_image_set_from_pixbuf(GTK_IMAGE(image), pic->getPixbuf());
  457     
  458     delete pic;
  459 }
  460 
  461 void gMenu::initialize()
  462 {
  463     //fprintf(stderr, "gMenu::gMenu: %p (%p)\n", this, pr);
  464     
  465     onFinish = NULL;
  466     onClick = NULL;
  467     onShow = NULL;
  468     onHide = NULL;
  469     
  470     hFree = NULL;
  471     _popup = NULL;
  472     image = NULL;
  473     label = NULL;
  474     shlabel = NULL;
  475     //check = NULL;
  476     menu = NULL;
  477     _toplevel = false;
  478     
  479     _text = NULL;
  480     _shortcut = NULL;
  481     _shortcut_key = 0;
  482     _shortcut_mods = (GdkModifierType)0;
  483     _checked = false;
  484     _picture = NULL;
  485     _name = NULL;
  486     _toggle = false;
  487     _radio = false;
  488 
  489     _style = NOTHING;
  490     _oldstyle = NOTHING;
  491     
  492     _ignore_destroy = false;
  493     _ignore_activate = false;
  494     _no_update = false;
  495     _destroyed = false;
  496     _delete_later = false;
  497     _action = false;
  498     _visible = false;
  499     _opened = false;
  500     _exec = false;
  501     _disabled = false;
  502     _mapping = false;
  503     _proxy_for = false;
  504     
  505     _proxy = NULL;
  506     
  507     sizeGroup = NULL;
  508     _children = NULL;
  509     
  510     menus = g_list_append (menus,(gpointer)this);
  511 }
  512 
  513 
  514 static gboolean cb_menubar_changed(GtkWidget *widget, gMainWindow *data)
  515 {
  516     data->configure();
  517     return false;
  518 }
  519 
  520 gMenu::gMenu(gMainWindow *par, bool hidden)
  521 {
  522     pr = (gpointer)par;
  523 
  524     if (!par->menuBar)
  525     {
  526         par->menuBar = (GtkMenuBar*)gtk_menu_bar_new();
  527         g_signal_connect_after(G_OBJECT(par->menuBar), "map", G_CALLBACK(cb_menubar_changed), (gpointer)par);
  528         g_signal_connect(G_OBJECT(par->menuBar),"unmap", G_CALLBACK(cb_menubar_changed), (gpointer)par);
  529         par->embedMenuBar(par->border);
  530     }
  531     
  532   initialize();
  533     _toplevel = true;
  534     
  535     accel = par->accel;
  536     g_object_ref(accel);
  537     
  538     setText(NULL);
  539     setVisible(!hidden);
  540 }
  541 
  542 gMenu::gMenu(gMenu *par, bool hidden)
  543 {
  544     pr = (gpointer)par;
  545     
  546     initialize();
  547     //sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
  548     
  549     if (!par) return;
  550     if (!par->menu) return;
  551     
  552     par->insert(this);
  553     
  554     accel = par->accel;
  555     g_object_ref(accel);
  556     
  557     setText(NULL);
  558     setVisible(!hidden);
  559 }
  560 
  561 void gMenu::dispose()
  562 {
  563     GList *item;
  564     gMenu *mn;
  565     int i;
  566 
  567     if (_destroyed)
  568         return;
  569     
  570     #if DEBUG_DELETE
  571     fprintf(stderr, "dispose: %s %p --> %p\n", name(), this, pr);
  572     #endif
  573     
  574     setProxy(NULL);
  575     ensureChildMenu();
  576     
  577   if (_proxy_for)
  578     {
  579         #define CLEAR_POINTER(_field) if ((_field) == (void *)this) _field = NULL
  580         
  581         item = g_list_first(menus);
  582         while (item)
  583         {
  584             mn = (gMenu*)item->data;
  585             //CLEAR_POINTER(mn->pr);
  586             CLEAR_POINTER(mn->_proxy);
  587             item = g_list_next(item);
  588         }
  589     }
  590     
  591     if (_children)
  592     {
  593         for (i = 0; i < childCount(); i++)
  594             child(i)->removeParent();
  595         g_ptr_array_unref(_children);
  596         _children = NULL;
  597     }
  598 
  599     //if (_style != NOTHING)
  600     {
  601         if (shlabel && (!_toplevel) && pr)
  602             gtk_size_group_remove_widget(((gMenu*)pr)->sizeGroup, shlabel);
  603         
  604         if (sizeGroup) 
  605             g_object_unref(G_OBJECT(sizeGroup));
  606         
  607         if (accel)
  608             g_object_unref(accel);  
  609     }
  610 
  611     gMenu *parent = parentMenu();
  612     if (parent)
  613     {
  614         parent->remove(this);
  615         parent = NULL;
  616     }
  617 
  618     menus = g_list_remove(menus, (gpointer)this);
  619     
  620     #if DEBUG_DELETE
  621     fprintf(stderr, "dispose: >>> %s\n", name());
  622     #endif
  623 }
  624 
  625 gMenu::~gMenu()
  626 {
  627     #if DEBUG_DELETE
  628     fprintf(stderr, "~gMenu: %s %p --> %p\n", name(), this, pr);
  629     #endif
  630     
  631     dispose();
  632   
  633     _no_update = true;
  634 
  635     setText(NULL);
  636     setPicture(NULL);
  637     setShortcut(NULL);
  638     
  639     _style = NOTHING;
  640     
  641     if (_popup)
  642         g_object_unref(_popup);
  643     
  644     if (_current_popup == this)
  645         _current_popup = NULL;
  646 
  647     menus_destroyed = g_list_remove(menus_destroyed, this);
  648     
  649     #if DEBUG_DELETE
  650     fprintf(stderr, "~gMenu: >>> %s\n", name());
  651     #endif
  652     if (onFinish) onFinish(this);
  653 }
  654 
  655 void gMenu::setEnabled(bool vl)
  656 {
  657     if (vl != _disabled)
  658         return;
  659     
  660     _disabled = !vl;
  661     gtk_widget_set_sensitive(GTK_WIDGET(menu), vl);
  662     updateShortcutRecursive();
  663 }
  664 
  665 bool gMenu::isFullyEnabled() const
  666 {
  667     const gMenu *menu = this;
  668     
  669     for(;;)
  670     {
  671         if (menu->_exec)
  672             return true;
  673 
  674         if (!menu->isEnabled())
  675             return false;
  676 
  677         if (menu->isTopLevel())
  678             return true;
  679 
  680         menu = menu->parentMenu();
  681     }
  682 }
  683 
  684 void gMenu::updateShortcut()
  685 {
  686     if (_no_update)
  687         return;
  688     
  689     if (isTopLevel())
  690         return;
  691     
  692     if (_shortcut_key)
  693     {
  694         //fprintf(stderr, "gtk_widget_remove_accelerator: (%s %p) accel = %p (%d,%d)\n", name(), menu, accel, _shortcut_key, _shortcut_mods);
  695         gtk_widget_remove_accelerator(GTK_WIDGET(menu), accel, _shortcut_key, _shortcut_mods);
  696         _shortcut_key = 0;
  697     }
  698     
  699     if (isFullyEnabled() && _shortcut)
  700     {
  701         gt_shortcut_parse(_shortcut, &_shortcut_key, &_shortcut_mods);
  702         if (_shortcut_key)
  703         {
  704             //fprintf(stderr, "gtk_widget_add_accelerator: (%s %p) accel = %p (%d,%d)\n", name(), menu, accel, _shortcut_key, _shortcut_mods);
  705             gtk_widget_add_accelerator(GTK_WIDGET(menu), "activate", accel, _shortcut_key, _shortcut_mods, (GtkAccelFlags)0);
  706         }
  707     }
  708 }
  709 
  710 void gMenu::updateShortcutRecursive()
  711 {
  712     gMenu *ch;
  713     int i;
  714     
  715     if (_exec)
  716         return;
  717 
  718     updateShortcut();
  719 
  720     for (i = 0;; i++)
  721     {
  722         ch = child(i);
  723         if (!ch)
  724             break;
  725         ch->updateShortcutRecursive();
  726     }
  727 }
  728 
  729 void gMenu::setText(const char *text)
  730 {
  731     g_free(_text);
  732     if (text)
  733         _text = g_strdup(text);
  734     else
  735         _text = NULL;
  736         
  737     update();
  738 }
  739 
  740 bool gMenu::isVisible()
  741 {
  742     if (!menu) return false;
  743     return _visible;    
  744 }
  745 
  746 void gMenu::updateVisible()
  747 {
  748     bool vl = _visible;
  749     
  750     if (_toplevel && _style != NORMAL)
  751         vl = false;
  752     
  753     //fprintf(stderr, "gMenu::updateVisible: %s '%s' %d\n", name(), text(), vl);
  754     
  755     gtk_widget_set_visible(GTK_WIDGET(menu), vl);
  756     //g_object_set(G_OBJECT(menu),"visible",vl,(void *)NULL);
  757     
  758     if (_toplevel && pr)
  759         ((gMainWindow *)pr)->checkMenuBar();
  760 }
  761 
  762 void gMenu::setVisible(bool vl)
  763 {
  764     if (!menu) return;
  765     if (vl == _visible) return;
  766     
  767     _visible = vl;
  768     updateVisible();
  769 }
  770 
  771 void gMenu::setPicture(gPicture *pic)
  772 {
  773     //fprintf(stderr, "gMenu::setPicture: %p\n", pic);
  774     gPicture::assign(&_picture, pic);
  775     update();
  776 }
  777 
  778 void gMenu::setChecked(bool vl)
  779 {
  780     if (vl == _checked || _popup)
  781         return;
  782     
  783     _checked = vl;
  784     if (_toggle || _radio)
  785     {
  786         _ignore_activate = true;
  787         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), vl);
  788     }
  789     else
  790         update();
  791 }
  792 
  793 void gMenu::setToggle(bool vl)
  794 {
  795     if (vl == _toggle)
  796         return;
  797 
  798     _toggle = vl;
  799     update();
  800 }
  801 
  802 void gMenu::setRadio(bool vl)
  803 {
  804     if (vl == _radio)
  805         return;
  806     
  807     _radio = vl;
  808     update();
  809 }
  810 
  811 int gMenu::childCount() const
  812 {
  813     if (!_children)
  814         return 0;
  815     else
  816         return _children->len;
  817 }
  818 
  819 gMenu *gMenu::child(int index) const
  820 {
  821     if (!_children || index < 0 || index >= (int)_children->len)
  822         return NULL;
  823     else
  824         return (gMenu *)g_ptr_array_index(_children, index);
  825 }
  826 
  827 void gMenu::destroy()
  828 {
  829     if (_destroyed)
  830         return;
  831     
  832     dispose();
  833     menus_destroyed = g_list_prepend(menus_destroyed, (gpointer)this);
  834     _destroyed = true;
  835 }
  836 
  837 #if GTK_CHECK_VERSION(3, 22, 0)
  838 #else
  839 static void position_menu(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, MenuPosition *pos)
  840 {
  841     *x = pos->x;
  842     *y = pos->y;
  843     *push_in = true;
  844 }
  845 #endif
  846 
  847 void gMenu::doPopup(bool move, int x, int y)
  848 {
  849     if (!_popup)
  850         return;
  851     
  852     gMenu *save_current_popup = _current_popup;
  853     _current_popup = this;
  854     
  855     _in_popup++;
  856     _popup_count++;
  857     _exec = true;
  858     
  859 #if GTK_CHECK_VERSION(3, 22, 0)
  860 
  861     GdkWindow *win;
  862     GdkRectangle rect;
  863     GdkEvent *event;
  864     GdkEvent *last_event;
  865     
  866     gt_disable_warnings(true);
  867     
  868     event = gdk_event_new(GDK_BUTTON_PRESS);
  869     event->button.time = gApplication::lastEventTime(); //GDK_CURRENT_TIME;
  870     
  871     last_event = gApplication::lastEvent();
  872     if (last_event && last_event->type == GDK_BUTTON_PRESS)
  873     {
  874         event->button.button = last_event->button.button;
  875         event->button.window = last_event->button.window;
  876     }
  877     else
  878     {
  879         event->button.button = 1;
  880         event->button.window = gtk_widget_get_window(window()->border);
  881     }
  882     
  883     gdk_event_set_device(event, gMouse::getPointer());
  884     
  885     if (move)
  886     {
  887         win = gdk_event_get_window(event);
  888         gdk_window_get_origin(win, &rect.x, &rect.y);
  889 
  890         rect.x = x - rect.x;
  891         rect.y = y - rect.y;
  892         rect.width = rect.height = 1;
  893         
  894         gtk_menu_popup_at_rect(_popup, win, &rect, GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST, event);
  895     }
  896     else
  897         gtk_menu_popup_at_pointer(_popup, event);
  898 
  899     gt_disable_warnings(false);
  900     
  901     event->button.window = NULL;
  902     gdk_event_free(event);
  903     
  904 #else
  905     
  906     MenuPosition *pos = NULL;
  907     
  908     if (move)
  909     {
  910         pos = new MenuPosition;
  911         pos->x = x;
  912         pos->y = y;
  913     }
  914     
  915     gtk_menu_popup(_popup, NULL, NULL, move ? (GtkMenuPositionFunc)position_menu : NULL, (gpointer)pos, 0, gApplication::lastEventTime());
  916 
  917 #endif
  918     
  919 #if GTK_CHECK_VERSION(2, 20, 0)
  920     while (_current_popup && _popup && gtk_widget_get_mapped(GTK_WIDGET(_popup)))
  921 #else
  922     while (_current_popup && _popup && GTK_WIDGET_MAPPED(_popup))
  923 #endif
  924         MAIN_do_iteration(false);
  925 
  926     _exec = false;
  927     _current_popup = save_current_popup;
  928 
  929     _in_popup--;
  930 
  931 #ifdef GTK3
  932 #else
  933     if (pos)
  934         delete pos;
  935 #endif
  936     
  937     // flush the event loop so that the main window is reactivated before the click menu event is raised
  938 
  939     while (gtk_events_pending())
  940         MAIN_do_iteration(false);
  941 }
  942 
  943 void gMenu::popup(int x, int y)
  944 {
  945     doPopup(true, x, y);
  946 }
  947 
  948 void gMenu::popup()
  949 {
  950     doPopup(false);
  951 }
  952 
  953 void gMenu::close()
  954 {
  955     if (!_popup)
  956         return;
  957     
  958     gtk_menu_popdown(_popup);
  959 }
  960 
  961 int gMenu::winChildCount(gMainWindow *par)
  962 {
  963     GList *item;
  964     gMenu *mn;
  965     int ct=0;
  966     
  967     if (!menus) return 0;
  968     
  969     item=g_list_first(menus);
  970     while (item)
  971     {
  972         mn=(gMenu*)item->data;
  973         if (mn->pr == (void*)par) ct++;
  974         item=g_list_next(item);
  975     }
  976     
  977     return ct;
  978 }
  979 
  980 gMenu* gMenu::winChildMenu(gMainWindow *par,int pos)
  981 {
  982     GList *item;
  983     gMenu *mn;
  984     int ct=0;
  985     
  986     if (!menus) return NULL;
  987     
  988     item=g_list_first(menus);
  989     while (item)
  990     {
  991         mn=(gMenu*)item->data;
  992         if (mn->pr == (void*)par)
  993         {
  994             if (ct==pos) return mn;
  995             ct++;
  996         }
  997         item=g_list_next(item);
  998     }
  999     
 1000     return NULL;
 1001 }
 1002 
 1003 gMenu *gMenu::findFromName(gMainWindow *win, const char *name)
 1004 {
 1005     int i;
 1006     int count;
 1007     gMenu *menu;
 1008     
 1009     for(;;)
 1010     {
 1011         count = winChildCount(win);
 1012         for (i = 0; i < count; i++)
 1013         {
 1014             menu = winChildMenu(win, i);
 1015             if (!strcasecmp(menu->name(), name))
 1016                 return menu;
 1017         }
 1018         
 1019         if (!win->parent())
 1020             break;
 1021         win = win->parent()->window();
 1022         if (!win)
 1023             break;
 1024     }
 1025     
 1026     return NULL;
 1027 }
 1028 
 1029 void gMenu::setShortcut(char *shortcut)
 1030 {
 1031     if (_shortcut)
 1032     {
 1033         g_free(_shortcut);
 1034         _shortcut = NULL;
 1035     }
 1036 
 1037     if (shortcut)
 1038         _shortcut = g_strdup(shortcut);
 1039     
 1040     updateShortcut();
 1041     update();
 1042 }
 1043 
 1044 gMainWindow *gMenu::window()
 1045 {
 1046   if (!pr)
 1047     return NULL;
 1048 
 1049   if (_toplevel)
 1050     return (gMainWindow *)pr;
 1051     
 1052   return ((gMenu *)pr)->window();
 1053 }
 1054 
 1055 void gMenu::setName(char *name)
 1056 {
 1057     if (_name)
 1058     {
 1059         g_free(_name);
 1060         _name = NULL;
 1061     }
 1062     
 1063     if (name) 
 1064         _name = g_strdup(name);
 1065 }
 1066 
 1067 void gMenu::hideSeparators()
 1068 {
 1069     gMenu *ch;
 1070     gMenu *last_ch;
 1071     bool is_sep;
 1072     bool last_sep;
 1073     //bool show_check = false;
 1074     bool show_image = false;
 1075     int i;
 1076 
 1077     if (!_popup)
 1078         return;
 1079     
 1080     last_sep = true;
 1081     last_ch = 0;
 1082     
 1083     for (i = 0; i < childCount(); i++)
 1084     {
 1085         ch = child(i);
 1086         
 1087         is_sep = ch->style() == SEPARATOR;
 1088         
 1089         if (is_sep)
 1090         {
 1091             if (last_sep)
 1092             {
 1093                 ch->hide();
 1094             }
 1095             else
 1096             {
 1097                 ch->show();
 1098                 last_sep = true;
 1099                 last_ch = ch;
 1100             }
 1101         }
 1102         else
 1103         {
 1104             if (ch->isVisible())
 1105             {
 1106                 ch->ensureChildMenu();
 1107                 last_sep = false;
 1108                 /*if (ch->radio() || ch->toggle() || ch->checked())
 1109                     show_check = true;*/
 1110                 if (ch->picture())
 1111                     show_image = true;
 1112             }
 1113         }
 1114     }
 1115     
 1116     if (last_sep && last_ch)
 1117         last_ch->hide();
 1118 
 1119     for (i = 0; i < childCount(); i++)
 1120     {
 1121         ch = child(i);
 1122         if (!ch->image || !ch->isVisible())
 1123             continue;
 1124         
 1125         if (show_image)
 1126             gtk_widget_show(ch->image);
 1127         else
 1128             gtk_widget_hide(ch->image);
 1129     }
 1130 }
 1131 
 1132 void gMenu::setFont()
 1133 {
 1134     gMainWindow *win = window();
 1135 #ifdef GTK3
 1136     if (label) gt_widget_update_css(GTK_WIDGET(label), win->font(), COLOR_DEFAULT, COLOR_DEFAULT);
 1137     if (shlabel) gt_widget_update_css(GTK_WIDGET(shlabel), win->font(), COLOR_DEFAULT, COLOR_DEFAULT);
 1138 #else
 1139     if (label) gtk_widget_modify_font(GTK_WIDGET(label), win->font()->desc());
 1140     if (shlabel) gtk_widget_modify_font(GTK_WIDGET(shlabel), win->font()->desc());
 1141 #endif
 1142 }
 1143 
 1144 /*void gMenu::setColor()
 1145 {
 1146     gMainWindow *win = window();
 1147     
 1148     if (pr == win)
 1149     {
 1150         if (label) set_gdk_fg_color(GTK_WIDGET(label), win->foreground());
 1151     }
 1152     //if (shortcut) set_gdk_fg_color(GTK_WIDGET(shortcut), win->foreground());
 1153 }*/
 1154 
 1155 void gMenu::updateColor(gMainWindow *win)
 1156 {
 1157     //GList *item;
 1158     //gMenu *mn;
 1159 
 1160     if (!win->menuBar)
 1161         return;
 1162     
 1163     #ifdef GTK3
 1164     gt_widget_update_css(GTK_WIDGET(win->menuBar), NULL, win->background(), win->foreground());
 1165     #else
 1166     set_gdk_bg_color(GTK_WIDGET(win->menuBar), win->background());
 1167     set_gdk_fg_color(GTK_WIDGET(win->menuBar), win->foreground());
 1168     #endif
 1169 
 1170     /*if (!menus) 
 1171         return;
 1172     
 1173     item = g_list_first(menus);
 1174     while (item)
 1175     {
 1176         mn = (gMenu*)item->data;
 1177         //if (mn->pr == (void*)win)
 1178             mn->setColor();
 1179         item = g_list_next(item);
 1180     }*/
 1181 }
 1182 
 1183 void gMenu::updateFont(gMainWindow *win)
 1184 {
 1185     GList *item;
 1186     gMenu *mn;
 1187     
 1188     if (win->menuBar)
 1189     {
 1190         //fprintf(stderr, "set menu bar font\n");
 1191 #ifdef GTK3
 1192         gt_widget_update_css(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font() : NULL, COLOR_DEFAULT, COLOR_DEFAULT);
 1193 #else
 1194         gtk_widget_modify_font(GTK_WIDGET(win->menuBar), win->ownFont() ? win->font()->desc() : NULL);
 1195 #endif
 1196     }
 1197     
 1198     if (!menus) 
 1199         return;
 1200     
 1201     item = g_list_first(menus);
 1202     while (item)
 1203     {
 1204         mn = (gMenu*)item->data;
 1205         if (mn->pr == (void*)win)
 1206             mn->setFont();
 1207         item=g_list_next(item);
 1208     }
 1209 }
 1210 
 1211 void gMenu::updateRadio()
 1212 {
 1213     gMenu *parent, *ch;
 1214     int i;
 1215     int start = -1;
 1216 
 1217     parent = parentMenu();
 1218     if (!parent)
 1219         return;
 1220     
 1221     for (i = 0; i < parent->childCount(); i++)
 1222     {
 1223         ch = parent->child(i);
 1224         if (ch->radio())
 1225         {
 1226             if (start < 0)
 1227                 start = i;
 1228             if (ch == this)
 1229                 break;
 1230         }
 1231         else
 1232             start = -1;
 1233     }
 1234 
 1235     if (start >= 0)
 1236     {
 1237         for (i = start; i < parent->childCount(); i++)
 1238         {
 1239             ch = parent->child(i);
 1240             if (!ch->radio())
 1241                 break;
 1242 
 1243             ch->setChecked(ch == this);
 1244         }
 1245     }
 1246 }
 1247 
 1248 bool gMenu::setProxy(gMenu *proxy)
 1249 {
 1250     gMenu *check = proxy;
 1251 
 1252     while (check)
 1253     {
 1254         if (check == this)
 1255             return true;
 1256 
 1257         check = check->_proxy;
 1258     }
 1259 
 1260     _proxy = proxy;
 1261     if (proxy)
 1262         proxy->_proxy_for = true;
 1263     
 1264     return false;
 1265 }
 1266 
 1267 GtkMenu *gMenu::getSubMenu()
 1268 {
 1269     if (_proxy)
 1270         return _proxy->getSubMenu();
 1271     else
 1272         return _popup;
 1273 }
 1274 
 1275 void gMenu::ensureChildMenu()
 1276 {
 1277     GtkMenu *sub_menu = getSubMenu();
 1278     
 1279     // TODO: create parent menu?
 1280     if (sub_menu && gtk_menu_item_get_submenu(menu) != (GtkWidget *)sub_menu)
 1281     {
 1282         //fprintf(stderr, "ensureChildMenu: %p\n", sub_menu);
 1283         g_object_ref(sub_menu);
 1284         /*attach = gtk_menu_get_attach_widget(sub_menu);
 1285         if (attach)
 1286             gtk_menu_item_set_submenu(GTK_MENU_ITEM(attach), NULL);*/
 1287         if (gtk_menu_get_attach_widget(sub_menu))
 1288             gtk_menu_detach(sub_menu);
 1289         gtk_menu_item_set_submenu(menu, GTK_WIDGET(sub_menu));
 1290         g_object_unref(sub_menu);
 1291     }
 1292 }
 1293 
 1294 
 1295 GtkSizeGroup *gMenu::getSizeGroup()
 1296 {
 1297     if (!sizeGroup)
 1298         sizeGroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 1299     return sizeGroup;
 1300 }
 1301 
 1302 void gMenu::insert(gMenu *child)
 1303 {
 1304     if (!_children)
 1305         _children = g_ptr_array_new();
 1306     
 1307     g_ptr_array_add(_children, child);
 1308 }
 1309 
 1310 void gMenu::remove(gMenu *child)
 1311 {
 1312     g_ptr_array_remove(_children, child);
 1313 }
 1314 
 1315 void gMenu::willBeDeletedLater()
 1316 {
 1317     //gMenu *parent = parentMenu();
 1318     _delete_later = TRUE;
 1319     dispose();
 1320 }
 1321 
 1322 void gMenu::removeParent()
 1323 {
 1324     #if DEBUG_DELETE
 1325     fprintf(stderr, "removeParent: %s %p --> %p\n", name(), this, pr);
 1326     #endif
 1327     pr = NULL;
 1328 }
 1329 
 1330 void gMenu::updateChecked()
 1331 {
 1332     if (_style == CHECK)
 1333         _checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu));
 1334     else
 1335         _checked = false;
 1336 }
 1337 
 1338 bool gMenu::ignoreDestroy()
 1339 {
 1340     if (_ignore_destroy)
 1341     {
 1342         _ignore_destroy = false;
 1343         return true;
 1344     }
 1345     else
 1346         return false;
 1347 }
 1348 
 1349 bool gMenu::ignoreActivate()
 1350 {
 1351     if (_ignore_activate)
 1352     {
 1353         _ignore_activate = false;
 1354         return true;
 1355     }
 1356     else
 1357         return false;
 1358 }
 1359 
 1360 void gMenu::cleanRemovedMenus()
 1361 {
 1362     GList *iter;
 1363     gMenu *menu;
 1364 
 1365     if (!menus_destroyed) return;
 1366 
 1367     for(;;)
 1368     {
 1369         iter = g_list_first(menus_destroyed);
 1370         if (!iter)
 1371             break;
 1372         menu = (gMenu *)iter->data;
 1373         gtk_widget_destroy(GTK_WIDGET(menu->menu));
 1374     }
 1375 
 1376     menus_destroyed = NULL;
 1377 }