LCOV - code coverage report
Current view: top level - gdraw - gmenu.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 1245 0.0 %
Date: 2017-08-04 Functions: 0 74 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2000-2012 by George Williams */
       2             : /*
       3             :  * Redistribution and use in source and binary forms, with or without
       4             :  * modification, are permitted provided that the following conditions are met:
       5             : 
       6             :  * Redistributions of source code must retain the above copyright notice, this
       7             :  * list of conditions and the following disclaimer.
       8             : 
       9             :  * Redistributions in binary form must reproduce the above copyright notice,
      10             :  * this list of conditions and the following disclaimer in the documentation
      11             :  * and/or other materials provided with the distribution.
      12             : 
      13             :  * The name of the author may not be used to endorse or promote products
      14             :  * derived from this software without specific prior written permission.
      15             : 
      16             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
      17             :  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
      18             :  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
      19             :  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      20             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      21             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
      22             :  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      23             :  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      24             :  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
      25             :  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      26             :  */
      27             : #include <stdlib.h>
      28             : #include <gdraw.h>
      29             : #include "ggadgetP.h"
      30             : #include <gwidget.h>
      31             : #include <ustring.h>
      32             : #include <gkeysym.h>
      33             : #include <utype.h>
      34             : #include <gresource.h>
      35             : #include "hotkeys.h"
      36             : #include "gutils/prefs.h"
      37             : 
      38             : static GBox menubar_box = GBOX_EMPTY; /* Don't initialize here */
      39             : static GBox menu_box = GBOX_EMPTY; /* Don't initialize here */
      40             : static FontInstance *menu_font = NULL, *menubar_font = NULL;
      41             : static int gmenubar_inited = false;
      42             : #ifdef __Mac
      43             : static int mac_menu_icons = true;
      44             : #else
      45             : static int mac_menu_icons = false;
      46             : #endif
      47             : static int menu_3d_look = 1; // The 3D look is the default/legacy setting.
      48             : static int mask_set=0;
      49             : static int menumask = ksm_control|ksm_meta|ksm_shift;           /* These are the modifier masks expected in menus. Will be overridden by what's actually there */
      50             : #ifndef _Keyboard
      51             : # define _Keyboard 0
      52             : #endif
      53             : static enum { kb_ibm, kb_mac, kb_sun, kb_ppc } keyboard = _Keyboard;
      54             : /* Sigh. In old XonX the command key is mapped to 0x20 and Option to 0x8 (meta) */
      55             : /*  the option key conversions (option-c => ccidilla) are not done */
      56             : /*  In the next X, the command key is mapped to 0x10 and Option to 0x2000 */
      57             : /*  (again option key conversion are not done) */
      58             : /*  In 10.3, the command key is mapped to 0x10 and Option to 0x8 */
      59             : /*  In 10.5 the command key is mapped to 0x10 and Option to 0x8 */
      60             : /*   (and option conversions are done) */
      61             : /*  While in Suse PPC X, the command key is 0x8 (meta) and option is 0x2000 */
      62             : /*  and the standard mac option conversions are done */
      63             : 
      64             : static GResInfo gmenu_ri;
      65             : static GResInfo gmenubar_ri = {
      66             :     &gmenu_ri, &ggadget_ri,&gmenu_ri, NULL,
      67             :     &menubar_box,
      68             :     &menubar_font,
      69             :     NULL,
      70             :     NULL,
      71             :     N_("Menu Bar"),
      72             :     N_("Menu Bar"),
      73             :     "GMenuBar",
      74             :     "Gdraw",
      75             :     false,
      76             :     omf_border_shape|omf_border_width|box_foreground_border_outer,
      77             :     NULL,
      78             :     GBOX_EMPTY,
      79             :     NULL,
      80             :     NULL,
      81             :     NULL
      82             : };
      83             : static struct resed menu_re[] = {
      84             :     {N_("MacIcons"), "MacIcons", rt_bool, &mac_menu_icons, N_("Whether to use mac-like icons to indicate modifiers (for instance ^ for Control)\nor to use an abbreviation (for instance \"Cnt-\")"), NULL, { 0 }, 0, 0 },
      85             :     RESED_EMPTY
      86             : };
      87             : static GResInfo gmenu_ri = {
      88             :     NULL, &ggadget_ri,&gmenubar_ri, NULL,
      89             :     &menu_box,
      90             :     &menu_font,
      91             :     NULL,
      92             :     menu_re,
      93             :     N_("Menu"),
      94             :     N_("Menu"),
      95             :     "GMenu",
      96             :     "Gdraw",
      97             :     false,
      98             :     omf_border_shape|omf_padding|box_foreground_border_outer,
      99             :     NULL,
     100             :     GBOX_EMPTY,
     101             :     NULL,
     102             :     NULL,
     103             :     NULL
     104             : };
     105             : 
     106             : char* HKTextInfoToUntranslatedText( char* text_untranslated );
     107             : char* HKTextInfoToUntranslatedTextFromTextInfo( GTextInfo* ti );
     108             : 
     109             : static void GMenuBarChangeSelection(GMenuBar *mb, int newsel,GEvent *);
     110             : static struct gmenu *GMenuCreateSubMenu(struct gmenu *parent,GMenuItem *mi,int disable);
     111             : static struct gmenu *GMenuCreatePulldownMenu(GMenuBar *mb,GMenuItem *mi, int disabled);
     112             : 
     113             : static int menu_grabs=true;
     114             : static struct gmenu *most_recent_popup_menu = NULL;
     115             : 
     116           0 : static void GMenuInit() {
     117             :     FontRequest rq;
     118             :     char *keystr, *end;
     119             : 
     120           0 :     GGadgetInit();
     121           0 :     memset(&rq,0,sizeof(rq));
     122           0 :     GDrawDecomposeFont(_ggadget_default_font,&rq);
     123           0 :     rq.weight = 400;
     124           0 :     menu_font = menubar_font = GDrawInstanciateFont(NULL,&rq);
     125           0 :     _GGadgetCopyDefaultBox(&menubar_box);
     126           0 :     _GGadgetCopyDefaultBox(&menu_box);
     127           0 :     menubar_box.border_shape = menu_box.border_shape = bs_rect;
     128           0 :     menubar_box.border_width = 0;
     129           0 :     menu_box.padding = 1;
     130           0 :     menubar_box.flags |= box_foreground_border_outer;
     131           0 :     menu_box.flags |= box_foreground_border_outer;
     132           0 :     menubar_font = _GGadgetInitDefaultBox("GMenuBar.",&menubar_box,menubar_font);
     133           0 :     menu_font = _GGadgetInitDefaultBox("GMenu.",&menu_box,menubar_font);
     134           0 :     keystr = GResourceFindString("Keyboard");
     135           0 :     if ( keystr!=NULL ) {
     136           0 :         if ( strmatch(keystr,"mac")==0 ) keyboard = kb_mac;
     137           0 :         else if ( strmatch(keystr,"sun")==0 ) keyboard = kb_sun;
     138           0 :         else if ( strmatch(keystr,"ppc")==0 ) keyboard = kb_ppc;
     139           0 :         else if ( strmatch(keystr,"ibm")==0 || strmatch(keystr,"pc")==0 ) keyboard = kb_ibm;
     140           0 :         else if ( strtol(keystr,&end,10), *end=='\0' )
     141           0 :             keyboard = strtol(keystr,NULL,10);
     142             :     }
     143           0 :     menu_grabs = GResourceFindBool("GMenu.Grab",menu_grabs);
     144           0 :     mac_menu_icons = GResourceFindBool("GMenu.MacIcons",mac_menu_icons);
     145           0 :     menu_3d_look = GResourceFindBool("GMenu.3DLook", menu_3d_look);
     146           0 :     gmenubar_inited = true;
     147           0 :     _GGroup_Init();
     148           0 : }
     149             : 
     150             : typedef struct gmenu {
     151             :     unsigned int hasticks: 1;
     152             :     unsigned int pressed: 1;
     153             :     unsigned int initial_press: 1;
     154             :     unsigned int scrollup: 1;
     155             :     unsigned int freemi: 1;
     156             :     unsigned int disabled: 1;
     157             :     unsigned int dying: 1;
     158             :     unsigned int hidden: 1;
     159             :     unsigned int any_unmasked_shortcuts: 1;             /* Only set for popup menus. Else info in menubar */
     160             :     int bp;
     161             :     int tickoff, tioff, rightedge;
     162             :     int width, height;
     163             :     int line_with_mouse;
     164             :     int offtop, lcnt, mcnt;
     165             :     GMenuItem *mi;
     166             :     int fh, as;
     167             :     GWindow w;
     168             :     GBox *box;
     169             :     struct gmenu *parent, *child;
     170             :     struct gmenubar *menubar;
     171             :     GWindow owner;
     172             :     GTimer *scrollit;           /* No longer in use, see comment below */
     173             :     FontInstance *font;
     174             :     void (*donecallback)(GWindow owner);
     175             :     GIC *gic;
     176             :     char subMenuName[100];
     177             :     /* The code below still contains the old code for using up/down arrows */
     178             :     /*  in the menu instead of a scrollbar. If vsb==NULL and the menu doesn't */
     179             :     /*  fit on the screen, then the old code will be implemented (normally */
     180             :     /*  that won't happen, but if I ever want to re-enable it, this is how */
     181             :     /* The up arrow was placed at the top of the menu IFF there were lines off */
     182             :     /*  the top, similarly for the bottom. Clicking on either one would scroll */
     183             :     /*  in the indicated directions */
     184             :     GGadget *vsb;
     185             : } GMenu;
     186             : 
     187             : static char*
     188           0 : translate_shortcut (int i, char *modifier)
     189             : {
     190             :   char buffer[32];
     191             :   char *temp;
     192             :   TRACE("translate_shortcut(top) i:%d modifier:%s\n", i, modifier );
     193             : 
     194           0 :   sprintf (buffer, "Flag0x%02x", 1 << i);
     195           0 :   temp = dgettext (GMenuGetShortcutDomain (), buffer);
     196             : 
     197           0 :   if (strcmp (temp, buffer) != 0)
     198           0 :       modifier = temp;
     199             :   else
     200           0 :       modifier = dgettext (GMenuGetShortcutDomain (), modifier);
     201             : 
     202             :   TRACE("translate_shortcut(end) i:%d modifier:%s\n", i, modifier );
     203             : 
     204           0 :   return modifier;
     205             : }
     206             : 
     207             : 
     208             : 
     209           0 : static void _shorttext(int shortcut, int short_mask, unichar_t *buf) {
     210           0 :     unichar_t *pt = buf;
     211             :     static int initted = false;
     212           0 :     struct { int mask; char *modifier; } mods[8] = {
     213             :         { ksm_shift, H_("Shift+") },
     214             :         { ksm_capslock, H_("CapsLk+") },
     215             :         { ksm_control, H_("Ctrl+") },
     216             :         { ksm_meta, H_("Alt+") },
     217             :         { 0x10, H_("Flag0x10+") },
     218             :         { 0x20, H_("Flag0x20+") },
     219             :         { 0x40, H_("Flag0x40+") },
     220             :         { 0x80, H_("Flag0x80+") }
     221             :         };
     222             :     int i;
     223             :     char buffer[32];
     224             : 
     225           0 :     uc_strcpy(pt,"xx⎇");
     226           0 :     pt += u_strlen(pt);
     227           0 :     *pt = '\0';
     228           0 :     return;
     229             : 
     230             :     if ( !initted )
     231             :     {
     232             :         /* char *temp; */
     233             :         for ( i=0; i<8; ++i )
     234             :         {
     235             :             /* sprintf( buffer,"Flag0x%02x", 1<<i ); */
     236             :             /* temp = dgettext(GMenuGetShortcutDomain(),buffer); */
     237             :             /* if ( strcmp(temp,buffer)!=0 ) */
     238             :             /*  mods[i].modifier = temp; */
     239             :             /* else */
     240             :             /*  mods[i].modifier = dgettext(GMenuGetShortcutDomain(),mods[i].modifier); */
     241             : 
     242             :           if (mac_menu_icons)
     243             :           {
     244             :               TRACE("mods[i].mask: %s\n", mods[i].modifier );
     245             : 
     246             :               if (mods[i].mask == ksm_cmdmacosx)
     247             :                   mods[i].modifier = "⌘";
     248             :               else if (mods[i].mask == ksm_control)
     249             :                   mods[i].modifier = "⌃";
     250             :               else if (mods[i].mask == ksm_meta)
     251             :                   mods[i].modifier = "⎇";
     252             :               else if (mods[i].mask == ksm_shift)
     253             :                   mods[i].modifier = "⇧";
     254             :               else
     255             :                   mods[i].modifier = translate_shortcut (i, mods[i].modifier);
     256             :           }
     257             :           else
     258             :           {
     259             :               translate_shortcut (i, mods[i].modifier);
     260             :           }
     261             : 
     262             : 
     263             : 
     264             : 
     265             :         }
     266             :         /* It used to be that the Command key was available to X on the mac */
     267             :         /*  but no longer. So we used to use it, but we can't now */
     268             :         /* It's sort of available. X11->Preferences->Input->Enable Keyboard shortcuts under X11 needs to be OFF */
     269             :         /* if ( strcmp(mods[2].modifier,"Ctl+")==0 ) */
     270             :             /* mods[2].modifier = keyboard!=kb_mac?"Ctl+":"Cmd+"; */
     271             :         if ( strcmp(mods[3].modifier,"Alt+")==0 )
     272             :             mods[3].modifier = keyboard==kb_ibm?"Alt+":keyboard==kb_mac?"Opt+":keyboard==kb_ppc?"Cmd+":"Meta+";
     273             :     }
     274             : 
     275             : 
     276             :     if ( shortcut==0 ) {
     277             :         *pt = '\0';
     278             : return;
     279             :     }
     280             : 
     281             :     for ( i=7; i>=0 ; --i ) {
     282             :         if ( short_mask&(1<<i) ) {
     283             :             uc_strcpy(pt,mods[i].modifier);
     284             :             pt += u_strlen(pt);
     285             :         }
     286             :     }
     287             : 
     288             : 
     289             :     if ( shortcut>=0xff00 && GDrawKeysyms[shortcut-0xff00] ) {
     290             :         cu_strcpy(buffer,GDrawKeysyms[shortcut-0xff00]);
     291             :         utf82u_strcpy(pt,dgettext(GMenuGetShortcutDomain(),buffer));
     292             :     } else {
     293             :         *pt++ = islower(shortcut)?toupper(shortcut):shortcut;
     294             :         *pt = '\0';
     295             :     }
     296             : }
     297             : 
     298             : 
     299             : /*
     300             :  * Unused
     301             : static void shorttext(GMenuItem *gi,unichar_t *buf) {
     302             :     _shorttext(gi->shortcut,gi->short_mask,buf);
     303             : }
     304             : */
     305             : 
     306           0 : static int GMenuGetMenuPathRecurse( GMenuItem** stack,
     307             :                                     GMenuItem *basemi,
     308             :                                     GMenuItem *targetmi ) {
     309           0 :     GMenuItem *mi = basemi;
     310             :     int i;
     311           0 :     for ( i=0; mi[i].ti.text || mi[i].ti.text_untranslated || mi[i].ti.image || mi[i].ti.line; ++i ) {
     312             : //      TRACE("text_untranslated: %s\n", mi[i].ti.text_untranslated );
     313           0 :         if ( mi[i].sub ) {
     314             : //          TRACE("GMenuGetMenuPathRecurse() going down on %s\n", u_to_c(mi[i].ti.text));
     315           0 :             stack[0] = &mi[i];
     316           0 :             int rc = GMenuGetMenuPathRecurse( &stack[1], mi[i].sub, targetmi );
     317           0 :             if( rc == 1 )
     318           0 :                 return rc;
     319           0 :             stack[0] = 0;
     320             :         }
     321           0 :         if( mi[i].ti.text ) {
     322             : //          TRACE("GMenuGetMenuPathRecurse() inspecting %s %p %p\n", u_to_c(mi[i].ti.text),mi[i],targetmi);
     323           0 :             GMenuItem* cur = &mi[i];
     324           0 :             if( cur == targetmi ) {
     325           0 :                 stack[0] = cur;
     326           0 :                 return 1;
     327             :             }
     328             :         }
     329             :     }
     330           0 :     return 0;
     331             : }
     332             : 
     333             : /**
     334             :  * Get a string describing the path from basemi to the targetmi.
     335             :  * For example, if the basemi is the first mi in the whole menu structure
     336             :  * and targetmi is to the edit / select all menu item then the return
     337             :  * value is:
     338             :  * edit.select all
     339             :  *
     340             :  * If you can't get to targetmi from basemi then return 0.
     341             :  * If something doesn't make sense then return 0.
     342             :  *
     343             :  * Do not free the return value, it is a pointer into a buffer owned
     344             :  * by this function.
     345             :  */
     346           0 : static char* GMenuGetMenuPath( GMenuItem *basemi, GMenuItem *targetmi ) {
     347             :     GMenuItem* stack[1024];
     348           0 :     memset(stack, 0, sizeof(stack));
     349           0 :     if( !targetmi->ti.text )
     350           0 :         return 0;
     351             : 
     352             : //    TRACE("GMenuGetMenuPath() base   %s\n", u_to_c(basemi->ti.text));
     353             : //    TRACE("GMenuGetMenuPath() target %s\n", u_to_c(targetmi->ti.text));
     354             : 
     355             :     /* { */
     356             :     /*  int i=0; */
     357             :     /*  GMenuItem *mi = basemi; */
     358             :     /*  for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) { */
     359             :     /*      if( mi[i].ti.text ) { */
     360             :     /*          TRACE("GMenuGetMenuPath() xbase   %s\n", u_to_c(mi[i].ti.text)); */
     361             :     /*      } */
     362             :     /*  } */
     363             :     /* } */
     364             :     /* TRACE("GMenuGetMenuPath() starting...\n"); */
     365             : 
     366           0 :     GMenuItem *mi = basemi;
     367             :     int i;
     368           0 :     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
     369           0 :         if( mi[i].ti.text ) {
     370           0 :             memset(stack, 0, sizeof(stack));
     371             : //          TRACE("GMenuGetMenuPath() xbase   %s\n", u_to_c(mi[i].ti.text));
     372             : //          TRACE("GMenuGetMenuPath() untrans %s\n", mi[i].ti.text_untranslated);
     373           0 :             GMenuGetMenuPathRecurse( stack, &mi[i], targetmi );
     374           0 :             if( stack[0] != 0 ) {
     375             : //              TRACE("GMenuGetMenuPath() have stack[0]...\n");
     376           0 :                 break;
     377             :             }
     378             :         }
     379             :     }
     380           0 :     if( stack[0] == 0 ) {
     381           0 :         return 0;
     382             :     }
     383             : 
     384             :     static char buffer[PATH_MAX];
     385           0 :     buffer[0] = '\0';
     386           0 :     for( i=0; stack[i]; i++ ) {
     387           0 :         if( i )
     388           0 :             strcat(buffer,".");
     389           0 :         if( stack[i]->ti.text_untranslated )
     390             :         {
     391             : //          TRACE("adding %s\n", HKTextInfoToUntranslatedTextFromTextInfo( &stack[i]->ti ));
     392           0 :             strcat( buffer, HKTextInfoToUntranslatedTextFromTextInfo( &stack[i]->ti ));
     393             :         }
     394           0 :         else if( stack[i]->ti.text )
     395             :         {
     396           0 :             cu_strcat(buffer,stack[i]->ti.text);
     397             :         }
     398             : //      TRACE("GMenuGetMenuPath() stack at i:%d  mi %s\n", i, u_to_c(stack[i]->ti.text));
     399             :     }
     400           0 :     return buffer;
     401             : }
     402             : 
     403           0 : static void GMenuDrawCheckMark(struct gmenu *m, Color fg, int ybase, int r2l) {
     404           0 :     int as = m->as;
     405           0 :     int pt = GDrawPointsToPixels(m->w,1);
     406           0 :     int x = r2l ? m->width-m->tioff+2*pt : m->tickoff;
     407             : 
     408           0 :     while ( pt>1 && 2*pt>=as/3 ) --pt;
     409           0 :     GDrawSetLineWidth(m->w,pt);
     410           0 :     GDrawDrawLine(m->w,x+2*pt,ybase-as/3,x+as/3,ybase-2*pt,fg);
     411           0 :     GDrawDrawLine(m->w,x+2*pt,ybase-as/3-pt,x+as/3,ybase-3*pt,fg);
     412           0 :     GDrawDrawLine(m->w,x+as/3,ybase-2*pt,x+as/3+as/5,ybase-2*pt-as/4,fg);
     413           0 :     GDrawDrawLine(m->w,x+as/3+as/5,ybase-2*pt-as/4,x+as/3+2*as/5,ybase-2*pt-as/3-as/7,fg);
     414           0 :     GDrawDrawLine(m->w,x+as/3+2*as/5,ybase-2*pt-as/3-as/7,x+as/3+3*as/5,ybase-2*pt-as/3-as/7-as/8,fg);
     415           0 : }
     416             : 
     417           0 : static void GMenuDrawUncheckMark(struct gmenu *m, Color fg, int ybase, int r2l) {
     418           0 : }
     419             : 
     420           0 : static void GMenuDrawArrow(struct gmenu *m, int ybase, int r2l) {
     421           0 :     int pt = GDrawPointsToPixels(m->w,1);
     422           0 :     int as = 2*(m->as/2);
     423           0 :     int x = r2l ? m->bp+2*pt : m->rightedge-2*pt;
     424             :     GPoint p[3];
     425             : 
     426           0 :     GDrawSetLineWidth(m->w,pt);
     427           0 :     if ( r2l ) {
     428           0 :         p[0].x = x;                     p[0].y = ybase-as/2;
     429           0 :         p[1].x = x+1*(as/2);            p[1].y = ybase;
     430           0 :         p[2].x = p[1].x;                p[2].y = ybase-as;
     431             : 
     432             :         // If rendering menus in standard (3-dimensional) look, use the shadow colors for fake relief.
     433             :         // Otherwise, use foreground colors.
     434           0 :         if (menu_3d_look) {
     435           0 :                 GDrawDrawLine(m->w,p[0].x,p[0].y,p[2].x,p[2].y,m->box->border_brighter);
     436           0 :                 GDrawDrawLine(m->w,p[0].x+pt,p[0].y,p[2].x+pt,p[2].y+pt,m->box->border_brighter);
     437           0 :                 GDrawDrawLine(m->w,p[1].x,p[1].y,p[0].x,p[0].y,m->box->border_darkest);
     438           0 :                 GDrawDrawLine(m->w,p[1].x+pt,p[1].y-pt,p[0].x-pt,p[0].y,m->box->border_darkest);
     439             :         } else {
     440           0 :                 GDrawDrawLine(m->w,p[0].x,p[0].y,p[2].x,p[2].y,m->box->main_foreground);
     441           0 :                 GDrawDrawLine(m->w,p[0].x+pt,p[0].y,p[2].x+pt,p[2].y+pt,m->box->main_foreground);
     442           0 :                 GDrawDrawLine(m->w,p[1].x,p[1].y,p[0].x,p[0].y,m->box->main_foreground);
     443           0 :                 GDrawDrawLine(m->w,p[1].x+pt,p[1].y-pt,p[0].x-pt,p[0].y,m->box->main_foreground);
     444             :         }
     445             :     } else {
     446           0 :         p[0].x = x;                     p[0].y = ybase-as/2;
     447           0 :         p[1].x = x-1*(as/2);            p[1].y = ybase;
     448           0 :         p[2].x = p[1].x;                p[2].y = ybase-as;
     449             : 
     450           0 :         if (menu_3d_look) {
     451           0 :                 GDrawDrawLine(m->w,p[0].x,p[0].y,p[2].x,p[2].y,m->box->border_brighter);
     452           0 :                 GDrawDrawLine(m->w,p[0].x-pt,p[0].y,p[2].x+pt,p[2].y+pt,m->box->border_brighter);
     453           0 :                 GDrawDrawLine(m->w,p[1].x,p[1].y,p[0].x,p[0].y,m->box->border_darkest);
     454           0 :                 GDrawDrawLine(m->w,p[1].x+pt,p[1].y-pt,p[0].x-pt,p[0].y,m->box->border_darkest);
     455             :         } else {
     456           0 :                 GDrawDrawLine(m->w,p[0].x,p[0].y,p[2].x,p[2].y,m->box->main_foreground);
     457           0 :                 GDrawDrawLine(m->w,p[0].x-pt,p[0].y,p[2].x+pt,p[2].y+pt,m->box->main_foreground);
     458           0 :                 GDrawDrawLine(m->w,p[1].x,p[1].y,p[0].x,p[0].y,m->box->main_foreground);
     459           0 :                 GDrawDrawLine(m->w,p[1].x+pt,p[1].y-pt,p[0].x-pt,p[0].y,m->box->main_foreground);
     460             :         }
     461             :     }
     462           0 : }
     463             : 
     464           0 : static void GMenuDrawUpArrow(struct gmenu *m, int ybase) {
     465           0 :     int pt = GDrawPointsToPixels(m->w,1);
     466           0 :     int x = (m->rightedge+m->tickoff)/2;
     467           0 :     int as = 2*(m->as/2);
     468             :     GPoint p[3];
     469             : 
     470           0 :     p[0].x = x;                 p[0].y = ybase - as;
     471           0 :     p[1].x = x-as;              p[1].y = ybase;
     472           0 :     p[2].x = x+as;              p[2].y = ybase;
     473             : 
     474           0 :     GDrawSetLineWidth(m->w,pt);
     475             : 
     476             :     // If rendering menus in standard (3-dimensional) look, use the shadow colors for fake relief.
     477             :     // Otherwise, use foreground colors.
     478           0 :     if (menu_3d_look) {
     479           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y,p[1].x,p[1].y,m->box->border_brightest);
     480           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,m->box->border_brightest);
     481           0 :         GDrawDrawLine(m->w,p[1].x,p[1].y,p[2].x,p[2].y,m->box->border_darker);
     482           0 :         GDrawDrawLine(m->w,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,m->box->border_darker);
     483           0 :         GDrawDrawLine(m->w,p[2].x,p[2].y,p[0].x,p[0].y,m->box->border_darkest);
     484           0 :         GDrawDrawLine(m->w,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,m->box->border_darkest);
     485             :     } else {
     486           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y,p[1].x,p[1].y,m->box->main_foreground);
     487           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,m->box->main_foreground);
     488           0 :         GDrawDrawLine(m->w,p[1].x,p[1].y,p[2].x,p[2].y,m->box->main_foreground);
     489           0 :         GDrawDrawLine(m->w,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,m->box->main_foreground);
     490           0 :         GDrawDrawLine(m->w,p[2].x,p[2].y,p[0].x,p[0].y,m->box->main_foreground);
     491           0 :         GDrawDrawLine(m->w,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,m->box->main_foreground);
     492             :     }
     493           0 : }
     494             : 
     495           0 : static void GMenuDrawDownArrow(struct gmenu *m, int ybase) {
     496           0 :     int pt = GDrawPointsToPixels(m->w,1);
     497           0 :     int x = (m->rightedge+m->tickoff)/2;
     498           0 :     int as = 2*(m->as/2);
     499             :     GPoint p[3];
     500             : 
     501           0 :     p[0].x = x;                 p[0].y = ybase;
     502           0 :     p[1].x = x-as;              p[1].y = ybase - as;
     503           0 :     p[2].x = x+as;              p[2].y = ybase - as;
     504             : 
     505           0 :     GDrawSetLineWidth(m->w,pt);
     506             : 
     507             :     // If rendering menus in standard (3-dimensional) look, use the shadow colors for fake relief.
     508             :     // Otherwise, use foreground colors.
     509           0 :     if (menu_3d_look) {
     510           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y,p[1].x,p[1].y,m->box->border_darker);
     511           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,m->box->border_darker);
     512           0 :         GDrawDrawLine(m->w,p[1].x,p[1].y,p[2].x,p[2].y,m->box->border_brightest);
     513           0 :         GDrawDrawLine(m->w,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,m->box->border_brightest);
     514           0 :         GDrawDrawLine(m->w,p[2].x,p[2].y,p[0].x,p[0].y,m->box->border_darkest);
     515           0 :         GDrawDrawLine(m->w,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,m->box->border_darkest);
     516             :     } else {
     517           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y,p[1].x,p[1].y,m->box->main_foreground);
     518           0 :         GDrawDrawLine(m->w,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,m->box->main_foreground);
     519           0 :         GDrawDrawLine(m->w,p[1].x,p[1].y,p[2].x,p[2].y,m->box->main_foreground);
     520           0 :         GDrawDrawLine(m->w,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,m->box->main_foreground);
     521           0 :         GDrawDrawLine(m->w,p[2].x,p[2].y,p[0].x,p[0].y,m->box->main_foreground);
     522           0 :         GDrawDrawLine(m->w,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,m->box->main_foreground);
     523             :     }
     524           0 : }
     525             : 
     526             : /**
     527             :  * Return the menu bar at the top of this menu list
     528             :  * */
     529           0 : static GMenuBar * getTopLevelMenubar( struct gmenu *m ) {
     530           0 :     while( m->parent ) {
     531           0 :         m = m->parent;
     532             :     }
     533           0 :     return m->menubar;
     534             : }
     535             : 
     536             : 
     537           0 : static int GMenuDrawMenuLine(struct gmenu *m, GMenuItem *mi, int y,GWindow pixmap) {
     538             :     unichar_t shortbuf[300];
     539           0 :     int as = GTextInfoGetAs(m->w,&mi->ti,m->font);
     540             :     int h, width;
     541           0 :     Color fg = m->box->main_foreground;
     542             :     GRect old, new;
     543           0 :     int ybase = y+as;
     544           0 :     int r2l = false;
     545             :     int x;
     546             : 
     547             :     //printf("GMenuDrawMenuLine(top)\n");
     548             :     /* if(mi->ti.text) */
     549             :     /*  TRACE("GMenuDrawMenuLine() mi:%s\n",u_to_c(mi->ti.text)); */
     550             : 
     551           0 :     new.x = m->tickoff; new.width = m->rightedge-m->tickoff;
     552           0 :     new.y = y; new.height = GTextInfoGetHeight(pixmap,&mi->ti,m->font);
     553           0 :     GDrawPushClip(pixmap,&new,&old);
     554             : 
     555           0 :     if ( mi->ti.fg!=COLOR_DEFAULT && mi->ti.fg!=COLOR_UNKNOWN )
     556           0 :         fg = mi->ti.fg;
     557           0 :     if ( mi->ti.disabled || m->disabled )
     558           0 :         fg = m->box->disabled_foreground;
     559           0 :     if ( fg==COLOR_DEFAULT )
     560           0 :         fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap));
     561           0 :     if ( mi->ti.text!=NULL && isrighttoleft(mi->ti.text[0]) )
     562           0 :         r2l = true;
     563             : 
     564           0 :     if ( r2l )
     565           0 :         x = m->width-m->tioff-GTextInfoGetWidth(pixmap,&mi->ti,m->font);
     566             :     else
     567           0 :         x = m->tioff;
     568           0 :     h = GTextInfoDraw(pixmap,x,y,&mi->ti,m->font,
     569           0 :             (mi->ti.disabled || m->disabled )?m->box->disabled_foreground:fg,
     570           0 :             m->box->active_border,new.y+new.height);
     571           0 :     if ( mi->ti.checkable ) {
     572           0 :         if ( mi->ti.checked )
     573           0 :             GMenuDrawCheckMark(m,fg,ybase,r2l);
     574             :         else
     575           0 :             GMenuDrawUncheckMark(m,fg,ybase,r2l);
     576             :     }
     577             : 
     578           0 :     if ( mi->sub!=NULL )
     579           0 :         GMenuDrawArrow(m,ybase,r2l);
     580             :     else
     581             :     {
     582           0 :         _shorttext(mi->shortcut,0,shortbuf);
     583           0 :         uint16 short_mask = mi->short_mask;
     584             : 
     585             :         /* TRACE("m->menubar: %p\n", m->menubar ); */
     586             :         /* TRACE("m->parent: %p\n", m->parent ); */
     587             :         /* TRACE("m->toplevel: %p\n", getTopLevelMenubar(m)); */
     588             : 
     589             :         /**
     590             :          * Grab the hotkey if there is one. First we work out the
     591             :          * menubar for this menu item, and then get the path from the
     592             :          * menubar to the menuitem and pass that to the hotkey system
     593             :          * to get the hotkey if there is one for that menu item.
     594             :          */
     595           0 :         GMenuBar* toplevel = getTopLevelMenubar(m);
     596           0 :         Hotkey* hk = 0;
     597           0 :         if( toplevel )
     598             :         {
     599           0 :             hk = hotkeyFindByMenuPath( toplevel->g.base,
     600             :                                        GMenuGetMenuPath( toplevel->mi, mi ));
     601             :         }
     602           0 :         else if( m->owner && strlen(m->subMenuName) )
     603             :         {
     604           0 :             hk = hotkeyFindByMenuPathInSubMenu( m->owner, m->subMenuName,
     605             :                                                 GMenuGetMenuPath( m->mi, mi ));
     606             :         }
     607             : 
     608           0 :         short_mask = 0;
     609           0 :         uc_strcpy(shortbuf,"");
     610             : 
     611           0 :         if( hk )
     612             :         {
     613             :             /* TRACE("m->menubar->mi: %p\n", toplevel->mi ); */
     614             :             /* TRACE("m->menubar->window: %p\n", toplevel->g.base ); */
     615             :             /* TRACE("drawline... hk: %p\n", hk ); */
     616           0 :             short_mask = hk->state;
     617           0 :             char* keydesc = hk->text;
     618           0 :             if( mac_menu_icons )
     619             :             {
     620           0 :                 keydesc = hotkeyTextToMacModifiers( keydesc );
     621             :             }
     622           0 :             utf82u_strcpy( shortbuf, keydesc );
     623           0 :             if( keydesc != hk->text )
     624           0 :                 free( keydesc );
     625             :         }
     626             : 
     627           0 :         width = GDrawGetTextWidth(pixmap,shortbuf,-1);
     628           0 :         if ( r2l )
     629             :         {
     630           0 :             GDrawDrawText(pixmap,m->bp,ybase,shortbuf,-1,fg);
     631             :         }
     632             :         else
     633             :         {
     634           0 :             int x = m->rightedge-width;
     635           0 :             GDrawDrawText(pixmap,x,ybase,shortbuf,-1,fg);
     636             :         }
     637             :     }
     638             : 
     639           0 :     GDrawPopClip(pixmap,&old);
     640           0 : return( y + h );
     641             : }
     642             : 
     643           0 : static int gmenu_expose(struct gmenu *m, GEvent *event,GWindow pixmap) {
     644             :     GRect old1, old2;
     645             :     GRect r;
     646             :     int i;
     647             : 
     648             :     /* TRACE("gmenu_expose() m:%p ev:%p\n",m,event); */
     649           0 :     GDrawPushClip(pixmap,&event->u.expose.rect,&old1);
     650           0 :     r.x = 0; r.width = m->width; r.y = 0; r.height = m->height;
     651           0 :     GBoxDrawBackground(pixmap,&r,m->box,gs_active,false);
     652           0 :     GBoxDrawBorder(pixmap,&r,m->box,gs_active,false);
     653           0 :     r.x = m->tickoff; r.width = m->rightedge-m->tickoff;
     654           0 :     r.y = m->bp; r.height = m->height - 2*m->bp;
     655           0 :     GDrawPushClip(pixmap,&r,&old2);
     656           0 :     for ( i = event->u.expose.rect.y/m->fh+m->offtop; i<m->mcnt &&
     657           0 :             i<=(event->u.expose.rect.y+event->u.expose.rect.height)/m->fh+m->offtop;
     658           0 :             ++i ) {
     659           0 :         if ( i==m->offtop && m->offtop!=0 && m->vsb==NULL )
     660           0 :             GMenuDrawUpArrow(m, m->bp+m->as);
     661           0 :         else if ( m->lcnt!=m->mcnt && i==m->lcnt+m->offtop-1 && i!=m->mcnt-1 ) {
     662           0 :             if ( m->vsb==NULL )
     663           0 :                 GMenuDrawDownArrow(m, m->bp+(i-m->offtop)*m->fh+m->as);
     664             :             else
     665           0 :                 GMenuDrawMenuLine(m, &m->mi[i], m->bp+(i-m->offtop)*m->fh, pixmap);
     666           0 :     break;      /* Otherwise we get bits of the line after the last */
     667             :         } else
     668           0 :             GMenuDrawMenuLine(m, &m->mi[i], m->bp+(i-m->offtop)*m->fh, pixmap);
     669             :     }
     670           0 :     GDrawPopClip(pixmap,&old2);
     671           0 :     GDrawPopClip(pixmap,&old1);
     672           0 : return( true );
     673             : }
     674             : 
     675           0 : static void GMenuDrawLines(struct gmenu *m, int ln, int cnt) {
     676             :     GRect r, old1, old2, winrect;
     677             : 
     678           0 :     winrect.x = 0; winrect.width = m->width; winrect.y = 0; winrect.height = m->height;
     679           0 :     r = winrect; r.height = cnt*m->fh;
     680           0 :     r.y = (ln-m->offtop)*m->fh+m->bp;
     681           0 :     GDrawPushClip(m->w,&r,&old1);
     682           0 :     GBoxDrawBackground(m->w,&winrect,m->box,gs_active,false);
     683           0 :     GBoxDrawBorder(m->w,&winrect,m->box,gs_active,false);
     684           0 :     r.x = m->tickoff; r.width = m->rightedge-r.x;
     685           0 :     GDrawPushClip(m->w,&r,&old2);
     686           0 :     cnt += ln;
     687           0 :     for ( ; ln<cnt; ++ln )
     688           0 :         GMenuDrawMenuLine(m, &m->mi[ln], m->bp+(ln-m->offtop)*m->fh,m->w);
     689           0 :     GDrawPopClip(m->w,&old2);
     690           0 :     GDrawPopClip(m->w,&old1);
     691           0 : }
     692             : 
     693           0 : static void GMenuSetPressed(struct gmenu *m, int pressed) {
     694           0 :     while ( m->child!=NULL ) m = m->child;
     695           0 :     while ( m->parent!=NULL ) {
     696           0 :         m->pressed = pressed;
     697           0 :         m = m->parent;
     698             :     }
     699           0 :     m->pressed = pressed;
     700           0 :     if ( m->menubar!=NULL )
     701           0 :         m->menubar->pressed = pressed;
     702           0 : }
     703             : 
     704           0 : static void _GMenuDestroy(struct gmenu *m) {
     705           0 :     if ( m->dying )
     706           0 : return;
     707           0 :     m->dying = true;
     708           0 :     if ( m->line_with_mouse!=-1 )
     709           0 :         m->mi[m->line_with_mouse].ti.selected = false;
     710           0 :     if ( m->child!=NULL )
     711           0 :         _GMenuDestroy(m->child);
     712           0 :     if ( m->parent!=NULL )
     713           0 :         m->parent->child = NULL;
     714           0 :     else if ( m->menubar!=NULL ) {
     715           0 :         m->menubar->child = NULL;
     716           0 :         m->menubar->pressed = false;
     717           0 :         _GWidget_ClearPopupOwner((GGadget *) (m->menubar));
     718           0 :         _GWidget_ClearGrabGadget((GGadget *) (m->menubar));
     719           0 :         GMenuBarChangeSelection(m->menubar,-1,NULL);
     720             :     }
     721           0 :     GDrawDestroyWindow(m->w);
     722             :     /* data are freed when we get the destroy event !!!! */
     723             : }
     724             : 
     725           0 : static void GMenuDestroy(struct gmenu *m) {
     726           0 :     GDrawPointerUngrab(GDrawGetDisplayOfWindow(m->w));
     727           0 :     if ( menu_grabs && m->parent!=NULL )
     728           0 :         GDrawPointerGrab(m->parent->w);
     729           0 :     _GMenuDestroy(m);
     730           0 : }
     731             : 
     732           0 : static void GMenuHideAll(struct gmenu *m) {
     733           0 :     if ( m!=NULL ) {
     734           0 :         struct gmenu *s = m;
     735           0 :         GDrawPointerUngrab(GDrawGetDisplayOfWindow(m->w));
     736           0 :         while ( m->parent ) m = m->parent;
     737           0 :         while ( m ) {
     738           0 :             m->hidden = true;
     739           0 :             GDrawSetVisible(m->w,false);
     740           0 :             m=m->child;
     741             :         }
     742           0 :         GDrawSync(GDrawGetDisplayOfWindow(s->w));
     743           0 :         GDrawProcessPendingEvents(GDrawGetDisplayOfWindow(s->w));
     744             :     }
     745           0 : }
     746             : 
     747           0 : static void GMenuDismissAll(struct gmenu *m) {
     748           0 :     if ( m!=NULL ) {
     749           0 :         while ( m->parent ) m = m->parent;
     750           0 :         GMenuDestroy(m);
     751             :     }
     752           0 : }
     753             : 
     754           0 : static void UnsetInitialPress(struct gmenu *m) {
     755           0 :     while ( m!=NULL ) {
     756           0 :         m->initial_press = false;
     757           0 :         if ( m->menubar!=NULL ) m->menubar->initial_press = false;
     758           0 :         m = m->parent;
     759             :     }
     760           0 : }
     761             : 
     762           0 : static void GMenuChangeSelection(struct gmenu *m, int newsel,GEvent *event) {
     763           0 :     int old=m->line_with_mouse;
     764             : 
     765           0 :     if ( old==newsel )
     766           0 : return;
     767           0 :     if ( newsel==m->mcnt )
     768           0 : return;
     769             : 
     770           0 :     if ( m->child!=NULL ) {
     771           0 :         GMenuDestroy(m->child);
     772           0 :         m->child = NULL;
     773             :     }
     774           0 :     UnsetInitialPress(m);
     775           0 :     m->line_with_mouse = newsel;
     776           0 :     if ( newsel!=-1 )
     777           0 :         m->mi[newsel].ti.selected = true;
     778           0 :     if ( old!=-1 )
     779           0 :         m->mi[old].ti.selected = false;
     780             : 
     781           0 :     if ( newsel==old+1 && old!=-1 ) {
     782           0 :         GMenuDrawLines(m,old,2);
     783           0 :     } else if ( old==newsel+1 && newsel!=-1 ) {
     784           0 :         GMenuDrawLines(m,newsel,2);
     785             :     } else {
     786           0 :         if ( newsel!=-1 )
     787           0 :             GMenuDrawLines(m,newsel,1);
     788           0 :         if ( old!=-1 )
     789           0 :             GMenuDrawLines(m,old,1);
     790             :     }
     791           0 :     if ( newsel!=-1 ) {
     792           0 :         if ( m->mi[newsel].moveto!=NULL )
     793           0 :             (m->mi[newsel].moveto)(m->owner,&m->mi[newsel],event);
     794           0 :         if ( m->mi[newsel].sub!=NULL )
     795           0 :             m->child = GMenuCreateSubMenu(m,m->mi[newsel].sub,
     796           0 :                     m->disabled || m->mi[newsel].ti.disabled);
     797             :     }
     798             : }
     799             : 
     800           0 : static void GMenuBarChangeSelection(GMenuBar *mb, int newsel,GEvent *event) {
     801           0 :     int old=mb->entry_with_mouse;
     802             :     GMenuItem *mi;
     803             : 
     804           0 :     if ( old==newsel )
     805           0 : return;
     806           0 :     if ( mb->child!=NULL ) {
     807           0 :         int waspressed = mb->pressed;
     808           0 :         GMenuDestroy(mb->child);
     809           0 :         mb->child = NULL;
     810           0 :         mb->pressed = waspressed;
     811             :     }
     812           0 :     mb->entry_with_mouse = newsel;
     813           0 :     if ( newsel!=-1 )
     814           0 :         mb->mi[newsel].ti.selected = true;
     815           0 :     if ( old!=-1 )
     816           0 :         mb->mi[old].ti.selected = false;
     817             : 
     818           0 :     _ggadget_redraw(&mb->g);
     819           0 :     if ( newsel!=-1 ) {
     820           0 :         mi = newsel==mb->lastmi ? mb->fake : &mb->mi[newsel];
     821           0 :         if ( mi->moveto!=NULL )
     822           0 :             (mi->moveto)(mb->g.base,mi,event);
     823           0 :         if ( mi->sub!=NULL )
     824           0 :             mb->child = GMenuCreatePulldownMenu(mb,mi->sub,mi->ti.disabled);
     825             :     }
     826             : }
     827             : 
     828           0 : static int MParentInitialPress(struct gmenu *m) {
     829           0 :     if ( m->parent!=NULL )
     830           0 : return( m->parent->initial_press );
     831           0 :     else if ( m->menubar!=NULL )
     832           0 : return( m->menubar->initial_press );
     833             : 
     834           0 : return( false );
     835             : }
     836             : 
     837           0 : static int gmenu_mouse(struct gmenu *m, GEvent *event) {
     838             :     GPoint p;
     839             :     struct gmenu *testm;
     840             : 
     841           0 :     if ( m->hidden || (m->child!=NULL && m->child->hidden))
     842           0 : return( true );
     843             : 
     844           0 :     if ( event->type == et_crossing ) {
     845           0 :         if ( !event->u.crossing.entered )
     846           0 :             UnsetInitialPress(m);
     847           0 : return( true );
     848           0 :     } else if ((event->type==et_mouseup || event->type==et_mousedown) &&
     849           0 :                (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
     850             :         //From scrollbar.c: X treats a scroll as a mousedown/mouseup event
     851           0 :         return GGadgetDispatchEvent(m->vsb,event);
     852             :     }
     853             : 
     854           0 :     p.x = event->u.mouse.x; p.y = event->u.mouse.y;
     855             : 
     856           0 :     for ( testm=m; testm->child!=NULL; testm = testm->child );
     857           0 :     if ( testm->scrollit && testm!=m ) {
     858           0 :         GDrawCancelTimer(testm->scrollit);
     859           0 :         testm->scrollit = NULL;
     860             :     }
     861           0 :     for ( ; testm!=NULL; testm=testm->parent )
     862           0 :         if ( GDrawEventInWindow(testm->w,event) )
     863           0 :     break;
     864             : 
     865           0 :     if ( testm!=m && testm!=NULL ) {
     866           0 :         GDrawPointerGrab(testm->w);
     867           0 :         GDrawTranslateCoordinates(m->w,testm->w,&p);
     868           0 :         m = testm;
     869           0 :     } else if ( testm==NULL /*&& event->u.mouse.y<0*/ ) {/* menubars can be below the menu if no room on screen */
     870           0 :         for ( testm=m; testm->parent!=NULL; testm=testm->parent );
     871           0 :         if ( testm->menubar!=NULL ) {
     872           0 :             GDrawTranslateCoordinates(m->w,testm->menubar->g.base,&p);
     873           0 :             if ( p.x>=0 && p.y>=0 &&
     874           0 :                     p.x<testm->menubar->g.inner.x+testm->menubar->g.inner.width &&
     875           0 :                     p.y<testm->menubar->g.inner.y+testm->menubar->g.inner.height ) {
     876             :                 /*GDrawPointerGrab(testm->menubar->g.base);*/     /* Don't do this */
     877           0 :                 event->u.mouse.x = p.x; event->u.mouse.y = p.y;
     878           0 : return( (GDrawGetEH(testm->menubar->g.base))(testm->menubar->g.base,event));
     879             :             }
     880             :         }
     881           0 :         testm = NULL;
     882             :     }
     883           0 :     if ( testm==NULL ) {
     884           0 :         if ( event->type==et_mousedown )
     885           0 :             GMenuDismissAll(m);
     886           0 :         else if ( event->type==et_mouseup )
     887           0 :             GMenuSetPressed(m,false);
     888           0 :         else if ( m->pressed )
     889           0 :             GMenuChangeSelection(m,-1,event);
     890           0 : return( true );
     891             :     }
     892             : 
     893           0 :     event->u.mouse.x = p.x; event->u.mouse.y = p.y; event->w = m->w;
     894           0 :     if (( m->pressed && event->type==et_mousemove ) ||
     895           0 :             event->type == et_mousedown ) {
     896           0 :         int l = (event->u.mouse.y-m->bp)/m->fh;
     897           0 :         int i = l + m->offtop;
     898           0 :         if ( m->scrollit!=NULL )
     899             :             ;
     900           0 :         else if ( event->u.mouse.y<m->bp && event->type==et_mousedown )
     901           0 :             GMenuDismissAll(m);
     902           0 :         else if ( l==0 && m->offtop!=0 && m->vsb==NULL ) {
     903           0 :             GMenuChangeSelection(m,-1,event);
     904           0 :             if ( m->scrollit==NULL )
     905           0 :                 m->scrollit = GDrawRequestTimer(m->w,1,_GScrollBar_RepeatTime,m);
     906           0 :             m->scrollup = true;
     907           0 :         } else if ( l>=m->lcnt-1 && m->offtop+m->lcnt<m->mcnt && m->vsb==NULL ) {
     908           0 :             GMenuChangeSelection(m,-1,event);
     909           0 :             if ( m->scrollit==NULL )
     910           0 :                 m->scrollit = GDrawRequestTimer(m->w,1,_GScrollBar_RepeatTime,m);
     911           0 :             m->scrollup = false;
     912           0 :         } else if ( event->type == et_mousedown && m->child!=NULL &&
     913           0 :                 i == m->line_with_mouse ) {
     914           0 :             GMenuChangeSelection(m,-1,event);
     915           0 :         } else if ( i >= m->mcnt ){
     916           0 :             GMenuChangeSelection(m,-1,event);
     917             :         } else
     918           0 :             GMenuChangeSelection(m,i,event);
     919           0 :         if ( event->type == et_mousedown ) {
     920           0 :             GMenuSetPressed(m,true);
     921           0 :             if ( m->child!=NULL )
     922           0 :                 m->initial_press = true;
     923             :         }
     924           0 :     } else if ( event->type == et_mouseup && m->child==NULL ) {
     925           0 :         if ( m->scrollit!=NULL ) {
     926           0 :             GDrawCancelTimer(m->scrollit);
     927           0 :             m->scrollit = NULL;
     928           0 :         } else if ( event->u.mouse.y>=m->bp && event->u.mouse.x>=0 &&
     929           0 :                 event->u.mouse.y<m->height-m->bp &&
     930           0 :                 event->u.mouse.x < m->width &&
     931           0 :                 !MParentInitialPress(m)) {
     932           0 :             int l = (event->u.mouse.y-m->bp)/m->fh;
     933           0 :             int i = l + m->offtop;
     934           0 :             if ( !( l==0 && m->offtop!=0 && m->vsb==NULL ) &&
     935           0 :                     !( l==m->lcnt-1 && m->offtop+m->lcnt<m->mcnt && m->vsb==NULL ) &&
     936           0 :                     !m->disabled &&
     937           0 :                     !m->mi[i].ti.disabled && !m->mi[i].ti.line ) {
     938           0 :                 if ( m->mi[i].ti.checkable )
     939           0 :                     m->mi[i].ti.checked = !m->mi[i].ti.checked;
     940           0 :                 GMenuHideAll(m);
     941           0 :                 GMenuDismissAll(m);
     942           0 :                 if ( m->mi[i].invoke!=NULL )
     943           0 :                     (m->mi[i].invoke)(m->owner,&m->mi[i],event);
     944             :             }
     945             :         }
     946           0 :     } else if ( event->type == et_mouseup ) {
     947           0 :         UnsetInitialPress(m);
     948           0 :         GMenuSetPressed(m,false);
     949             :     } else
     950           0 : return( false );
     951             : 
     952           0 : return( true );
     953             : }
     954             : 
     955           0 : static int gmenu_timer(struct gmenu *m, GEvent *event) {
     956           0 :     if ( m->scrollup ) {
     957           0 :         if ( m->offtop==0 )
     958           0 : return(true);
     959           0 :         if ( --m->offtop<0 ) m->offtop = 0;
     960             :     } else {
     961           0 :         if ( m->offtop == m->mcnt-m->lcnt )
     962           0 : return( true );
     963           0 :         ++m->offtop;
     964           0 :         if ( m->offtop + m->lcnt > m->mcnt )
     965           0 :             m->offtop = m->mcnt-m->lcnt;
     966             :     }
     967           0 :     GDrawRequestExpose(m->w, NULL, false);
     968           0 : return( true );
     969             : }
     970             : 
     971           0 : static int GMenuKeyInvoke(struct gmenu *m, int i) {
     972           0 :     GMenuChangeSelection(m,i,NULL);
     973           0 :     if ( m->mi[i].ti.checkable )
     974           0 :         m->mi[i].ti.checked = !m->mi[i].ti.checked;
     975           0 :     if ( m->mi[i].sub==NULL ) {
     976           0 :         GMenuHideAll(m);
     977             :     }
     978           0 :     if ( m->mi[i].invoke!=NULL )
     979           0 :         (m->mi[i].invoke)(m->owner,&m->mi[i],NULL);
     980           0 :     if ( m->mi[i].sub==NULL ) {
     981           0 :         GMenuDismissAll(m);
     982             :     }
     983           0 : return( true );
     984             : }
     985             : 
     986           0 : static int GMenuBarKeyInvoke(struct gmenubar *mb, int i) {
     987           0 :     GMenuBarChangeSelection(mb,i,NULL);
     988           0 :     if ( mb->mi[i].invoke!=NULL )
     989           0 :         (mb->mi[i].invoke)(mb->g.base,&mb->mi[i],NULL);
     990           0 : return( true );
     991             : }
     992             : 
     993             : /**
     994             :  * Remove any instances of 'ch' from 'ret' and return 'ret'
     995             :  *
     996             :  * When ch is encountered it is removed from the string, with all the
     997             :  * characters following it moved left to cover over the space ch used
     998             :  * to occupy.
     999             :  */
    1000           0 : static char* str_remove_all_single_char( char * ret, char ch )
    1001             : {
    1002           0 :     char* src = ret;
    1003           0 :     char* dst = ret;
    1004           0 :     for( ; *src; src++ )
    1005             :     {
    1006           0 :         if( *src == ch )
    1007           0 :             continue;
    1008             : 
    1009           0 :         *dst = *src;
    1010           0 :         dst++;
    1011             :     }
    1012           0 :     *dst = '\0';
    1013           0 :     return ret;
    1014             : }
    1015             : 
    1016             : /**
    1017             :  * Given a text_untranslated which may be either the hotkey of format:
    1018             :  *
    1019             :  * Foo|ShortCutKey
    1020             :  * Win*Foo|ShortCutKey
    1021             :  * _Foo
    1022             :  *
    1023             :  * return just the english text for the entry, ie, Foo
    1024             :  *
    1025             :  * The return value is owned by this function, do not free it.
    1026             :  */
    1027           0 : char* HKTextInfoToUntranslatedText(char *text_untranslated) {
    1028             :     char ret[PATH_MAX+1];
    1029             :     char* pt;
    1030             :     int i;
    1031             : 
    1032           0 :     strncpy(ret,text_untranslated,PATH_MAX);
    1033             : 
    1034           0 :     if( (pt=strchr(ret,'*')) )
    1035           0 :         for (i=0; pt[i]!='\0'; i++)
    1036           0 :             ret[i]=pt[i+1];
    1037           0 :     if( (pt=strchr(ret,'|')) )
    1038           0 :         *pt = '\0';
    1039           0 :     str_remove_all_single_char( ret, '_' );
    1040           0 :     return copy(ret);
    1041             : }
    1042             : 
    1043             : /**
    1044             :  * Call HKTextInfoToUntranslatedText on ti->text_untranslated
    1045             :  * guarding against the chance of null for ti, and ti->text_untranslated
    1046             :  */
    1047           0 : char* HKTextInfoToUntranslatedTextFromTextInfo( GTextInfo* ti )
    1048             : {
    1049           0 :     if( !ti )
    1050           0 :         return 0;
    1051           0 :     if( !ti->text_untranslated )
    1052           0 :         return 0;
    1053           0 :     return HKTextInfoToUntranslatedText( ti->text_untranslated );
    1054             : }
    1055             : 
    1056             : 
    1057             : /**
    1058             :  * return true if the prefix matches the first segment of the given action.
    1059             :  * For example,
    1060             :  * return will be 0 when action = foo.bar.baz prefix = bar
    1061             :  * return will be 0 when action = foo.bar.baz prefix = fooo
    1062             :  * return will be 1 when action = foo.bar.baz prefix = foo
    1063             :  * return will be 1 when action = baz prefix = baz
    1064             :  */
    1065           0 : static int HKActionMatchesFirstPartOf( char* action, char* prefix_const, int munge )
    1066             : {
    1067             :     char prefix[PATH_MAX+1];
    1068           0 :     char* pt = 0;
    1069           0 :     strncpy( prefix, prefix_const, PATH_MAX );
    1070           0 :     if( munge ) {
    1071           0 :         char *tofree = HKTextInfoToUntranslatedText(prefix_const);
    1072           0 :         strncpy( prefix, tofree,PATH_MAX );
    1073           0 :         free(tofree);
    1074             :     }
    1075             : //    TRACE("munge:%d prefix2:%s\n", munge, prefix );
    1076             : 
    1077           0 :     pt = strchr(action,'.');
    1078           0 :     if( !pt )
    1079           0 :         return( 0==strcmp(action,prefix) );
    1080             : 
    1081           0 :     int l = pt - action;
    1082           0 :     if( strlen(prefix) < l )
    1083           0 :         return 0;
    1084           0 :     int rc = strncmp( action, prefix, l );
    1085           0 :     return rc == 0;
    1086             : }
    1087             : 
    1088             : /**
    1089             :  * Call HKActionMatchesFirstPartOf on the given ti.
    1090             :  */
    1091           0 : static int HKActionMatchesFirstPartOfTextInfo( char* action, GTextInfo* ti )
    1092             : {
    1093           0 :     if( ti->text_untranslated )
    1094           0 :         return HKActionMatchesFirstPartOf( action, ti->text_untranslated, 1 );
    1095           0 :     if( ti->text )
    1096           0 :         return HKActionMatchesFirstPartOf( action, u_to_c(ti->text), 0 );
    1097           0 :     return 0;
    1098             : }
    1099             : 
    1100           0 : static char* HKActionPointerPastLeftmostKey( char* action ) {
    1101           0 :     char* pt = strchr(action,'.');
    1102           0 :     if( !pt )
    1103           0 :         return 0;
    1104           0 :     return pt + 1;
    1105             : }
    1106             : 
    1107             : 
    1108             : 
    1109           0 : static GMenuItem *GMenuSearchActionRecursive( GWindow gw,
    1110             :                                               GMenuItem *mi,
    1111             :                                               char* action,
    1112             :                                               GEvent *event,
    1113             :                                               int call_moveto) {
    1114             : 
    1115             : //    TRACE("GMenuSearchAction() action:%s\n", action );
    1116             :     int i;
    1117           0 :     for ( i=0; mi[i].ti.text || mi[i].ti.text_untranslated || mi[i].ti.image || mi[i].ti.line; ++i ) {
    1118             : //      if( mi[i].ti.text )
    1119             : //          TRACE("GMenuSearchActionRecursive() text             : %s\n", u_to_c(mi[i].ti.text) );
    1120             : //      TRACE("GMenuSearchActionRecursive() text_untranslated: %s\n", mi[i].ti.text_untranslated );
    1121           0 :         if ( call_moveto && mi[i].moveto != NULL)
    1122           0 :             (mi[i].moveto)(gw,&(mi[i]),event);
    1123             : 
    1124           0 :         if ( mi[i].sub ) {
    1125           0 :             if( HKActionMatchesFirstPartOfTextInfo( action, &mi[i].ti )) {
    1126           0 :                 char* subaction = HKActionPointerPastLeftmostKey(action);
    1127             : 
    1128             : //              TRACE("GMenuSearchAction() action:%s decending menu:%s\n", action, u_to_c(mi[i].ti.text) );
    1129           0 :                 GMenuItem *ret = GMenuSearchActionRecursive(gw,mi[i].sub,subaction,event,call_moveto);
    1130           0 :                 if ( ret!=NULL )
    1131           0 :                     return( ret );
    1132             :             }
    1133             :         } else {
    1134             : //          TRACE("GMenuSearchAction() action:%s testing menu:%s\n", action, u_to_c(mi[i].ti.text) );
    1135           0 :             if( HKActionMatchesFirstPartOfTextInfo( action, &mi[i].ti )) {
    1136             : //              TRACE("GMenuSearchAction() matching final menu part! action:%s\n", action );
    1137           0 :                 return &mi[i];
    1138             :             }
    1139             :         }
    1140             : 
    1141             :     }
    1142           0 : return( NULL );
    1143             : }
    1144           0 : static GMenuItem *GMenuSearchAction( GWindow gw,
    1145             :                                      GMenuItem *mi,
    1146             :                                      char* action,
    1147             :                                      GEvent *event,
    1148             :                                      int call_moveto) {
    1149           0 :     char* windowType = GDrawGetWindowTypeName( gw );
    1150           0 :     if( !windowType )
    1151           0 :         return 0;
    1152             : //    TRACE("GMenuSearchAction() windowtype:%s\n", windowType );
    1153           0 :     int actionlen = strlen(action);
    1154           0 :     int prefixlen = strlen(windowType) + 1 + strlen("Menu.");
    1155           0 :     if( actionlen < prefixlen ) {
    1156           0 :         return 0;
    1157             :     }
    1158           0 :     action += prefixlen;
    1159           0 :     GMenuItem * ret = GMenuSearchActionRecursive( gw, mi, action,
    1160             :                                                   event, call_moveto );
    1161           0 :     return ret;
    1162             : }
    1163             : 
    1164             : 
    1165             : 
    1166           0 : static GMenuItem *GMenuSearchShortcut(GWindow gw, GMenuItem *mi, GEvent *event,
    1167             :         int call_moveto) {
    1168             :     int i;
    1169           0 :     unichar_t keysym = event->u.chr.keysym;
    1170             : 
    1171           0 :     if ( keysym<GK_Special && islower(keysym))
    1172           0 :         keysym = toupper(keysym); /*getkey(keysym,event->u.chr.state&0x2000 );*/
    1173           0 :     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
    1174           0 :         if ( call_moveto && mi[i].moveto != NULL)
    1175           0 :             (mi[i].moveto)(gw,&(mi[i]),event);
    1176           0 :         if ( mi[i].sub==NULL && mi[i].shortcut == keysym &&
    1177           0 :                 (menumask&event->u.chr.state)==mi[i].short_mask )
    1178           0 : return( &mi[i]);
    1179           0 :         else if ( mi[i].sub!=NULL ) {
    1180           0 :             GMenuItem *ret = GMenuSearchShortcut(gw,mi[i].sub,event,call_moveto);
    1181           0 :             if ( ret!=NULL )
    1182           0 : return( ret );
    1183             :         }
    1184             :     }
    1185           0 : return( NULL );
    1186             : }
    1187             : 
    1188           0 : static int GMenuSpecialKeys(struct gmenu *m, unichar_t keysym, GEvent *event) {
    1189           0 :     switch ( keysym ) {
    1190             :       case GK_Escape:
    1191           0 :         GMenuDestroy(m);
    1192           0 : return( true );
    1193             :       case GK_Return:
    1194           0 :         if ( m->line_with_mouse==-1 ) {
    1195           0 :             int ns=0;
    1196           0 :             while ( ns<m->mcnt && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) ++ns;
    1197           0 :             if ( ns<m->mcnt )
    1198           0 :                 GMenuChangeSelection(m,ns,event);
    1199           0 :         } else if ( m->mi[m->line_with_mouse].sub!=NULL &&
    1200           0 :                 m->child==NULL ) {
    1201           0 :             m->child = GMenuCreateSubMenu(m,m->mi[m->line_with_mouse].sub,
    1202           0 :                     m->disabled || m->mi[m->line_with_mouse].ti.disabled);
    1203             :         } else {
    1204           0 :             int i = m->line_with_mouse;
    1205           0 :             if ( !m->disabled && !m->mi[i].ti.disabled && !m->mi[i].ti.line ) {
    1206           0 :                 if ( m->mi[i].ti.checkable )
    1207           0 :                     m->mi[i].ti.checked = !m->mi[i].ti.checked;
    1208           0 :                 GMenuDismissAll(m);
    1209           0 :                 if ( m->mi[i].invoke!=NULL )
    1210           0 :                     (m->mi[i].invoke)(m->owner,&m->mi[i],event);
    1211             :             } else
    1212           0 :                 GMenuDismissAll(m);
    1213             :         }
    1214           0 : return( true );
    1215             :       case GK_Left: case GK_KP_Left:
    1216           0 :         if ( m->parent!=NULL ) {
    1217           0 :             GMenuDestroy(m);
    1218           0 : return( true );
    1219           0 :         } else if ( m->menubar!=NULL ) {
    1220           0 :             GMenuBar *mb = m->menubar;
    1221           0 :             int en = mb->entry_with_mouse;
    1222           0 :             int lastmi = mb->fake[0].sub!=NULL ? mb->lastmi+1 : mb->lastmi;
    1223           0 :             if ( en>0 ) {
    1224           0 :                 GMenuBarChangeSelection(mb,en-1,event);
    1225             :             } else
    1226           0 :                 GMenuBarChangeSelection(mb,lastmi-1,event);
    1227           0 : return( true );
    1228             :         }
    1229             :         /* Else fall into the "Up" case */
    1230             :       case GK_Up: case GK_KP_Up: case GK_Page_Up: case GK_KP_Page_Up: {
    1231             :         int ns;
    1232           0 :         if ( keysym!=GK_Left && keysym!=GK_KP_Left ) {
    1233           0 :             while ( m->line_with_mouse==-1 && m->parent!=NULL ) {
    1234           0 :                 GMenu *p = m->parent;
    1235           0 :                 GMenuDestroy(m);
    1236           0 :                 m = p;
    1237             :             }
    1238             :         }
    1239           0 :         ns = m->line_with_mouse-1;
    1240           0 :         while ( ns>=0 && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) --ns;
    1241           0 :         if ( ns<0 ) {
    1242           0 :             ns = m->mcnt-1;
    1243           0 :             while ( ns>=0 && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) --ns;
    1244             :         }
    1245           0 :         if ( ns<0 && m->line_with_mouse==-1 ) {   /* Nothing selectable? get rid of menu */
    1246           0 :             GMenuDestroy(m);
    1247           0 : return( true );
    1248             :         }
    1249           0 :         if ( ns<0 ) ns = -1;
    1250           0 :         GMenuChangeSelection(m,ns,NULL);
    1251           0 : return( true );
    1252             :       }
    1253             :       case GK_Right: case GK_KP_Right:
    1254           0 :         if ( m->line_with_mouse!=-1 &&
    1255           0 :                 m->mi[m->line_with_mouse].sub!=NULL && m->child==NULL ) {
    1256           0 :             m->child = GMenuCreateSubMenu(m,m->mi[m->line_with_mouse].sub,
    1257           0 :                     m->disabled || m->mi[m->line_with_mouse].ti.disabled);
    1258           0 : return( true );
    1259           0 :         } else if ( m->parent==NULL && m->menubar!=NULL ) {
    1260           0 :             GMenuBar *mb = m->menubar;
    1261           0 :             int en = mb->entry_with_mouse;
    1262           0 :             int lastmi = mb->fake[0].sub!=NULL ? mb->lastmi+1 : mb->lastmi;
    1263           0 :             if ( en+1<lastmi ) {
    1264           0 :                 GMenuBarChangeSelection(mb,en+1,event);
    1265             :             } else
    1266           0 :                 GMenuBarChangeSelection(mb,0,event);
    1267           0 : return( true );
    1268             :         }
    1269             :       /* Fall through into the "Down" case */
    1270             :       case GK_Down: case GK_KP_Down: case GK_Page_Down: case GK_KP_Page_Down: {
    1271             :         int ns;
    1272           0 :         if ( keysym!=GK_Right && keysym!=GK_KP_Right ) {
    1273           0 :             while ( m->line_with_mouse==-1 && m->parent!=NULL ) {
    1274           0 :                 GMenu *p = m->parent;
    1275           0 :                 GMenuDestroy(m);
    1276           0 :                 m = p;
    1277             :             }
    1278             :         }
    1279           0 :         ns = m->line_with_mouse+1;
    1280           0 :         while ( ns<m->mcnt && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) ++ns;
    1281           0 :         if ( ns>=m->mcnt ) {
    1282           0 :             ns = 0;
    1283           0 :             while ( ns<m->mcnt && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) ++ns;
    1284             :         }
    1285           0 :         if ( ns>=m->mcnt && m->line_with_mouse==-1 ) { /* Nothing selectable? get rid of menu */
    1286           0 :             GMenuDestroy(m);
    1287           0 : return( true );
    1288             :         }
    1289           0 :         GMenuChangeSelection(m,ns,event);
    1290           0 : return( true );
    1291             :       }
    1292             :       case GK_Home: case GK_KP_Home: {
    1293           0 :         int ns=0;
    1294           0 :         while ( ns<m->mcnt && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) ++ns;
    1295           0 :         if ( ns!=m->mcnt )
    1296           0 :             GMenuChangeSelection(m,ns,event);
    1297           0 : return( true );
    1298             :       }
    1299             :       case GK_End: case GK_KP_End: {
    1300           0 :         int ns=m->mcnt-1;
    1301           0 :         while ( ns>=0 && (m->mi[ns].ti.disabled || m->mi[ns].ti.line)) --ns;
    1302           0 :         if ( ns>=0 )
    1303           0 :             GMenuChangeSelection(m,ns,event);
    1304           0 : return( true );
    1305             :       }
    1306             :     }
    1307           0 : return( false );
    1308             : }
    1309             : 
    1310           0 : static int gmenu_key(struct gmenu *m, GEvent *event) {
    1311             :     int i;
    1312             :     GMenuItem *mi;
    1313             :     GMenu *top;
    1314           0 :     unichar_t keysym = event->u.chr.keysym;
    1315             : 
    1316           0 :     if ( islower(keysym)) keysym = toupper(keysym);
    1317           0 :     if ( event->u.chr.state&ksm_meta && !(event->u.chr.state&(menumask&~(ksm_meta|ksm_shift)))) {
    1318             :         /* Only look for mneumonics in the child */
    1319           0 :         while ( m->child!=NULL )
    1320           0 :             m = m->child;
    1321           0 :         for ( i=0; i<m->mcnt; ++i ) {
    1322           0 :             if ( m->mi[i].ti.mnemonic == keysym &&
    1323           0 :                         !m->disabled &&
    1324           0 :                         !m->mi[i].ti.disabled ) {
    1325           0 :                 GMenuKeyInvoke(m,i);
    1326           0 : return( true );
    1327             :             }
    1328             :         }
    1329             :     }
    1330             : 
    1331             :     /* then look for shortcuts everywhere */
    1332           0 :     if ( (event->u.chr.state&(menumask&~ksm_shift)) ||
    1333           0 :             event->u.chr.keysym>=GK_Special ) {
    1334           0 :         for ( top = m; top->parent!=NULL ; top = top->parent );
    1335           0 :         if ( top->menubar!=NULL )
    1336           0 :             mi = GMenuSearchShortcut(top->owner,top->menubar->mi,event,false);
    1337             :         else
    1338           0 :             mi = GMenuSearchShortcut(top->owner,top->mi,event,false);
    1339           0 :         if ( mi!=NULL ) {
    1340           0 :             if ( mi->ti.checkable )
    1341           0 :                 mi->ti.checked = !mi->ti.checked;
    1342           0 :             GMenuHideAll(top);
    1343           0 :             if ( mi->invoke!=NULL )
    1344           0 :                 (mi->invoke)(m->owner,mi,event);
    1345           0 :             GMenuDestroy(m);
    1346           0 : return( true );
    1347             :         }
    1348           0 :         for ( ; m->child!=NULL ; m = m->child );
    1349           0 : return( GMenuSpecialKeys(m,event->u.chr.keysym,event));
    1350             :     }
    1351             : 
    1352           0 : return( false );
    1353             : }
    1354             : 
    1355           0 : static int gmenu_destroy(struct gmenu *m) {
    1356           0 :     if ( most_recent_popup_menu==m )
    1357           0 :         most_recent_popup_menu = NULL;
    1358           0 :     if ( m->donecallback )
    1359           0 :         (m->donecallback)(m->owner);
    1360           0 :     if ( m->freemi )
    1361           0 :         GMenuItemArrayFree(m->mi);
    1362           0 :     free(m);
    1363           0 : return( true );
    1364             : }
    1365             : 
    1366           0 : static int gmenu_eh(GWindow w,GEvent *ge) {
    1367           0 :     GMenu *m = (GMenu *) GDrawGetUserData(w);
    1368             : 
    1369           0 :     switch ( ge->type ) {
    1370             :       case et_map:
    1371             :         /* I need to initialize the input context, but I can't do that until */
    1372             :         /*  the menu pops up */
    1373           0 :         if ( ge->u.map.is_visible && m->gic!=NULL )
    1374           0 :             GDrawSetGIC(w,m->gic,0,20);
    1375           0 : return( true );
    1376             :       case et_expose:
    1377           0 : return( gmenu_expose(m,ge,w));
    1378             :       case et_char:
    1379           0 : return( gmenu_key(m,ge));
    1380             :       case et_mousemove: case et_mousedown: case et_mouseup: case et_crossing:
    1381           0 : return( gmenu_mouse(m,ge));
    1382             :       case et_timer:
    1383           0 : return( gmenu_timer(m,ge));
    1384             :       case et_destroy:
    1385           0 : return( gmenu_destroy(m));
    1386             :       case et_close:
    1387           0 :         GMenuDestroy(m);
    1388           0 : return( true );
    1389             :     }
    1390           0 : return( false );
    1391             : }
    1392             : 
    1393           0 : static int gmenu_scroll(GGadget *g, GEvent *event) {
    1394           0 :     enum sb sbt = event->u.control.u.sb.type;
    1395           0 :     GMenu *m = (GMenu *) (g->data);
    1396           0 :     int newpos = m->offtop;
    1397             : 
    1398           0 :     if ( sbt==et_sb_top )
    1399           0 :         newpos = 0;
    1400           0 :     else if ( sbt==et_sb_bottom )
    1401           0 :         newpos = m->mcnt-m->lcnt;
    1402           0 :     else if ( sbt==et_sb_up ) {
    1403           0 :         --newpos;
    1404           0 :     } else if ( sbt==et_sb_down ) {
    1405           0 :         ++newpos;
    1406           0 :     } else if ( sbt==et_sb_uppage ) {
    1407           0 :         if ( m->lcnt!=1 )            /* Normally we leave one line in window from before, except if only one line fits */
    1408           0 :             newpos -= m->lcnt-1;
    1409             :         else
    1410           0 :             newpos -= 1;
    1411           0 :     } else if ( sbt==et_sb_downpage ) {
    1412           0 :         if ( m->lcnt!=1 )            /* Normally we leave one line in window from before, except if only one line fits */
    1413           0 :             newpos += m->lcnt-1;
    1414             :         else
    1415           0 :             newpos += 1;
    1416             :     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
    1417           0 :         newpos = event->u.control.u.sb.pos;
    1418             :     }
    1419           0 :     if ( newpos+m->lcnt > m->mcnt )
    1420           0 :         newpos = m->mcnt-m->lcnt;
    1421           0 :     if ( newpos<0 )
    1422           0 :         newpos = 0;
    1423           0 :     if ( newpos!= m->offtop ) {
    1424           0 :         m->offtop = newpos;
    1425           0 :         GScrollBarSetPos(m->vsb,newpos);
    1426           0 :         GDrawRequestExpose(m->w,NULL,false);
    1427             :     }
    1428           0 : return( true );
    1429             : }
    1430             : 
    1431           0 : static GMenu *_GMenu_Create( GMenuBar* toplevel,
    1432             :                              GWindow owner,
    1433             :                              GMenuItem *mi,
    1434             :                              GPoint *where,
    1435             :                              int awidth, int aheight,
    1436             :                              GFont *font, int disable,
    1437             :                              char* subMenuName )
    1438             : {
    1439           0 :     GMenu *m = calloc(1,sizeof(GMenu));
    1440             :     GRect pos;
    1441           0 :     GDisplay *disp = GDrawGetDisplayOfWindow(owner);
    1442             :     GWindowAttrs pattrs;
    1443           0 :     int i, width, max_iwidth = 0, max_hkwidth = 0;
    1444             :     unichar_t buffer[300];
    1445             :     extern int _GScrollBar_Width;
    1446             :     int ds, ld, temp, lh;
    1447           0 :     int sbwidth = 0;
    1448             :     GRect screen;
    1449             : 
    1450           0 :     m->owner = owner;
    1451           0 :     m->mi = mi;
    1452           0 :     m->disabled = disable;
    1453           0 :     m->font = font;
    1454           0 :     m->box = &menu_box;
    1455           0 :     m->tickoff = m->tioff = m->bp = GBoxBorderWidth(owner,m->box);
    1456           0 :     m->line_with_mouse = -1;
    1457           0 :     if( subMenuName )
    1458           0 :         strncpy(m->subMenuName,subMenuName,sizeof(m->subMenuName)-1);
    1459             : /* Mnemonics in menus don't work under gnome. Turning off nodecor makes them */
    1460             : /*  work, but that seems a high price to pay */
    1461           0 :     pattrs.mask = wam_events|wam_nodecor|wam_positioned|wam_cursor|wam_transient|wam_verytransient;
    1462           0 :     pattrs.event_masks = -1;
    1463           0 :     pattrs.nodecoration = true;
    1464           0 :     pattrs.positioned = true;
    1465           0 :     pattrs.cursor = ct_pointer;
    1466           0 :     pattrs.transient = GWidgetGetTopWidget(owner);
    1467             : 
    1468           0 :     pos.x = pos.y = 0; pos.width = pos.height = 100;
    1469             : 
    1470           0 :     m->w = GDrawCreateTopWindow(disp,&pos,gmenu_eh,m,&pattrs);
    1471           0 :     m->gic = GDrawCreateInputContext(m->w,gic_root|gic_orlesser);
    1472           0 :     GDrawWindowFontMetrics(m->w,m->font,&m->as, &ds, &ld);
    1473           0 :     m->fh = m->as + ds + 1;               /* I need some extra space, else mneumonic underlines look bad */
    1474           0 :     lh = m->fh;
    1475             : 
    1476           0 :     GDrawSetFont(m->w,m->font);
    1477           0 :     m->hasticks = false;
    1478           0 :     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
    1479           0 :         if ( mi[i].ti.checkable )
    1480           0 :             m->hasticks = true;
    1481           0 :         temp = GTextInfoGetWidth(m->w,&mi[i].ti,m->font);
    1482           0 :         if (temp > max_iwidth)
    1483           0 :             max_iwidth = temp;
    1484             : 
    1485           0 :         uc_strcpy(buffer,"");
    1486           0 :         uint16 short_mask = 0;
    1487             :         /**
    1488             :          * Grab the hotkey if there is one. First we work out the
    1489             :          * menubar for this menu item, and then get the path from the
    1490             :          * menubar to the menuitem and pass that to the hotkey system
    1491             :          * to get the hotkey if there is one for that menu item.
    1492             :          *
    1493             :          * If we have a hotkey then set the width to the text porition
    1494             :          * first and then add the modifier icons if there are to be
    1495             :          * any for the key combination.
    1496             :          */
    1497             : //      TRACE("_gmenu_create() toplevel:%p owner:%p\n", toplevel, owner );
    1498             : 
    1499           0 :         Hotkey* hk = 0;
    1500           0 :         if( toplevel ) {
    1501           0 :             hk = hotkeyFindByMenuPath( toplevel->g.base,
    1502           0 :                                        GMenuGetMenuPath( toplevel->mi, &mi[i] ));
    1503             :         }
    1504           0 :         else if( owner && strlen(m->subMenuName) )
    1505             :         {
    1506           0 :             hk = hotkeyFindByMenuPathInSubMenu( owner, m->subMenuName,
    1507           0 :                                                 GMenuGetMenuPath( mi, &mi[i] ));
    1508             :         }
    1509             : 
    1510             : //      TRACE("hk:%p\n", hk);
    1511           0 :         if( hk )
    1512             :         {
    1513           0 :             short_mask = hk->state;
    1514           0 :             char* keydesc = hk->text;
    1515           0 :             if( mac_menu_icons )
    1516             :             {
    1517             : //              keydesc = hotkeyTextWithoutModifiers( keydesc );
    1518             :             }
    1519           0 :             uc_strcpy( buffer, keydesc );
    1520             : 
    1521           0 :             temp = GDrawGetTextWidth(m->w,buffer,-1);
    1522           0 :             if (temp > max_hkwidth)
    1523           0 :                 max_hkwidth = temp;
    1524             :             /* if( short_mask && mac_menu_icons ) { */
    1525             :             /*  temp += GMenuMacIconsWidth( m, short_mask ); */
    1526             :             /* } */
    1527             :         }
    1528             : 
    1529           0 :         temp = GTextInfoGetHeight(m->w,&mi[i].ti,m->font);
    1530           0 :         if ( temp>lh ) {
    1531           0 :             if ( temp>3*m->fh/2 )
    1532           0 :                 temp = 3*m->fh/2;
    1533           0 :             lh = temp;
    1534             :         }
    1535             :     }
    1536           0 :     m->fh = lh;
    1537           0 :     m->mcnt = m->lcnt = i;
    1538             : 
    1539             :     //Width: Max item length + max length of hotkey text + padding
    1540           0 :     width = max_iwidth + max_hkwidth + GDrawPointsToPixels(m->w, 10);
    1541           0 :     if ( m->hasticks ) {
    1542           0 :         int ticklen = m->as + GDrawPointsToPixels(m->w,5);
    1543           0 :         width += ticklen;
    1544           0 :         m->tioff += ticklen;
    1545             :     }
    1546           0 :     m->width = pos.width = width + 2*m->bp;
    1547           0 :     m->rightedge = m->width - m->bp;
    1548           0 :     m->height = pos.height = i*m->fh + 2*m->bp;
    1549           0 :     GDrawGetSize(GDrawGetRoot(disp),&screen);
    1550             : 
    1551           0 :     sbwidth = 0;
    1552             : /* On the mac, the menu bar takes up the top twenty pixels or so of screen */
    1553             : /*  so never put a menu that high */
    1554             : #define MAC_MENUBAR     20
    1555           0 :     if ( pos.height > screen.height-MAC_MENUBAR-m->fh ) {
    1556             :         GGadgetData gd;
    1557             : 
    1558           0 :         m->lcnt = (screen.height-MAC_MENUBAR-m->fh-2*m->bp)/m->fh;
    1559           0 :         m->height = pos.height = m->lcnt*m->fh + 2*m->bp;
    1560             : 
    1561             :         /* It's too long, so add a scrollbar */
    1562           0 :         sbwidth = GDrawPointsToPixels(owner,_GScrollBar_Width);
    1563           0 :         pos.width += sbwidth;
    1564           0 :         memset(&gd,'\0',sizeof(gd));
    1565           0 :         gd.pos.y = 0; gd.pos.height = pos.height;
    1566           0 :         gd.pos.width = sbwidth;
    1567           0 :         gd.pos.x = m->width;
    1568           0 :         gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert|gg_pos_use0;
    1569           0 :         gd.handle_controlevent = gmenu_scroll;
    1570           0 :         m->vsb = GScrollBarCreate(m->w,&gd,m);
    1571           0 :         GScrollBarSetBounds(m->vsb,0,m->mcnt,m->lcnt);
    1572             :     }
    1573             : 
    1574           0 :     pos.x = where->x; pos.y = where->y;
    1575           0 :     if ( pos.y + pos.height > screen.height-MAC_MENUBAR ) {
    1576           0 :         if ( where->y+aheight-pos.height >= MAC_MENUBAR )
    1577           0 :             pos.y = where->y+aheight-pos.height;
    1578             :         else {
    1579           0 :             pos.y = MAC_MENUBAR;
    1580             :             /* Ok, it's going to overlap the press point if we got here */
    1581             :             /*  let's see if we can shift it left/right a bit so it won't */
    1582           0 :             if ( awidth<0 )
    1583             :                 /* Oh, well, I guess it won't. It's a submenu and we've already */
    1584             :                 /*  moved off to the left */;
    1585           0 :             else if ( pos.x+awidth+pos.width+3<screen.width )
    1586           0 :                 pos.x += awidth+3;
    1587           0 :             else if ( pos.x-pos.width-3>=0 )
    1588           0 :                 pos.x -= pos.width+3;
    1589             :             else {
    1590             :                 /* There doesn't seem much we can do in this case */
    1591             :                 ;
    1592             :             }
    1593             :         }
    1594             :     }
    1595           0 :     if ( pos.x+pos.width > screen.width ) {
    1596           0 :         if ( where->x+awidth-pos.width >= 0 )
    1597           0 :             pos.x = where->x+awidth-pos.width-3;
    1598             :         else
    1599           0 :             pos.x = 0;
    1600             :     }
    1601           0 :     GDrawResize(m->w,pos.width,pos.height);
    1602           0 :     GDrawMove(m->w,pos.x,pos.y);
    1603             : 
    1604           0 :     GDrawSetVisible(m->w,true);
    1605           0 :     if ( menu_grabs )
    1606           0 :         GDrawPointerGrab(m->w);
    1607           0 : return( m );
    1608             : }
    1609             : 
    1610           0 : static GMenu *GMenuCreateSubMenu(GMenu *parent,GMenuItem *mi,int disable) {
    1611             :     GPoint p;
    1612             :     GMenu *m;
    1613           0 :     char *subMenuName = 0;
    1614             : 
    1615           0 :     p.x = parent->width;
    1616           0 :     p.y = (parent->line_with_mouse-parent->offtop)*parent->fh + parent->bp;
    1617           0 :     GDrawTranslateCoordinates(parent->w,GDrawGetRoot(GDrawGetDisplayOfWindow(parent->w)),&p);
    1618           0 :     m = _GMenu_Create(getTopLevelMenubar(parent),parent->owner,mi,&p,-parent->width,parent->fh,
    1619           0 :                       parent->font, disable, subMenuName );
    1620           0 :     m->parent = parent;
    1621           0 :     m->pressed = parent->pressed;
    1622           0 : return( m );
    1623             : }
    1624             : 
    1625           0 : static GMenu *GMenuCreatePulldownMenu(GMenuBar *mb,GMenuItem *mi,int disabled) {
    1626             :     GPoint p;
    1627             :     GMenu *m;
    1628           0 :     char *subMenuName = 0;
    1629             : 
    1630           0 :     p.x = mb->g.inner.x + mb->xs[mb->entry_with_mouse]-
    1631           0 :             GBoxDrawnWidth(mb->g.base,&menu_box );
    1632           0 :     p.y = mb->g.r.y + mb->g.r.height;
    1633           0 :     GDrawTranslateCoordinates(mb->g.base,GDrawGetRoot(GDrawGetDisplayOfWindow(mb->g.base)),&p);
    1634           0 :     m = _GMenu_Create( mb, mb->g.base, mi, &p,
    1635           0 :                        mb->xs[mb->entry_with_mouse+1]-mb->xs[mb->entry_with_mouse],
    1636           0 :                        -mb->g.r.height,
    1637           0 :                        mb->font, disabled, subMenuName );
    1638           0 :     m->menubar = mb;
    1639           0 :     m->pressed = mb->pressed;
    1640           0 :     _GWidget_SetPopupOwner((GGadget *) mb);
    1641           0 : return( m );
    1642             : }
    1643             : 
    1644           0 : GWindow _GMenuCreatePopupMenu( GWindow owner,GEvent *event, GMenuItem *mi,
    1645             :                                void (*donecallback)(GWindow) )
    1646             : {
    1647           0 :     char *subMenuName = 0;
    1648           0 :     return _GMenuCreatePopupMenuWithName( owner, event, mi, subMenuName, donecallback );
    1649             : }
    1650             : 
    1651             : 
    1652           0 : GWindow _GMenuCreatePopupMenuWithName( GWindow owner,GEvent *event, GMenuItem *mi,
    1653             :                                        char *subMenuName,
    1654             :                                        void (*donecallback)(GWindow) )
    1655             : {
    1656             :     GPoint p;
    1657             :     GMenu *m;
    1658             :     GEvent e;
    1659             : 
    1660           0 :     if ( !gmenubar_inited )
    1661           0 :         GMenuInit();
    1662             : 
    1663           0 :     p.x = event->u.mouse.x;
    1664           0 :     p.y = event->u.mouse.y;
    1665           0 :     GDrawTranslateCoordinates(owner,GDrawGetRoot(GDrawGetDisplayOfWindow(owner)),&p);
    1666           0 :     m = _GMenu_Create( 0, owner, GMenuItemArrayCopy(mi,NULL), &p,
    1667             :                        0, 0, menu_font,false, subMenuName );
    1668           0 :     m->any_unmasked_shortcuts = GMenuItemArrayAnyUnmasked(m->mi);
    1669           0 :     GDrawPointerUngrab(GDrawGetDisplayOfWindow(owner));
    1670           0 :     GDrawPointerGrab(m->w);
    1671           0 :     GDrawGetPointerPosition(m->w,&e);
    1672           0 :     if ( e.u.mouse.state & (ksm_button1|ksm_button2|ksm_button3) )
    1673           0 :         m->pressed = m->initial_press = true;
    1674           0 :     m->donecallback = donecallback;
    1675           0 :     m->freemi = true;
    1676           0 :     most_recent_popup_menu = m;
    1677           0 : return( m->w );
    1678             : }
    1679             : 
    1680           0 : GWindow GMenuCreatePopupMenu(GWindow owner,GEvent *event, GMenuItem *mi)
    1681             : {
    1682           0 :     char* subMenuName = 0;
    1683           0 :     return( _GMenuCreatePopupMenuWithName(owner,event,mi,subMenuName,NULL));
    1684             : }
    1685             : 
    1686           0 : GWindow GMenuCreatePopupMenuWithName(GWindow owner,GEvent *event,
    1687             :                                      char* subMenuName, GMenuItem *mi)
    1688             : {
    1689           0 :     return( _GMenuCreatePopupMenuWithName(owner,event,mi,subMenuName,NULL));
    1690             : }
    1691             : 
    1692             : 
    1693           0 : int GMenuPopupCheckKey(GEvent *event) {
    1694             : 
    1695           0 :     if ( most_recent_popup_menu==NULL ) return( false );
    1696             : 
    1697           0 :     return( gmenu_key(most_recent_popup_menu,event) );
    1698             : }
    1699             : 
    1700             : /* ************************************************************************** */
    1701             : 
    1702           0 : int GGadgetUndoMacEnglishOptionCombinations(GEvent *event) {
    1703           0 :     int keysym = event->u.chr.keysym;
    1704             : 
    1705           0 :     switch ( keysym ) {
    1706             :     /* translate special mac keys to ordinary keys */
    1707             :       case 0xba:
    1708           0 :         keysym = '0'; break;
    1709             :       case 0xa1:
    1710           0 :         keysym = '1'; break;
    1711             :       case 0x2122:
    1712           0 :         keysym = '2'; break;
    1713             :       case 0xa3:
    1714           0 :         keysym = '3'; break;
    1715             :       case 0xa2:
    1716           0 :         keysym = '4'; break;
    1717             :       case 0x221e:
    1718           0 :         keysym = '5'; break;
    1719             :       case 0xa7:
    1720           0 :         keysym = '6'; break;
    1721             :       case 0xb6:
    1722           0 :         keysym = '7'; break;
    1723             :       case 0x2022:
    1724           0 :         keysym = '8'; break;
    1725             :       case 0xaa:
    1726           0 :         keysym = '9'; break;
    1727             :       case 0xe5:
    1728           0 :         keysym = 'a'; break;
    1729             :       case 0x222b:
    1730           0 :         keysym = 'b'; break;
    1731             :       case 0xe7:
    1732           0 :         keysym = 'c'; break;
    1733             :       case 0x2202:
    1734           0 :         keysym = 'd'; break;
    1735             :       /* e is a modifier */
    1736             :       case 0x192:
    1737           0 :         keysym = 'f'; break;
    1738             :       case 0xa9:
    1739           0 :         keysym = 'g'; break;
    1740             :       case 0x2d9:
    1741           0 :         keysym = 'h'; break;
    1742             :       /* i is a modifier */
    1743             :       case 0x2206:
    1744           0 :         keysym = 'j'; break;
    1745             :       case 0x2da:
    1746           0 :         keysym = 'k'; break;
    1747             :       case 0xac:
    1748           0 :         keysym = 'l'; break;
    1749             :       case 0xb5:
    1750           0 :         keysym = 'm'; break;
    1751             :       /* n is a modifier */
    1752             :       case 0xf8:
    1753           0 :         keysym = 'o'; break;
    1754             :       case 0x3c0:
    1755           0 :         keysym = 'p'; break;
    1756             :       case 0x153:
    1757           0 :         keysym = 'q'; break;
    1758             :       case 0xae:
    1759           0 :         keysym = 'r'; break;
    1760             :       case 0x2020:
    1761           0 :         keysym = 's'; break;
    1762             :       case 0xee:
    1763           0 :         keysym = 't'; break;
    1764             :       /* u is a modifier */
    1765             :       case 0x221a:
    1766           0 :         keysym = 'v'; break;
    1767             :       case 0x2211:
    1768           0 :         keysym = 'w'; break;
    1769             :       case 0x2248:
    1770           0 :         keysym = 'x'; break;
    1771             :       case 0xa5:
    1772           0 :         keysym = 'y'; break;
    1773             :       case 0x3a9:
    1774           0 :         keysym = 'z'; break;
    1775             :     }
    1776           0 :     return( keysym );
    1777             : }
    1778             : 
    1779             : /**
    1780             :  * On OSX the XEvents have some extra translation performed to try to be handier.
    1781             :  * For example, in xev you might notice that alt+- gives a keysym of endash.
    1782             :  *
    1783             :  * Under Linux this translation doesn't happen and you get the alt
    1784             :  * modifier and the minus keysym. The hotkey code is expecting
    1785             :  * modifier(s) + base keysym not what osx gives (modifier(s) +
    1786             :  * alternate-keysym). So this little function is designed to convert
    1787             :  * the osx "enhanced" keysym back to their basic keysym.
    1788             :  */
    1789             : #ifdef __Mac
    1790             : static int osx_handle_keysyms( int st, int k )
    1791             : {
    1792             : //    TRACE("osx_handle_keysyms() st:%d k:%d\n", st, k );
    1793             : 
    1794             :     if( (st & ksm_control) && (st & ksm_meta) )
    1795             :         switch( k )
    1796             :         {
    1797             :         case 8211:  return 45; // Command + Alt + -
    1798             :         case 8804:  return 44; // Command + Alt + ,
    1799             :         }
    1800             : 
    1801             :     if( (st & ksm_control) && (st & ksm_meta) && (st & ksm_shift) )
    1802             :         switch( k )
    1803             :         {
    1804             :         case 177:   return 43; // Command + Alt + Shift + =
    1805             :         case 197:   return 65; // Command + Alt + Shift + A
    1806             :         case 305:   return 66; // Command + Alt + Shift + B
    1807             :         case 199:   return 67; // Command + Alt + Shift + C
    1808             :         case 206:   return 68; // Command + Alt + Shift + D
    1809             :         case 180:   return 69; // Command + Alt + Shift + E
    1810             :         case 207:   return 70; // Command + Alt + Shift + F
    1811             :         case 733:   return 71; // Command + Alt + Shift + G
    1812             :         case 211:   return 72; // Command + Alt + Shift + H
    1813             :         case 710:   return 73; // Command + Alt + Shift + I
    1814             :         case 212:   return 74; // Command + Alt + Shift + J
    1815             :         case 63743: return 75; // Command + Alt + Shift + K
    1816             :         case 210:   return 76; // Command + Alt + Shift + L
    1817             :         case 194:   return 77; // Command + Alt + Shift + M
    1818             :         case 732:   return 78; // Command + Alt + Shift + N
    1819             :         case 216:   return 79; // Command + Alt + Shift + O
    1820             :         case 8719:  return 80; // Command + Alt + Shift + P
    1821             :         case 65505: return 81; // Command + Alt + Shift + Q
    1822             :         case 8240:  return 82; // Command + Alt + Shift + R
    1823             :         case 205:   return 83; // Command + Alt + Shift + S
    1824             :         case 711:   return 84; // Command + Alt + Shift + T
    1825             :         case 168:   return 85; // Command + Alt + Shift + U
    1826             :         case 9674:  return 86; // Command + Alt + Shift + V
    1827             :         case 8222:  return 87; // Command + Alt + Shift + W
    1828             :         case 731:   return 88; // Command + Alt + Shift + X
    1829             :         case 193:   return 89; // Command + Alt + Shift + Y
    1830             :         case 184:   return 90; // Command + Alt + Shift + Z
    1831             : 
    1832             :         case 8260:  return 33; // Command + Alt + Shift + 1
    1833             :         case 8360:  return 64; // Command + Alt + Shift + 2
    1834             :         case 8249:  return 35; // Command + Alt + Shift + 3
    1835             :         case 8250:  return 36; // Command + Alt + Shift + 4
    1836             :         case 64257: return 37; // Command + Alt + Shift + 5
    1837             :         case 64258: return 94; // Command + Alt + Shift + 6
    1838             :         case 8225:  return 38; // Command + Alt + Shift + 7
    1839             :         case 176:   return 42; // Command + Alt + Shift + 8
    1840             :         case 183:   return 40; // Command + Alt + Shift + 9
    1841             :         case 8218:  return 41; // Command + Alt + Shift + 0
    1842             :         }
    1843             : 
    1844             :     if( st & ksm_meta )
    1845             :         switch( k )
    1846             :         {
    1847             :         case 2730:  return 45; // Alt + -
    1848             :         case 2237:  return 61; // Alt + = (can avoid shift on this one for simpler up/down)
    1849             :         case 8800:  return 61; // Alt + = (can avoid shift on this one for simpler up/down)
    1850             :         }
    1851             : 
    1852             :     return k;
    1853             : }
    1854             : #endif
    1855             : 
    1856             : int osx_fontview_copy_cut_counter = 0;
    1857             : 
    1858             : 
    1859           0 : static int GMenuBarCheckHotkey(GWindow top, GGadget *g, GEvent *event) {
    1860             : //    TRACE("GMenuBarCheckKey(top) keysym:%d upper:%d lower:%d\n",keysym,toupper(keysym),tolower(keysym));
    1861             :     // see if we should skip processing (e.g. no modifier key pressed)
    1862           0 :     GMenuBar *mb = (GMenuBar *) g;
    1863           0 :         GWindow w = GGadgetGetWindow(g);
    1864           0 :         GGadget* focus = GWindowGetFocusGadgetOfWindow(w);
    1865           0 :         if (GGadgetGetSkipHotkeyProcessing(focus))
    1866           0 :             return 0;
    1867             : 
    1868             : //    TRACE("GMenuBarCheckKey(2) keysym:%d upper:%d lower:%d\n",keysym,toupper(keysym),tolower(keysym));
    1869             : 
    1870             :     /* then look for hotkeys everywhere */
    1871             : 
    1872           0 :     if( hotkeySystemGetCanUseMacCommand() )
    1873             :     {
    1874             :         // If Mac command key was pressed, swap with the control key.
    1875           0 :         if ((event->u.chr.state & (ksm_cmdmacosx|ksm_control)) == ksm_cmdmacosx) {
    1876           0 :             event->u.chr.state ^= (ksm_cmdmacosx|ksm_control);
    1877             :         }
    1878             :     }
    1879             : #ifdef __Mac
    1880             : 
    1881             :     //
    1882             :     // Command + Alt + Shift + F on OSX doesn't give the keysym one
    1883             :     // might expect if they have been testing on a Linux Machine.
    1884             :     //
    1885             :     TRACE("looking2 for hotkey in new system...state:%d keysym:%d\n", event->u.chr.state, event->u.chr.keysym );
    1886             :     TRACE("     has ksm_control:%d\n", (event->u.chr.state & ksm_control ));
    1887             :     TRACE("     has ksm_cmdmacosx:%d\n", (event->u.chr.state & ksm_cmdmacosx ));
    1888             :     TRACE("     has ksm_cmdmacosx|control:%d\n", ((event->u.chr.state & (ksm_cmdmacosx|ksm_control)) == ksm_cmdmacosx) );
    1889             :     TRACE("     has ksm_meta:%d\n",    (event->u.chr.state & ksm_meta ));
    1890             :     TRACE("     has ksm_shift:%d\n",   (event->u.chr.state & ksm_shift ));
    1891             :     TRACE("     has ksm_option:%d\n",   (event->u.chr.state & ksm_option ));
    1892             : 
    1893             :     if( event->u.chr.state & ksm_option )
    1894             :         event->u.chr.state ^= ksm_option;
    1895             : 
    1896             :     event->u.chr.keysym = osx_handle_keysyms( event->u.chr.state, event->u.chr.keysym );
    1897             :     TRACE(" 3   has ksm_option:%d\n",   (event->u.chr.state & ksm_option ));
    1898             : 
    1899             :     // Command-c or Command-x
    1900             :     if( event->u.chr.state == ksm_control
    1901             :         && (event->u.chr.keysym == 99 || event->u.chr.keysym == 120 ))
    1902             :     {
    1903             :         osx_fontview_copy_cut_counter++;
    1904             :     }
    1905             : #endif
    1906             : 
    1907             : //    TRACE("about to look for hotkey in new system...state:%d keysym:%d\n", event->u.chr.state, event->u.chr.keysym );
    1908             : //    TRACE("     has ksm_control:%d\n", (event->u.chr.state & ksm_control ));
    1909             : //    TRACE("     has ksm_meta:%d\n",    (event->u.chr.state & ksm_meta ));
    1910             : //    TRACE("     has ksm_shift:%d\n",   (event->u.chr.state & ksm_shift ));
    1911             : 
    1912           0 :     event->u.chr.state |= ksm_numlock;
    1913             : //    TRACE("about2 to look for hotkey in new system...state:%d keysym:%d\n", event->u.chr.state, event->u.chr.keysym );
    1914             : 
    1915             :     /**
    1916             :      * Mask off the parts we don't explicitly care about
    1917             :      */
    1918           0 :     event->u.chr.state &= ( ksm_control | ksm_meta | ksm_shift | ksm_option );
    1919             : 
    1920             : //    TRACE("about3 to look for hotkey in new system...state:%d keysym:%d\n", event->u.chr.state, event->u.chr.keysym );
    1921             : //    TRACE("     has ksm_control:%d\n", (event->u.chr.state & ksm_control ));
    1922             : //    TRACE("     has ksm_meta:%d\n",    (event->u.chr.state & ksm_meta ));
    1923             : //    TRACE("     has ksm_shift:%d\n",   (event->u.chr.state & ksm_shift ));
    1924             : 
    1925           0 :     if( GGadgetGetSkipUnQualifiedHotkeyProcessing(focus) && !event->u.chr.state )
    1926             :     {
    1927             :         TRACE("skipping unqualified hotkey for widget g:%p\n", g);
    1928           0 :         return 0;
    1929             :     }
    1930             : 
    1931             : 
    1932           0 :     struct dlistnodeExternal* node= hotkeyFindAllByEvent( top, event );
    1933           0 :     struct dlistnode* hklist = (struct dlistnode*)node;
    1934           0 :     for( ; node; node=(struct dlistnodeExternal*)(node->next) ) {
    1935           0 :         Hotkey* hk = (Hotkey*)node->ptr;
    1936             : //      TRACE("hotkey found by event! hk:%p\n", hk );
    1937             : //      TRACE("hotkey found by event! action:%s\n", hk->action );
    1938             : 
    1939           0 :         int skipkey = false;
    1940             : 
    1941           0 :         if( cv_auto_goto )
    1942             :         {
    1943           0 :             if( !hk->state )
    1944           0 :                 skipkey = true;
    1945             : //              TRACE("hotkey state:%d skip:%d\n", hk->state, skipkey );
    1946             :         }
    1947             : 
    1948           0 :         if( !skipkey )
    1949             :         {
    1950           0 :             GMenuItem *mi = GMenuSearchAction(mb->g.base,mb->mi,hk->action,event,mb->child==NULL);
    1951           0 :             if ( mi )
    1952             :             {
    1953             : //              TRACE("GMenuBarCheckKey(x) have mi... :%p\n", mi );
    1954             : //              TRACE("GMenuBarCheckKey(x) have mitext:%s\n", u_to_c(mi->ti.text) );
    1955           0 :                 if ( mi->ti.checkable && !mi->ti.disabled )
    1956           0 :                     mi->ti.checked = !mi->ti.checked;
    1957           0 :                 if ( mi->invoke!=NULL && !mi->ti.disabled )
    1958           0 :                     (mi->invoke)(mb->g.base,mi,NULL);
    1959           0 :                 if ( mb->child != NULL )
    1960           0 :                     GMenuDestroy(mb->child);
    1961           0 :                 return( true );
    1962             :             }
    1963             :             else
    1964             :             {
    1965             : //                  TRACE("hotkey found for event must be a non menu action... action:%s\n", hk->action );
    1966             : 
    1967             :             }
    1968             :         }
    1969             : 
    1970             : //          TRACE("END hotkey found by event! hk:%p\n", hk );
    1971             :     }
    1972           0 :     dlist_free_external(&hklist);
    1973             : 
    1974             : //    TRACE("menubarcheckkey(e1)\n");
    1975           0 :     return false;
    1976             : }
    1977             : 
    1978             : 
    1979           0 : int GMenuBarCheckKey(GWindow top, GGadget *g, GEvent *event) {
    1980             :     int i;
    1981           0 :     GMenuBar *mb = (GMenuBar *) g;
    1982           0 :     unichar_t keysym = event->u.chr.keysym;
    1983             : 
    1984           0 :     if ( g==NULL || keysym==0 ) return( false ); /* exit if no gadget or key */
    1985             : 
    1986           0 :     if ( (menumask&ksm_cmdmacosx) && keysym>0x7f &&
    1987           0 :             (event->u.chr.state&ksm_meta) &&
    1988           0 :             !(event->u.chr.state&menumask&(ksm_control|ksm_cmdmacosx)) )
    1989           0 :         keysym = GGadgetUndoMacEnglishOptionCombinations(event);
    1990             : 
    1991           0 :     if ( keysym<GK_Special && islower(keysym))
    1992           0 :         keysym = toupper(keysym);
    1993           0 :     if ( event->u.chr.state&ksm_meta && !(event->u.chr.state&(menumask&~(ksm_meta|ksm_shift)))) {
    1994             :         /* Only look for mneumonics in the leaf of the displayed menu structure */
    1995           0 :         if ( mb->child!=NULL )
    1996           0 :             return( gmenu_key(mb->child,event)); /* this routine will do shortcuts too */
    1997             : 
    1998           0 :         for ( i=0; i<mb->mtot; ++i ) {
    1999           0 :             if ( mb->mi[i].ti.mnemonic == keysym && !mb->mi[i].ti.disabled ) {
    2000           0 :                 GMenuBarKeyInvoke(mb,i);
    2001           0 :                 return( true );
    2002             :             }
    2003             :         }
    2004             :     }
    2005             : 
    2006             :     // See if it matches a hotkey
    2007           0 :     if (GMenuBarCheckHotkey(top, g, event)) {
    2008           0 :         return true;
    2009             :     }
    2010             : 
    2011           0 :     if ( mb->child )
    2012             :     {
    2013             :         GMenu *m;
    2014           0 :         for ( m=mb->child; m->child!=NULL; m = m->child )
    2015             :         {
    2016             :         }
    2017           0 :         return( GMenuSpecialKeys(m,event->u.chr.keysym,event));
    2018             :     }
    2019             : 
    2020             : //    TRACE("menubarcheckkey(e2)\n");
    2021           0 :     if ( event->u.chr.keysym==GK_Menu )
    2022           0 :         GMenuCreatePopupMenu(event->w,event, mb->mi);
    2023             : 
    2024           0 :     return( false );
    2025             : }
    2026             : 
    2027           0 : static void GMenuBarDrawDownArrow(GWindow pixmap, GMenuBar *mb, int x) {
    2028           0 :     int pt = GDrawPointsToPixels(pixmap,1);
    2029           0 :     int size = 2*(mb->g.inner.height/3);
    2030           0 :     int ybase = mb->g.inner.y + size + (mb->g.inner.height-size)/2;
    2031             :     GPoint p[3];
    2032             : 
    2033           0 :     p[0].x = x+size;            p[0].y = ybase;
    2034           0 :     p[1].x = x;                 p[1].y = ybase - size;
    2035           0 :     p[2].x = x+2*size;          p[2].y = ybase - size;
    2036             : 
    2037           0 :     GDrawSetLineWidth(pixmap,pt);
    2038             : 
    2039             :     // If rendering menus in standard (3-dimensional) look, use the shadow colors for fake relief.
    2040             :     // Otherwise, use foreground colors.
    2041           0 :     if (menu_3d_look) {
    2042           0 :         GDrawDrawLine(pixmap,p[0].x,p[0].y,p[1].x,p[1].y,mb->g.box->border_darker);
    2043           0 :         GDrawDrawLine(pixmap,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,mb->g.box->border_darker);
    2044           0 :         GDrawDrawLine(pixmap,p[1].x,p[1].y,p[2].x,p[2].y,mb->g.box->border_brightest);
    2045           0 :         GDrawDrawLine(pixmap,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,mb->g.box->border_brightest);
    2046           0 :         GDrawDrawLine(pixmap,p[2].x,p[2].y,p[0].x,p[0].y,mb->g.box->border_darkest);
    2047           0 :         GDrawDrawLine(pixmap,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,mb->g.box->border_darkest);
    2048             :     } else {
    2049           0 :         GDrawDrawLine(pixmap,p[0].x,p[0].y,p[1].x,p[1].y,mb->g.box->main_foreground);
    2050           0 :         GDrawDrawLine(pixmap,p[0].x,p[0].y+pt,p[1].x+pt,p[1].y,mb->g.box->main_foreground);
    2051           0 :         GDrawDrawLine(pixmap,p[1].x,p[1].y,p[2].x,p[2].y,mb->g.box->main_foreground);
    2052           0 :         GDrawDrawLine(pixmap,p[1].x+pt,p[1].y,p[2].x-pt,p[2].y,mb->g.box->main_foreground);
    2053           0 :         GDrawDrawLine(pixmap,p[2].x,p[2].y,p[0].x,p[0].y,mb->g.box->main_foreground);
    2054           0 :         GDrawDrawLine(pixmap,p[2].x-pt,p[2].y,p[0].x,p[0].y+pt,mb->g.box->main_foreground);
    2055             :     }
    2056           0 : }
    2057             : 
    2058           0 : static int gmenubar_expose(GWindow pixmap, GGadget *g, GEvent *expose) {
    2059           0 :     GMenuBar *mb = (GMenuBar *) g;
    2060             :     GRect r,old1,old2, old3;
    2061           0 :     Color fg = g->state==gs_disabled?g->box->disabled_foreground:
    2062           0 :                     g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
    2063           0 :                     g->box->main_foreground;
    2064             :     int i;
    2065             : 
    2066           0 :     if ( fg==COLOR_DEFAULT )
    2067           0 :         fg = GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(mb->g.base));
    2068             : 
    2069           0 :     GDrawPushClip(pixmap,&g->r,&old1);
    2070             : 
    2071           0 :     GBoxDrawBackground(pixmap,&g->r,g->box, g->state,false);
    2072           0 :     GBoxDrawBorder(pixmap,&g->r,g->box,g->state,false);
    2073           0 :     GDrawPushClip(pixmap,&g->inner,&old2);
    2074           0 :     GDrawSetFont(pixmap,mb->font);
    2075             : 
    2076           0 :     r = g->inner;
    2077           0 :     for ( i=0; i<mb->lastmi; ++i ) {
    2078           0 :         r.x = mb->xs[i]+mb->g.inner.x; r.width = mb->xs[i+1]-mb->xs[i];
    2079           0 :         GDrawPushClip(pixmap,&r,&old3);
    2080           0 :         GTextInfoDraw(pixmap,r.x,r.y,&mb->mi[i].ti,mb->font,
    2081           0 :                 mb->mi[i].ti.disabled?mb->g.box->disabled_foreground:fg,
    2082           0 :                 mb->g.box->active_border,r.y+r.height);
    2083           0 :         GDrawPopClip(pixmap,&old3);
    2084             :     }
    2085           0 :     if ( i<mb->mtot ) {
    2086           0 :         GMenuBarDrawDownArrow(pixmap,mb,mb->xs[i]+mb->g.inner.x);
    2087             :     }
    2088             : 
    2089           0 :     GDrawPopClip(pixmap,&old2);
    2090           0 :     GDrawPopClip(pixmap,&old1);
    2091           0 : return( true );
    2092             : }
    2093             : 
    2094           0 : static int GMenuBarIndex(GMenuBar *mb, int x ) {
    2095             :     int i;
    2096             : 
    2097           0 :     if ( x<0 )
    2098           0 : return( -1 );
    2099           0 :     for ( i=0; i< mb->lastmi; ++i )
    2100           0 :         if ( x<mb->g.inner.x+mb->xs[i+1] )
    2101           0 : return( i );
    2102           0 :     if ( mb->lastmi!=mb->mtot )
    2103           0 : return( mb->lastmi );
    2104             : 
    2105           0 : return( -1 );
    2106             : }
    2107             : 
    2108           0 : static int gmenubar_mouse(GGadget *g, GEvent *event) {
    2109           0 :     GMenuBar *mb = (GMenuBar *) g;
    2110             :     int which;
    2111             : 
    2112           0 :     if ( mb->child!=NULL && mb->child->hidden )
    2113           0 : return( true );
    2114             : 
    2115           0 :     if ( event->type == et_mousedown ) {
    2116           0 :         mb->pressed = true;
    2117           0 :         if ( mb->child!=NULL )
    2118           0 :             GMenuSetPressed(mb->child,true);
    2119           0 :         which = GMenuBarIndex(mb,event->u.mouse.x);
    2120           0 :         if ( which==mb->entry_with_mouse && mb->child!=NULL )
    2121           0 :             GMenuDestroy(mb->child);
    2122             :         else {
    2123           0 :             mb->initial_press = true;
    2124           0 :             GMenuBarChangeSelection(mb,which,event);
    2125             :         }
    2126           0 :     } else if ( event->type == et_mousemove && mb->pressed ) {
    2127           0 :         if ( GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))
    2128           0 :             GMenuBarChangeSelection(mb,GMenuBarIndex(mb,event->u.mouse.x),event);
    2129           0 :         else if ( mb->child!=NULL ) {
    2130             :             GPoint p;
    2131             : 
    2132           0 :             p.x = event->u.mouse.x; p.y = event->u.mouse.y;
    2133           0 :             GDrawTranslateCoordinates(mb->g.base,mb->child->w,&p);
    2134           0 :             if ( p.x>=0 && p.y>=0 && p.x<mb->child->width && p.y<mb->child->height ) {
    2135           0 :                 GDrawPointerUngrab(GDrawGetDisplayOfWindow(mb->g.base));
    2136           0 :                 GDrawPointerGrab(mb->child->w);
    2137           0 :                 event->u.mouse.x = p.x; event->u.mouse.y = p.y;
    2138           0 :                 event->w = mb->child->w;
    2139           0 :                 gmenu_mouse(mb->child,event);
    2140             :             }
    2141             :         }
    2142           0 :     } else if ( event->type == et_mouseup &&
    2143           0 :             (!mb->initial_press ||
    2144           0 :               !GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))) {
    2145           0 :         GMenuBarChangeSelection(mb,-1,event);
    2146           0 :         mb->pressed = false;
    2147           0 :     } else if ( event->type == et_mouseup ) {
    2148           0 :         mb->initial_press = mb->pressed = false;
    2149           0 :         if ( mb->child!=NULL )
    2150           0 :             GMenuSetPressed(mb->child,false);
    2151             :     }
    2152           0 : return( true );
    2153             : }
    2154             : 
    2155           0 : static void gmenubar_destroy(GGadget *g) {
    2156           0 :     GMenuBar *mb = (GMenuBar *) g;
    2157           0 :     if ( g==NULL )
    2158           0 : return;
    2159           0 :     if ( mb->child!=NULL ) {
    2160           0 :         GMenuDestroy(mb->child);
    2161           0 :         GDrawSync(NULL);
    2162           0 :         GDrawProcessPendingEvents(NULL);        /* popup's destroy routine must execute before we die */
    2163             :     }
    2164           0 :     GMenuItemArrayFree(mb->mi);
    2165           0 :     free(mb->xs);
    2166           0 :     _ggadget_destroy(g);
    2167             : }
    2168             : 
    2169           0 : static void GMenuBarSetFont(GGadget *g,FontInstance *new) {
    2170           0 :     GMenuBar *b = (GMenuBar *) g;
    2171           0 :     b->font = new;
    2172           0 : }
    2173             : 
    2174           0 : static FontInstance *GMenuBarGetFont(GGadget *g) {
    2175           0 :     GMenuBar *b = (GMenuBar *) g;
    2176           0 : return( b->font );
    2177             : }
    2178             : 
    2179           0 : static void GMenuBarTestSize(GMenuBar *mb) {
    2180           0 :     int arrow_size = mb->g.inner.height;
    2181             :     int i;
    2182             : 
    2183           0 :     if ( mb->xs[mb->mtot]<=mb->g.inner.width+4 ) {
    2184           0 :         mb->lastmi = mb->mtot;
    2185             :     } else {
    2186           0 :         for ( i=mb->mtot-1; i>0 && mb->xs[i]>mb->g.inner.width-arrow_size; --i );
    2187           0 :         mb->lastmi = i;
    2188           0 :         memset(&mb->fake,0,sizeof(GMenuItem));
    2189           0 :         mb->fake[0].sub = mb->mi+mb->lastmi;
    2190             :     }
    2191           0 : }
    2192             : 
    2193           0 : static void GMenuBarResize(GGadget *g, int32 width, int32 height) {
    2194           0 :     _ggadget_resize(g,width,height);
    2195           0 :     GMenuBarTestSize((GMenuBar *) g);
    2196           0 : }
    2197             : 
    2198             : struct gfuncs gmenubar_funcs = {
    2199             :     0,
    2200             :     sizeof(struct gfuncs),
    2201             : 
    2202             :     gmenubar_expose,
    2203             :     gmenubar_mouse,
    2204             :     NULL,
    2205             :     NULL,
    2206             :     NULL,
    2207             :     NULL,
    2208             :     NULL,
    2209             : 
    2210             :     _ggadget_redraw,
    2211             :     _ggadget_move,
    2212             :     GMenuBarResize,
    2213             :     _ggadget_setvisible,
    2214             :     _ggadget_setenabled,
    2215             :     _ggadget_getsize,
    2216             :     _ggadget_getinnersize,
    2217             : 
    2218             :     gmenubar_destroy,
    2219             : 
    2220             :     NULL,
    2221             :     NULL,
    2222             :     NULL,
    2223             :     NULL,
    2224             :     NULL,
    2225             :     GMenuBarSetFont,
    2226             :     GMenuBarGetFont,
    2227             : 
    2228             :     NULL,
    2229             :     NULL,
    2230             :     NULL,
    2231             :     NULL,
    2232             :     NULL,
    2233             :     NULL,
    2234             :     NULL,
    2235             :     NULL,
    2236             :     NULL,
    2237             :     NULL,
    2238             :     NULL,
    2239             : 
    2240             :     NULL,
    2241             :     NULL,
    2242             :     NULL,
    2243             :     NULL
    2244             : };
    2245             : 
    2246           0 : static void GMenuBarFit(GMenuBar *mb,GGadgetData *gd) {
    2247           0 :     int bp = GBoxBorderWidth(mb->g.base,mb->g.box );
    2248             :     GRect r;
    2249             : 
    2250           0 :     if ( gd->pos.x <= 0 )
    2251           0 :         mb->g.r.x = 0;
    2252           0 :     if ( gd->pos.y <= 0 )
    2253           0 :         mb->g.r.y = 0;
    2254           0 :     if ( mb->g.r.width == 0 ) {
    2255           0 :         GDrawGetSize(mb->g.base,&r);
    2256           0 :         mb->g.r.width = r.width-mb->g.r.x;
    2257             :     }
    2258           0 :     if ( mb->g.r.height == 0 ) {
    2259             :         int as,ds,ld;
    2260           0 :         GDrawWindowFontMetrics(mb->g.base,mb->font,&as, &ds, &ld);
    2261           0 :         mb->g.r.height = as+ds+2*bp;
    2262             :     }
    2263           0 :     mb->g.inner.x = mb->g.r.x + bp;
    2264           0 :     mb->g.inner.y = mb->g.r.y + bp;
    2265           0 :     mb->g.inner.width = mb->g.r.width - 2*bp;
    2266           0 :     mb->g.inner.height = mb->g.r.height - 2*bp;
    2267           0 : }
    2268             : 
    2269           0 : static void GMenuBarFindXs(GMenuBar *mb) {
    2270             :     int i, wid;
    2271             : 
    2272           0 :     GDrawSetFont(mb->g.base,mb->font);
    2273           0 :     wid = GDrawPointsToPixels(mb->g.base,8);
    2274           0 :     mb->xs[0] = GDrawPointsToPixels(mb->g.base,2);
    2275           0 :     for ( i=0; i<mb->mtot; ++i )
    2276           0 :         mb->xs[i+1] = mb->xs[i]+wid+GTextInfoGetWidth(mb->g.base,&mb->mi[i].ti,NULL);
    2277           0 :     GMenuBarTestSize(mb);
    2278           0 : }
    2279             : 
    2280           0 : static void MenuMaskInit(GMenuItem *mi) {
    2281           0 :     int mask = GMenuItemArrayMask(mi);
    2282           0 :     if ( mask_set )
    2283           0 :         menumask |= mask;
    2284             :     else {
    2285           0 :         menumask = mask;
    2286           0 :         mask_set = true;
    2287             :     }
    2288           0 : }
    2289             : 
    2290           0 : GGadget *GMenuBarCreate(struct gwindow *base, GGadgetData *gd,void *data) {
    2291           0 :     GMenuBar *mb = calloc(1,sizeof(GMenuBar));
    2292             : 
    2293           0 :     if ( !gmenubar_inited )
    2294           0 :         GMenuInit();
    2295           0 :     mb->g.funcs = &gmenubar_funcs;
    2296           0 :     _GGadget_Create(&mb->g,base,gd,data,&menubar_box);
    2297             : 
    2298           0 :     mb->mi = GMenuItemArrayCopy(gd->u.menu,&mb->mtot);
    2299           0 :     mb->xs = malloc((mb->mtot+1)*sizeof(uint16));
    2300           0 :     mb->entry_with_mouse = -1;
    2301           0 :     mb->font = menubar_font;
    2302             : 
    2303           0 :     GMenuBarFit(mb,gd);
    2304           0 :     GMenuBarFindXs(mb);
    2305             : 
    2306           0 :     MenuMaskInit(mb->mi);
    2307           0 :     mb->any_unmasked_shortcuts = GMenuItemArrayAnyUnmasked(mb->mi);
    2308             : 
    2309           0 :     if ( gd->flags & gg_group_end )
    2310           0 :         _GGadgetCloseGroup(&mb->g);
    2311           0 :     _GWidget_SetMenuBar(&mb->g);
    2312             : 
    2313           0 :     mb->g.takes_input = true;
    2314           0 : return( &mb->g );
    2315             : }
    2316             : 
    2317           0 : GGadget *GMenu2BarCreate(struct gwindow *base, GGadgetData *gd,void *data) {
    2318           0 :     GMenuBar *mb = calloc(1,sizeof(GMenuBar));
    2319             : 
    2320           0 :     if ( !gmenubar_inited )
    2321           0 :         GMenuInit();
    2322           0 :     mb->g.funcs = &gmenubar_funcs;
    2323           0 :     _GGadget_Create(&mb->g,base,gd,data,&menubar_box);
    2324             : 
    2325           0 :     mb->mi = GMenuItem2ArrayCopy(gd->u.menu2,&mb->mtot);
    2326           0 :     mb->xs = malloc((mb->mtot+1)*sizeof(uint16));
    2327           0 :     mb->entry_with_mouse = -1;
    2328           0 :     mb->font = menubar_font;
    2329             : 
    2330           0 :     GMenuBarFit(mb,gd);
    2331           0 :     GMenuBarFindXs(mb);
    2332             : 
    2333           0 :     MenuMaskInit(mb->mi);
    2334           0 :     mb->any_unmasked_shortcuts = GMenuItemArrayAnyUnmasked(mb->mi);
    2335             : 
    2336           0 :     if ( gd->flags & gg_group_end )
    2337           0 :         _GGadgetCloseGroup(&mb->g);
    2338           0 :     _GWidget_SetMenuBar(&mb->g);
    2339             : 
    2340           0 :     mb->g.takes_input = true;
    2341           0 : return( &mb->g );
    2342             : }
    2343             : 
    2344             : /* ************************************************************************** */
    2345           0 : static GMenuItem *GMenuBarFindMid(GMenuItem *mi, int mid) {
    2346             :     int i;
    2347             :     GMenuItem *ret;
    2348             : 
    2349           0 :     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.image!=NULL || mi[i].ti.line; ++i ) {
    2350           0 :         if ( mi[i].mid == mid )
    2351           0 : return( &mi[i]);
    2352           0 :         if ( mi[i].sub!=NULL ) {
    2353           0 :             ret = GMenuBarFindMid( mi[i].sub, mid );
    2354           0 :             if ( ret!=NULL )
    2355           0 : return( ret );
    2356             :         }
    2357             :     }
    2358           0 : return( NULL );
    2359             : }
    2360             : 
    2361           0 : void GMenuBarSetItemChecked(GGadget *g, int mid, int check) {
    2362           0 :     GMenuBar *mb = (GMenuBar *) g;
    2363             :     GMenuItem *item;
    2364             : 
    2365           0 :     item = GMenuBarFindMid(mb->mi,mid);
    2366           0 :     if ( item!=NULL )
    2367           0 :         item->ti.checked = check;
    2368           0 : }
    2369             : 
    2370           0 : void GMenuBarSetItemEnabled(GGadget *g, int mid, int enabled) {
    2371           0 :     GMenuBar *mb = (GMenuBar *) g;
    2372             :     GMenuItem *item;
    2373             : 
    2374           0 :     item = GMenuBarFindMid(mb->mi,mid);
    2375           0 :     if ( item!=NULL )
    2376           0 :         item->ti.disabled = !enabled;
    2377           0 : }
    2378             : 
    2379           0 : void GMenuBarSetItemName(GGadget *g, int mid, const unichar_t *name) {
    2380           0 :     GMenuBar *mb = (GMenuBar *) g;
    2381             :     GMenuItem *item;
    2382             : 
    2383           0 :     item = GMenuBarFindMid(mb->mi,mid);
    2384           0 :     if ( item!=NULL ) {
    2385           0 :         free( item->ti.text );
    2386           0 :         item->ti.text = u_copy(name);
    2387             :     }
    2388           0 : }
    2389             : 
    2390             : /* Check to see if event matches the given shortcut, expressed in our standard*/
    2391             : /*  syntax and subject to gettext translation */
    2392           0 : int GMenuIsCommand(GEvent *event,char *shortcut) {
    2393             :     GMenuItem foo;
    2394           0 :     unichar_t keysym = event->u.chr.keysym;
    2395             : 
    2396           0 :     if ( event->type!=et_char )
    2397           0 : return( false );
    2398             : 
    2399           0 :     if ( keysym<GK_Special && islower(keysym))
    2400           0 :         keysym = toupper(keysym);
    2401             : 
    2402           0 :     memset(&foo,0,sizeof(foo));
    2403             : 
    2404           0 :     GMenuItemParseShortCut(&foo,shortcut);
    2405             : 
    2406           0 : return( (menumask&event->u.chr.state)==foo.short_mask && foo.shortcut == keysym );
    2407             : }
    2408             : 
    2409           0 : int GMenuMask(void) {
    2410           0 : return( menumask );
    2411             : }
    2412             : 
    2413           0 : GResInfo *_GMenuRIHead(void) {
    2414           0 :     if ( !gmenubar_inited )
    2415           0 :         GMenuInit();
    2416           0 : return( &gmenubar_ri );
    2417             : }
    2418             : 
    2419           0 : int GMenuAnyUnmaskedShortcuts(GGadget *mb1, GGadget *mb2) {
    2420             : 
    2421           0 :     if ( most_recent_popup_menu!=NULL && most_recent_popup_menu->any_unmasked_shortcuts )
    2422           0 : return( true );
    2423             : 
    2424           0 :     if ( mb1!=NULL && ((GMenuBar *) mb1)->any_unmasked_shortcuts )
    2425           0 : return( true );
    2426             : 
    2427           0 :     if ( mb2!=NULL && ((GMenuBar *) mb2)->any_unmasked_shortcuts )
    2428           0 : return( true );
    2429             : 
    2430           0 : return( false );
    2431             : }

Generated by: LCOV version 1.10