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 : }
|