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