1 /***********************************************************/
2 /* */
3 /* System dependent menu routines (unix, x11) */
4 /* */
5 /***********************************************************/
6
7 #include "unix/guts.h"
8 #include "Menu.h"
9 #include "Icon.h"
10 #include "Window.h"
11 #include "Application.h"
12 #define XK_MISCELLANY
13 #define XK_LATIN1
14 #define XK_XKB_KEYS
15 #include <X11/keysymdef.h>
16
17 #define TREE (PAbstractMenu(self)->tree)
18
19 #define MAX_BITMAP_HEIGHT (guts.displaySize.y / 4)
20 #define MAX_BITMAP_WIDTH (guts.displaySize.x / 4)
21
22 static PMenuWindow
get_menu_window(Handle self,XWindow xw)23 get_menu_window( Handle self, XWindow xw)
24 {
25 DEFMM;
26 PMenuWindow w = XX-> w;
27 while (w && w->w != xw)
28 w = w-> next;
29 return w;
30 }
31
32 extern Cursor predefined_cursors[];
33
34 static PMenuWindow
get_window(Handle self,PMenuItemReg m)35 get_window( Handle self, PMenuItemReg m)
36 {
37 DEFMM;
38 PMenuWindow w, wx;
39 XSetWindowAttributes attrs;
40
41 if ( !( w = malloc( sizeof( MenuWindow)))) return NULL;
42 bzero(w, sizeof( MenuWindow));
43 w-> self = self;
44 w-> m = m;
45 w-> sz.x = -1;
46 w-> sz.y = -1;
47 attrs. event_mask = 0
48 | KeyPressMask
49 | KeyReleaseMask
50 | ButtonPressMask
51 | ButtonReleaseMask
52 | EnterWindowMask
53 | LeaveWindowMask
54 | PointerMotionMask
55 | ButtonMotionMask
56 | KeymapStateMask
57 | ExposureMask
58 | VisibilityChangeMask
59 | StructureNotifyMask
60 | FocusChangeMask
61 | PropertyChangeMask
62 | ColormapChangeMask
63 | OwnerGrabButtonMask;
64 attrs. override_redirect = true;
65 attrs. save_under = true;
66 attrs. do_not_propagate_mask = attrs. event_mask;
67 w->w = XCreateWindow( DISP, guts. root,
68 0, 0, 1, 1, 0, CopyFromParent,
69 InputOutput, CopyFromParent,
70 0
71 | CWOverrideRedirect
72 | CWEventMask
73 | CWSaveUnder
74 , &attrs);
75 if (!w->w) {
76 free(w);
77 return NULL;
78 }
79 XCHECKPOINT;
80 XSetTransientForHint( DISP, w->w, None);
81 hash_store( guts.menu_windows, &w->w, sizeof(w->w), (void*)self);
82 wx = XX-> w;
83 if ( predefined_cursors[crArrow] == None) {
84 XCreateFontCursor( DISP, XC_left_ptr);
85 XCHECKPOINT;
86 }
87 XDefineCursor( DISP, w-> w, predefined_cursors[crArrow]);
88 if ( wx) {
89 while ( wx-> next ) wx = wx-> next;
90 w-> prev = wx;
91 wx-> next = w;
92 } else
93 XX-> w = w;
94 CREATE_ARGB_PICTURE(w->w, X(self)->flags.layered ? 32 : 0, w->argb_picture);
95 return w;
96 }
97
98 static int
item_count(PMenuWindow w)99 item_count( PMenuWindow w)
100 {
101 int i = 0;
102 PMenuItemReg m = w->m;
103
104 while (m) {
105 i++; m=m->next;
106 }
107 return i;
108 }
109
110 static void
kill_menu_bitmap(PMenuBitmap bm)111 kill_menu_bitmap( PMenuBitmap bm )
112 {
113 if ( bm-> xor ) {
114 prima_refcnt_dec(bm->xor);
115 unprotect_object(bm->xor);
116 Object_destroy( bm->xor);
117 }
118 if ( bm-> and ) {
119 prima_refcnt_dec(bm->and);
120 unprotect_object(bm->and);
121 Object_destroy( bm->and);
122 }
123 }
124
125 static void
free_unix_items(PMenuWindow w)126 free_unix_items( PMenuWindow w)
127 {
128 if ( w-> um) {
129 if ( w-> first < 0) {
130 int i;
131 for ( i = 0; i < w->num; i++) {
132 kill_menu_bitmap( &w->um[i].icon );
133 kill_menu_bitmap( &w->um[i].bitmap);
134 }
135 free( w-> um);
136 }
137 w-> um = NULL;
138 }
139 w-> num = 0;
140 }
141
142 static void
menu_window_delete_downlinks(PMenuSysData XX,PMenuWindow wx)143 menu_window_delete_downlinks( PMenuSysData XX, PMenuWindow wx)
144 {
145 PMenuWindow w = wx-> next;
146 {
147 XRectangle r;
148 Region rgn;
149 r. x = 0;
150 r. y = 0;
151 r. width = guts. displaySize. x;
152 r. height = guts. displaySize. y;
153 rgn = XCreateRegion();
154 XUnionRectWithRegion( &r, rgn, rgn);
155 XSetRegion( DISP, guts. menugc, rgn);
156 XDestroyRegion( rgn);
157 XSetForeground( DISP, guts. menugc, XX->c[ciBack]);
158 }
159 while ( w) {
160 PMenuWindow xw = w-> next;
161 hash_delete( guts. menu_windows, &w-> w, sizeof( w-> w), false);
162 XFillRectangle( DISP, w-> w, guts. menugc, 0, 0, w-> sz. x, w-> sz. y);
163 DELETE_ARGB_PICTURE(w->argb_picture);
164 XDestroyWindow( DISP, w-> w);
165 XFlush( DISP);
166 free_unix_items( w);
167 free( w);
168 w = xw;
169 }
170 wx-> next = NULL;
171 XX-> focused = wx;
172 XFlush(DISP);
173 }
174
175 static int
get_text_width(PCachedFont font,const char * text,int byte_length,Bool utf8,uint32_t * xft_map8)176 get_text_width( PCachedFont font, const char * text, int byte_length, Bool utf8, uint32_t * xft_map8)
177 {
178 int ret = 0;
179 int char_len = utf8 ? utf8_length(( U8*) text, ( U8*) text + byte_length) : byte_length;
180 #ifdef USE_XFT
181 if ( font-> xft)
182 return prima_xft_get_text_width( font, text, char_len, utf8 ? toUTF8 : 0, xft_map8, NULL);
183 #endif
184 if ( utf8) {
185 XChar2b * xc = prima_alloc_utf8_to_wchar( text, char_len);
186 if ( xc) {
187 ret = XTextWidth16( font-> fs, xc, char_len);
188 free( xc);
189 }
190 } else {
191 ret = XTextWidth( font-> fs, text, byte_length);
192 }
193 return ret;
194 }
195
196 typedef struct {
197 void * xft_drawable;
198 uint32_t * xft_map8;
199 Color rgb;
200 unsigned long pixel;
201 XWindow win;
202 GC gc;
203 Bool layered;
204 unsigned long * c;
205 } MenuDrawRec;
206
207 static void
get_font_abc(PCachedFont font,char * index,Bool utf8,FontABC * rec,MenuDrawRec * data)208 get_font_abc( PCachedFont font, char * index, Bool utf8, FontABC * rec, MenuDrawRec * data)
209 {
210 char buf[2];
211 XCharStruct * cs;
212 #ifdef USE_XFT
213 if ( font-> xft) {
214 Point ovx;
215 rec-> b = prima_xft_get_text_width(
216 font, index, 1, utf8 ? toUTF8 : 0,
217 data-> xft_map8, &ovx
218 );
219 /* not really abc, but enough for its single invocation */
220 rec-> a = -ovx. x;
221 rec-> c = -ovx. y;
222 return;
223 }
224 #endif
225 if ( utf8)
226 prima_utf8_to_wchar( index, ( XChar2b*) buf, 2, 1);
227 else
228 buf[0] = *index;
229 cs = prima_char_struct( font-> fs, ( XChar2b*) buf, utf8);
230 rec-> a = cs-> lbearing;
231 rec-> b = cs-> rbearing - cs-> lbearing;
232 rec-> c = cs-> width - cs-> rbearing;
233 }
234
235 static void
text_out(PCachedFont font,const char * text,int length,int x,int y,Bool utf8,MenuDrawRec * data)236 text_out( PCachedFont font, const char * text, int length, int x, int y,
237 Bool utf8, MenuDrawRec * data)
238 {
239 #ifdef USE_XFT
240 XftColor xftcolor;
241 if ( font-> xft) {
242 xftcolor.color.red = COLOR_R16(data-> rgb);
243 xftcolor.color.green = COLOR_G16(data-> rgb);
244 xftcolor.color.blue = COLOR_B16(data-> rgb);
245 xftcolor.color.alpha = 0xffff;
246 xftcolor.pixel = data-> pixel;
247 XftDrawString32(( XftDraw*) data-> xft_drawable, &xftcolor,
248 font-> xft, x, y, ( FcChar32*)text, length);
249 return;
250 }
251 #endif
252 XSetForeground( DISP, data-> gc, data-> pixel);
253 if ( utf8)
254 XDrawString16( DISP, data->win, data->gc, x, y, ( XChar2b*) text, length);
255 else
256 XDrawString( DISP, data->win, data->gc, x, y, text, length);
257 }
258
259 static void
convert_to_lowres(Handle src)260 convert_to_lowres(Handle src)
261 {
262 int i, colors = 0;
263 RGBColor palette[256];
264
265 for ( i = 0; i < guts. palSize; i++) {
266 if ( guts. palette[i]. rank <= RANK_LOCKED) continue;
267 palette[colors]. r = guts. palette[i]. r;
268 palette[colors]. g = guts. palette[i]. g;
269 palette[colors]. b = guts. palette[i]. b;
270 if ( ++colors > 255) break;
271 }
272
273 CImage(src)->reset( src, guts.qdepth, palette, colors);
274 }
275
276 static Bool
create_menu_bitmap(Handle src,PMenuBitmap dst,Bool layered,Bool disabled,int * w,int * h)277 create_menu_bitmap(Handle src, PMenuBitmap dst, Bool layered, Bool disabled, int * w, int * h)
278 {
279 Bool dispose_src = false;
280 PIcon i = (PIcon) src;
281
282 if ( i == NULL || i-> stage >= csDead)
283 return false;
284
285 bzero(dst, sizeof(MenuBitmap));
286
287 /* XXX check for dynamic colors and layered env */
288 #define USE_ARGB_SHADING 1
289
290 if ( i-> w > MAX_BITMAP_WIDTH || i-> h > MAX_BITMAP_HEIGHT ) {
291 Handle dup;
292 int w = i-> w, h = i-> h;
293 if ( w > MAX_BITMAP_WIDTH ) w = MAX_BITMAP_WIDTH;
294 if ( h > MAX_BITMAP_HEIGHT ) h = MAX_BITMAP_HEIGHT;
295
296 dup = i-> self-> dup(src);
297 Object_destroy(dup);
298 dispose_src = true;
299 src = dup;
300
301 i = (PIcon) src;
302 i-> self-> stretch(src, w, h);
303 }
304 *w = i->w;
305 *h = i->h;
306
307 /* menu is special around 1-bit/1-bit icons, for win32 compat - these should be treated as bitmaps
308 to paint with menu colors */
309 if ( XT_IS_ICON(X(src)) && ((i->type & imBPP) == 1) && (i->maskType == 1)) {
310 IconHandle split = i->self->split(src);
311 dst->is_mono = true;
312 dst->xor = CImage(split.xorMask)->bitmap(split.xorMask);
313 Object_destroy( split.xorMask );
314 Object_destroy( split.andMask );
315 }
316
317 /* as-is image */
318 else if ( !( XT_IS_ICON(X(src))) && !disabled ) {
319 goto FALLBACK;
320 }
321
322 /* as-is icon */
323 else if ( XT_IS_ICON(X(src)) && !disabled && (i->maskType == 1)) {
324 IconHandle split = i->self->split(src);
325 if ( guts.dynamicColors )
326 convert_to_lowres(split.xorMask);
327 dst->xor = CImage(split.xorMask)->bitmap(split.xorMask);
328 dst->and = CImage(split.andMask)->bitmap(split.andMask);
329 Object_destroy( split.xorMask );
330 Object_destroy( split.andMask );
331 }
332
333 /* layering is available -- use it */
334 else if (
335 guts. argb_depth &&
336 (
337 layered ||
338 ( disabled && USE_ARGB_SHADING ) ||
339 ( XT_IS_ICON(X(src)) && (i->maskType == 8))
340 )
341 ) {
342 /* convert input to rgba */
343 if ( XT_IS_ICON(X(src))) {
344 if ( !dispose_src ) {
345 i = (PIcon)(src = i->self->dup(src));
346 dispose_src = true;
347 }
348 if ( i-> maskType == 1 )
349 i->self->set_maskType(src, 8);
350 if ( disabled ) {
351 int sz = i-> maskSize;
352 Byte * p = i-> mask;
353 while (sz--) *(p++) /= 2;
354 }
355 i-> autoMasking = amNone;
356 i-> self-> set_type( src, imRGB );
357 i-> self-> premultiply_alpha(src, NULL);
358 dst->xor = CImage(src)->bitmap(src);
359 } else {
360 Handle dup = (Handle) create_object("Prima::Icon", "iiiii",
361 "width", i->w,
362 "height", i-> h,
363 "type", i->type,
364 "maskType", 8,
365 "autoMasking", amNone
366 );
367 PImage(dup)->palSize = i->palSize;
368 memcpy( PImage(dup)->palette, i->palette, i->palSize * 3);
369 memcpy( PImage(dup)->data, i->data, i->dataSize);
370 CImage(dup)->set_type(dup, imRGB);
371 memset( PIcon(dup)->mask, disabled ? 0x80 : 0xff, PIcon(dup)->maskSize);
372 if ( disabled )
373 CImage(dup)->premultiply_alpha(dup, NULL);
374 dst->xor = CImage(dup)->bitmap(dup);
375 Object_destroy(dup);
376 }
377
378 } else if ( XT_IS_ICON(X(src))) {
379 /* argb icon over non-argb surface */
380 IconHandle split;
381 if ( !dispose_src ) {
382 i = (PIcon)(src = i->self->dup(src));
383 dispose_src = true;
384 }
385 if ( i->maskType == 8 ) {
386 i->self->set_type(src, imRGB);
387 i->self->premultiply_alpha(src, NULL);
388 i->self->set_maskType(src, 1);
389 }
390 split = i->self->split(src);
391 dst->use_stippling = disabled;
392 dst->xor = CImage(split.xorMask)->bitmap(split.xorMask);
393 dst->and = CImage(split.andMask)->bitmap(split.andMask);
394 Object_destroy( split.xorMask );
395 Object_destroy( split.andMask );
396 } else {
397 dst-> use_stippling = disabled;
398 FALLBACK:
399 if ( guts.dynamicColors ) {
400 Handle dup = i-> self-> dup(src);
401 if ( dispose_src ) Object_destroy(src);
402 dispose_src = true;
403 convert_to_lowres(dup);
404 i = (PIcon) (src = dup);
405 }
406 dst->xor = i->self->bitmap(src);
407 }
408
409 if ( dispose_src )
410 Object_destroy( src );
411 if ( dst->xor ) {
412 prima_refcnt_inc(dst->xor);
413 protect_object(dst->xor);
414 }
415 if ( dst->and ) {
416 prima_refcnt_inc(dst->and);
417 protect_object(dst->and);
418 }
419
420 return true;
421 #undef USE_ARGB_SHADING
422 }
423
424 static void
update_menu_window(PMenuSysData XX,PMenuWindow w)425 update_menu_window( PMenuSysData XX, PMenuWindow w)
426 {
427 int x, y = 2 + 2, startx, icon_width;
428 Bool vertical = w != &XX-> wstatic, layered;
429 PMenuItemReg m = w->m;
430 PUnixMenuItem ix;
431 int lastIncOk = 1;
432 PCachedFont kf = XX-> font;
433 uint32_t *xft_map8 = NULL;
434 PWindow owner = ( PWindow)(PComponent( w-> self)-> owner);
435
436 #ifdef USE_XFT
437 {
438 const char * encoding = ( XX-> type. popup) ?
439 owner-> popupFont. encoding :
440 owner-> menuFont. encoding;
441 xft_map8 = prima_xft_map8( encoding);
442 }
443 #endif
444 layered = vertical ? false : X(owner)->flags. layered;
445
446 free_unix_items( w);
447 w-> num = item_count( w);
448 ix = w-> um = malloc( sizeof( UnixMenuItem) * w-> num);
449 if ( !ix) return;
450 bzero( w-> um, sizeof( UnixMenuItem) * w-> num);
451
452 m = w->m;
453 if ( vertical ) {
454 icon_width = MENU_CHECK_XOFFSET;
455 while (m) {
456 if ( m-> icon && PObject( m-> icon)-> stage < csDead) {
457 PImage i = ( PImage) m-> icon;
458 if ( i-> w > icon_width ) icon_width = i-> w;
459 }
460 m = m-> next;
461 }
462 startx = x = MENU_XOFFSET * 4 + MENU_CHECK_XOFFSET + icon_width;
463 } else {
464 startx = x = icon_width = 0;
465 }
466
467 if ( vertical) w-> last = -1;
468 w-> selected = -100;
469 m = w->m;
470 while ( m) {
471 ix-> icon_width = icon_width;
472
473 if ( m-> flags. custom_draw ) {
474 Event ev = { cmMenuItemMeasure };
475 ev.gen.P.x = 0;
476 ev.gen.P.y = 0;
477 ev.gen.i = m-> id;
478 CComponent(w-> self)-> message(w-> self,&ev);
479 ix-> width = ev.gen.P.x;
480 ix-> height = ev.gen.P.y + MENU_ITEM_GAP * 2;
481 } else if ( m-> flags. divider) {
482 ix-> height = vertical ? MENU_ITEM_GAP * 2 : 0;
483 } else {
484 int l = 0, w, h;
485 if ( m-> text) {
486 int i, ntildas = 0;
487 char * t = m-> text;
488 ix-> height = MENU_ITEM_GAP * 2 + kf-> font. height;
489 for ( i = 0; t[i]; i++) {
490 if ( t[i] == '~' && t[i+1]) {
491 ntildas++;
492 if ( t[i+1] == '~')
493 i++;
494 }
495 }
496 ix-> width += startx + get_text_width( kf, m-> text, i,
497 m-> flags. utf8_text, xft_map8);
498 if ( ntildas)
499 ix-> width -= ntildas * get_text_width( kf, "~", 1, false, xft_map8);
500 } else if ( create_menu_bitmap(m->bitmap, &ix->bitmap, layered, m->flags.disabled, &w, &h)) {
501 ix-> height += (( h < kf-> font. height) ? kf-> font. height : h) +
502 MENU_ITEM_GAP * 2;
503 ix-> width += w + startx;
504 }
505
506 if ( create_menu_bitmap(m->icon, &ix->icon, layered, m->flags.disabled, &w, &h)) {
507 int y = h + MENU_ITEM_GAP * 2;
508 if ( ix-> height < y ) ix-> height = y;
509 }
510
511 if ( ix-> height > MAX_BITMAP_HEIGHT )
512 ix-> height = MAX_BITMAP_HEIGHT;
513
514 if ( m-> accel && ( l = strlen( m-> accel))) {
515 ix-> accel_width = get_text_width( kf, m-> accel, l,
516 m-> flags. utf8_accel, xft_map8);
517 }
518 if ( ix-> accel_width + ix-> width > x) x = ix-> accel_width + ix-> width;
519 }
520
521 if ( vertical && lastIncOk &&
522 y + ix-> height + MENU_ITEM_GAP * 2 + kf-> font. height > guts. displaySize. y) {
523 lastIncOk = 0;
524 y += MENU_ITEM_GAP * 2 + kf-> font. height;
525 }
526 m = m-> next;
527 if ( lastIncOk) {
528 y += ix-> height;
529 w-> last++;
530 }
531 ix++;
532 if ( !lastIncOk) break;
533 }
534
535 if ( vertical) {
536 if ( x > guts. displaySize. x - 64) x = guts. displaySize. x - 64;
537 w-> sz.x = x;
538 w-> sz.y = y;
539 XResizeWindow( DISP, w-> w, x, y);
540 }
541 }
542
543 static int
menu_point2item(PMenuSysData XX,PMenuWindow w,int x,int y,PMenuItemReg * m_ptr)544 menu_point2item( PMenuSysData XX, PMenuWindow w, int x, int y, PMenuItemReg * m_ptr)
545 {
546 int l = 0, r = 0, index = 0;
547 PMenuItemReg m;
548 PUnixMenuItem ix;
549 if ( !w) return -1;
550 m = w-> m;
551 ix = w-> um;
552 if ( !ix) return -1;
553 if ( w == &XX-> wstatic) {
554 int right = w-> right;
555 l = r = 0;
556 if ( x < l) return -1;
557 while ( m) {
558 if ( m-> flags. divider) {
559 if ( right > 0) {
560 r += right;
561 right = 0;
562 }
563 if ( x < r) return -1;
564 } else {
565 if ( index <= w-> last) {
566 r += MENU_XOFFSET * 2 + ix-> width;
567 if ( m-> accel) r += MENU_XOFFSET/2 + ix-> accel_width;
568 } else
569 r += MENU_XOFFSET * 2 + XX-> guillemots;
570 if (x >= l && x <= r) {
571 if ( m_ptr) *m_ptr = m;
572 return index;
573 }
574 if ( index > w-> last) return -1;
575 }
576 l = r;
577 index++;
578 ix++;
579 m = m-> next;
580 }
581 } else {
582 l = r = 2;
583 if ( y < l) return -1;
584 while ( m) {
585 if ( index > w-> last) {
586 r += MENU_ITEM_GAP * 2 + XX-> font-> font. height;
587 goto CHECK;
588 } else if ( m-> flags. divider) {
589 r += MENU_ITEM_GAP * 2;
590 if ( y < r) return -1;
591 } else {
592 r += ix-> height;
593 CHECK:
594 if ( y >= l && y <= r) {
595 if ( m_ptr) *m_ptr = m;
596 return index;
597 }
598 if ( index > w-> last) return -1;
599 }
600 l = r;
601 index++;
602 ix++;
603 m = m-> next;
604 }
605 }
606 return -1;
607 }
608
609 static Point
menu_item_offset(PMenuSysData XX,PMenuWindow w,int index)610 menu_item_offset( PMenuSysData XX, PMenuWindow w, int index)
611 {
612 Point ret = {0,0};
613 PMenuItemReg m = w-> m;
614 PUnixMenuItem ix = w-> um;
615 if ( index < 0 || !ix || !m) return ret;
616 if ( w == &XX-> wstatic) {
617 int right = w-> right;
618 while ( m && index--) {
619 if ( m-> flags. divider) {
620 if ( right > 0) {
621 ret. x += right;
622 right = 0;
623 }
624 } else {
625 ret. x += MENU_XOFFSET * 2 + ix-> width;
626 if ( m-> accel) ret. x += MENU_XOFFSET / 2 + ix-> accel_width;
627 }
628 ix++;
629 m = m-> next;
630 }
631 } else {
632 ret. y = 2;
633 ret. x = 2;
634 while ( m && index--) {
635 ret. y += ix-> height;
636 ix++;
637 m = m-> next;
638 }
639 }
640 return ret;
641 }
642
643 static Point
menu_item_size(PMenuSysData XX,PMenuWindow w,int index)644 menu_item_size( PMenuSysData XX, PMenuWindow w, int index)
645 {
646 PMenuItemReg m = w-> m;
647 PUnixMenuItem ix;
648 Point ret = {0,0};
649 if ( index < 0 || !w-> um || !m) return ret;
650 if ( w == &XX-> wstatic) {
651 if ( index >= 0 && index <= w-> last) {
652 ix = w-> um + index;
653 while ( index--) m = m-> next;
654 if ( m-> flags. divider) return ret;
655 ret. x = MENU_XOFFSET * 2 + ix-> width;
656 if ( m-> accel) ret. x += MENU_XOFFSET / 2+ ix-> accel_width;
657 } else if ( index == w-> last + 1) {
658 ret. x = MENU_XOFFSET * 2 + XX-> guillemots;
659 } else
660 return ret;
661 ret. y = XX-> font-> font. height + MENU_ITEM_GAP * 2;
662 } else {
663 if ( index >= 0 && index <= w-> last) {
664 ix = w-> um + index;
665 ret. y = ix-> height;
666 } else if ( index == w-> last + 1) {
667 ret. y = 2 * MENU_ITEM_GAP + XX-> font-> font. height;
668 } else
669 return ret;
670 ret. x = w-> sz. x - 4;
671 }
672 return ret;
673 }
674
675 static void
menu_select_item(PMenuSysData XX,PMenuWindow w,int index)676 menu_select_item( PMenuSysData XX, PMenuWindow w, int index)
677 {
678 if ( index != w-> selected) {
679 XRectangle r;
680 Point p1 = menu_item_offset( XX, w, index);
681 Point p2 = menu_item_offset( XX, w, w-> selected );
682 Point s1 = menu_item_size( XX, w, index);
683 Point s2 = menu_item_size( XX, w, w-> selected );
684 if ( s1.x == 0 && s1.y == 0) {
685 if ( s2.x == 0 && s2.y == 0) return;
686 r.x = p2.x; r.y = p2.y;
687 r.width = s2.x; r.height = s2.y;
688 } else if ( s2.x == 0 && s2.y == 0) {
689 r.x = p1.x; r.y = p1.y;
690 r.width = s1.x; r.height = s1.y;
691 } else {
692 r. x = ( p1. x < p2. x) ? p1. x : p2. x;
693 r. y = ( p1. y < p2. y) ? p1. y : p2. y;
694 r. width = ( p1.x + s1.x > p2.x + s2.x) ? p1.x + s1.x - r.x : p2.x + s2.x - r.x;
695 r. height = ( p1.y + s1.y > p2.y + s2.y) ? p1.y + s1.y - r.y : p2.y + s2.y - r.y;
696 }
697 w-> selected = ( index < 0) ? -100 : index;
698 XClearArea( DISP, w-> w, r.x, r.y, r.width, r.height, true);
699 XX-> paint_pending = true;
700 }
701 }
702
703 static Bool
send_cmMenu(Handle self,PMenuItemReg m)704 send_cmMenu( Handle self, PMenuItemReg m)
705 {
706 Event ev;
707 Handle owner = PComponent( self)-> owner;
708 bzero( &ev, sizeof( ev));
709 ev. cmd = cmMenu;
710 ev. gen. H = self;
711 ev. gen. i = m ? m-> id : 0;
712 CComponent(owner)-> message( owner, &ev);
713 if ( PComponent( owner)-> stage == csDead ||
714 PComponent( self)-> stage == csDead) return false;
715 if ( self != guts. currentMenu) return false;
716 return true;
717 }
718
719 static Bool
menu_enter_item(PMenuSysData XX,PMenuWindow w,int index,int type)720 menu_enter_item( PMenuSysData XX, PMenuWindow w, int index, int type)
721 {
722 PMenuItemReg m = w-> m;
723 int index2 = index, div = 0;
724
725 XX-> focused = w;
726
727 if ( index < 0 || index > w-> last + 1 || !w-> um || !m) return false;
728 while ( index2--) {
729 if ( m-> flags. divider) div = 1;
730 m = m-> next;
731 }
732 if ( index == w-> last + 1) div = 0;
733
734 if ( m-> flags. disabled && index <= w-> last) return false;
735
736 if ( m-> down || index == w-> last + 1) {
737 PMenuWindow w2;
738 Point p, s, n = w-> pos;
739
740 if ( w-> next && w-> next-> m == m-> down) {
741 XX-> focused = w-> next;
742 XMapRaised( DISP, w-> next-> w);
743 XFlush(DISP);
744 return true;
745 }
746
747 if ( index != w-> last + 1) {
748 if ( !send_cmMenu( w-> self, m)) return false;
749 m = m-> down;
750 }
751
752 menu_window_delete_downlinks( XX, w);
753 w2 = get_window( w-> self, m);
754 if ( !w2) return false;
755
756 update_menu_window( XX, w2);
757 p = menu_item_offset( XX, w, index);
758 s = menu_item_size( XX, w, index);
759
760 if ( &XX-> wstatic == w) {
761 XWindow dummy;
762 XTranslateCoordinates( DISP, w->w, guts. root, 0, 0, &n.x, &n.y, &dummy);
763 w-> pos = n;
764 }
765
766 n. x += p. x;
767 n. y += p. y;
768 p. x += w-> pos. x;
769 p. y += w-> pos. y;
770 if ( &XX-> wstatic == w) {
771 if ( div) n. x -= w2-> sz. x - s. x;
772 if ( p.y + s.y + w2-> sz.y <= guts. displaySize.y)
773 n. y = p. y + s. y;
774 else if ( w2-> sz.y <= p. y)
775 n. y = p. y - w2-> sz. y;
776 else
777 n. y = 0;
778 if ( n. x + w2-> sz. x > guts. displaySize. x)
779 n. x = guts. displaySize. x - w2-> sz. x;
780 else if ( n. x < 0)
781 n. x = 0;
782 } else {
783 div = 0;
784 if ( p.y + w2-> sz.y <= guts. displaySize.y)
785 n. y = p. y;
786 else if ( w2-> sz.y <= p. y + s. y)
787 n. y = p. y + s. y - w2-> sz. y;
788 else
789 n. y = 0;
790 if ( p.x + s. x + w2-> sz.x <= guts. displaySize.x)
791 n. x = p. x + s. x;
792 else if ( w2-> sz.x <= p.x)
793 n. x = p. x - w2-> sz. x;
794 else {
795 n. x = 0;
796 if ( p.y + w2-> sz.y + s.y <= guts. displaySize.y)
797 n. y += s.y;
798 else if ( w2-> sz.y <= p. y)
799 n. y -= s.y;
800 }
801 }
802 XMoveWindow( DISP, w2-> w, n. x, n. y);
803 XMapRaised( DISP, w2-> w);
804 w2-> pos = n;
805 XX-> focused = w2;
806 XFlush(DISP);
807 } else {
808 Handle self = w-> self;
809 if (( &XX-> wstatic == w) && ( type == 0)) {
810 menu_window_delete_downlinks( XX, w);
811 return true;
812 }
813 prima_end_menu();
814 CAbstractMenu( self)-> sub_call( self, m);
815 return false;
816 }
817 return true;
818 }
819
820
821 static void
store_char(char * src,int srclen,int * srcptr,char * dst,int * dstptr,Bool utf8,MenuDrawRec * data)822 store_char( char * src, int srclen, int * srcptr, char * dst, int * dstptr, Bool utf8, MenuDrawRec * data)
823 {
824 if ( *srcptr >= srclen ) return;
825
826 if ( utf8) {
827 STRLEN char_len;
828 UV uv = prima_utf8_uvchr_end(src + *srcptr, src + srclen, &char_len);
829 *srcptr += char_len;
830 if ( data-> xft_map8) {
831 *(( uint32_t*)(dst + *dstptr)) = (uint32_t) uv;
832 *dstptr += 4;
833 } else {
834 (( XChar2b*)(dst + *dstptr))-> byte1 = uv >> 8;
835 (( XChar2b*)(dst + *dstptr))-> byte2 = uv & 0xff;
836 *dstptr += 2;
837 }
838 } else {
839 if ( data-> xft_map8) {
840 uint32_t c = (( U8*) src)[ *srcptr];
841 if ( c > 127)
842 c = data-> xft_map8[ c - 128];
843 *(( uint32_t*)(dst + *dstptr)) = c;
844 (*dstptr) += 4;
845 (*srcptr)++;
846 } else {
847 dst[(*dstptr)++] = src[(*srcptr)++];
848 }
849 }
850 }
851
852 #define DECL_DRAW(name) \
853 static Bool \
854 menuitem_draw_##name( \
855 Handle self, XWindow win, PMenuWindow w, Region rgn, \
856 PMenuItemReg m, PUnixMenuItem ix, \
857 int x, int y, int *str_size, char ** str_ptr, \
858 MenuDrawRec * draw, \
859 Bool vertical, Bool selected, int descent, unsigned long clr, Color rgb, int index, void* param \
860 )
861
862 #define DRAW(name) menuitem_draw_ ## name( \
863 self, win, w, rgn, m, ix, x, y, \
864 &sz, &s, &draw, vertical, selected, descent, clr, rgb, last, NULL \
865 )
866
DECL_DRAW(text)867 DECL_DRAW(text)
868 {
869 DEFMM;
870 PCachedFont kf = XX->font;
871 int lineStart, lineEnd = 0, haveDash = 0;
872 int ay = y + (ix->height + kf-> font. height) / 2 - kf-> font. descent;
873 int text_len = strlen(m-> text);
874 int l, i, slen;
875 char * t = m-> text;
876 x += MENU_XOFFSET + (vertical ? ix-> icon_width : 0);
877
878 for (;;) {
879 l = i = slen = lineEnd = haveDash = 0;
880 lineStart = -1;
881 while ( l < *str_size - 1 && t[i]) {
882 if (t[i] == '~' && t[i+1]) {
883 if ( t[i+1] == '~') {
884 int dummy = 0;
885 store_char( "~", strlen("~"), &dummy, *str_ptr, &l, false, draw);
886 i += 2;
887 slen++;
888 } else {
889 if ( !haveDash) {
890 haveDash = 1;
891 lineEnd = lineStart + 1 +
892 get_text_width( kf, t + i + 1, 1,
893 m-> flags. utf8_text, draw-> xft_map8);
894 }
895 i++;
896 }
897 } else {
898 if ( !haveDash) {
899 FontABC abc;
900 get_font_abc( kf, t+i, m-> flags. utf8_text, &abc, draw);
901 if ( lineStart < 0)
902 lineStart = ( abc.a < 0) ? -abc.a : 0;
903 lineStart += abc.a + abc.b + abc.c;
904 }
905 store_char( t, text_len, &i, *str_ptr, &l, m-> flags. utf8_text, draw);
906 slen++;
907 }
908 }
909 if ( t[i]) {
910 free(*str_ptr);
911 if ( !( *str_ptr = malloc(( *str_size) *= 2))) return false;
912 } else
913 break;
914 }
915 if ( m-> flags. disabled && !selected) {
916 draw-> pixel = draw->c[ciLight3DColor];
917 draw-> rgb = XX->rgb[ciLight3DColor];
918 text_out( kf, *str_ptr, slen, x+1, ay+1,
919 m-> flags. utf8_text, draw);
920 }
921 draw-> pixel = clr;
922 draw-> rgb = rgb;
923 text_out( kf, *str_ptr, slen, x, ay, m-> flags. utf8_text, draw);
924 if ( haveDash) {
925 if ( m-> flags. disabled && !selected) {
926 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
927 XDrawLine( DISP, win, draw->gc, x+lineStart+1, ay+descent-1+1,
928 x+lineEnd+1, ay+descent-1+1);
929 }
930 XSetForeground( DISP, draw->gc, clr);
931 XDrawLine( DISP, win, draw->gc, x+lineStart, ay+descent-1,
932 x+lineEnd, ay+descent-1);
933 }
934
935 return true;
936 }
937
DECL_DRAW(accel)938 DECL_DRAW(accel)
939 {
940 DEFMM;
941 PCachedFont kf = XX->font;
942 int i, ul = 0, sl = 0, dl = 0;
943 int zx = vertical ?
944 w->sz.x - 1 - MENU_XOFFSET - MENU_CHECK_XOFFSET - ix-> accel_width :
945 x + MENU_XOFFSET/2;
946 int zy = vertical ?
947 y + ( ix->height + kf-> font. height) / 2 - kf-> font. descent:
948 y + ix->height - MENU_ITEM_GAP - kf-> font. descent;
949 int accel_len = strlen(m-> accel);
950
951 if ( m-> flags. utf8_accel)
952 ul = prima_utf8_length( m-> accel, -1);
953 else
954 ul = accel_len;
955 if (( ul * 4 + 4) > *str_size) {
956 free(*str_ptr);
957 if ( !( *str_ptr = malloc((*str_size) = (ul * 4 + 4)))) return false;
958 }
959 for ( i = 0; i < ul; i++)
960 store_char( m-> accel, accel_len, &sl, *str_ptr, &dl, m-> flags. utf8_accel, draw);
961
962 if ( m-> flags. disabled && !selected) {
963 draw-> pixel = draw-> c[ciLight3DColor];
964 draw-> rgb = XX->rgb[ciLight3DColor];
965 text_out( kf, *str_ptr, ul, zx + 1, zy + 1,
966 m-> flags. utf8_accel, draw);
967 }
968 draw-> pixel = clr;
969 draw-> rgb = rgb;
970 text_out( kf, *str_ptr, ul, zx, zy,
971 m-> flags. utf8_accel, draw);
972 return true;
973 }
974
DECL_DRAW(submenu)975 DECL_DRAW(submenu)
976 {
977 DEFMM;
978 PCachedFont kf = XX->font;
979 int ave = kf-> font. height * 0.4;
980 int center = y + ix->height / 2;
981 int mx = w->sz.x - 1;
982 XPoint p[3];
983 p[0].x = mx - MENU_CHECK_XOFFSET/2;
984 p[0].y = center;
985 p[1].x = mx - ave - MENU_CHECK_XOFFSET/2;
986 p[1].y = center - ave * 0.6;
987 p[2].x = mx - ave - MENU_CHECK_XOFFSET/2;
988 p[2].y = center + ave * 0.6 + 1;
989 if ( m-> flags. disabled && !selected) {
990 int i;
991 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
992 for ( i = 0; i < 3; i++) {
993 p[i].x++;
994 p[i].y++;
995 }
996 XFillPolygon( DISP, win, draw->gc, p, 3, Nonconvex, CoordModeOrigin);
997 for ( i = 0; i < 3; i++) {
998 p[i].x--;
999 p[i].y--;
1000 }
1001 }
1002 XSetForeground( DISP, draw->gc, clr);
1003 XFillPolygon( DISP, win, draw->gc, p, 3, Nonconvex, CoordModeOrigin);
1004 return true;
1005 }
1006
DECL_DRAW(check)1007 DECL_DRAW(check)
1008 {
1009 int bottom = y + ix->height - MENU_ITEM_GAP - ix-> height * 0.2;
1010 int ax = x + MENU_XOFFSET / 2;
1011 XGCValues gcv;
1012 gcv. line_width = 3;
1013 XChangeGC( DISP, draw->gc, GCLineWidth, &gcv);
1014 if ( m-> flags. disabled && !selected) {
1015 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
1016 XDrawLine( DISP, win, draw->gc, ax + 1 + 1 , y + ix->height / 2 + 1, ax + MENU_XOFFSET - 2 + 1, bottom - 1);
1017 XDrawLine( DISP, win, draw->gc, ax + MENU_XOFFSET - 2 + 1, bottom + 1, ax + MENU_CHECK_XOFFSET + 1, y + MENU_ITEM_GAP + ix-> height * 0.2);
1018 }
1019 XSetForeground( DISP, draw->gc, clr);
1020 XDrawLine( DISP, win, draw->gc, ax + 1, y + ix->height / 2, ax + MENU_XOFFSET - 2, bottom);
1021 XDrawLine( DISP, win, draw->gc, ax + MENU_XOFFSET - 2, bottom, ax + MENU_CHECK_XOFFSET, y + MENU_ITEM_GAP + ix-> height * 0.2);
1022 gcv. line_width = 1;
1023 XChangeGC( DISP, draw->gc, GCLineWidth, &gcv);
1024
1025 return true;
1026 }
1027
DECL_DRAW(checkbox)1028 DECL_DRAW(checkbox)
1029 {
1030 int
1031 x1 = x + MENU_XOFFSET / 2 + 1,
1032 y1 = y + (ix-> height/2) - MENU_CHECK_XOFFSET/2 + 1,
1033 x2 = x1 + MENU_CHECK_XOFFSET - 2,
1034 y2 = y1 + MENU_CHECK_XOFFSET - 2;
1035
1036 XSetForeground( DISP, draw->gc, draw->c[m->flags.disabled ? ciLight3DColor : (MENU_PALETTE_SIZE-1)]);
1037 XDrawLine( DISP, win, draw->gc, x1, y2, x2 + 1, y2);
1038 XDrawLine( DISP, win, draw->gc, x2, y2, x2, y1);
1039 XSetForeground( DISP, draw->gc, draw->c[ m->flags.disabled ? ciDisabledText : ciDark3DColor] );
1040 XDrawLine( DISP, win, draw->gc, x2, y1, x1, y1);
1041 XDrawLine( DISP, win, draw->gc, x1, y1, x1, y2);
1042
1043 x1++; y1++; x2--; y2--;
1044 XSetForeground( DISP, draw->gc, draw->c[ m->flags.disabled ? ciDisabledText : ciDark3DColor] );
1045 XDrawLine( DISP, win, draw->gc, x1, y2, x2 + 1, y2);
1046 XDrawLine( DISP, win, draw->gc, x2, y2, x2, y1);
1047 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
1048 XDrawLine( DISP, win, draw->gc, x2, y1, x1, y1);
1049 XDrawLine( DISP, win, draw->gc, x1, y1, x1, y2);
1050
1051 return true;
1052 }
1053
DECL_DRAW(guillemots)1054 DECL_DRAW(guillemots)
1055 {
1056 DEFMM;
1057 PCachedFont kf = XX->font;
1058 char buf[8];
1059 int s = 0, d = 0;
1060
1061 x += MENU_XOFFSET + (vertical ? ix-> icon_width : 0);
1062 draw-> pixel = clr;
1063 draw-> rgb = rgb;
1064 store_char( ">", strlen(">"), &s, buf, &d, 0, draw);
1065 s = 0;
1066 store_char( ">", strlen(">"), &s, buf, &d, 0, draw);
1067 text_out( kf, buf, 2,
1068 x,
1069 y + MENU_ITEM_GAP + kf-> font. height - kf-> font. descent,
1070 false, draw);
1071 return true;
1072 }
1073
DECL_DRAW(divider)1074 DECL_DRAW(divider)
1075 {
1076 int mx = w-> sz.x - 1;
1077 if ( vertical) {
1078 y += MENU_ITEM_GAP - 1;
1079 XSetForeground( DISP, draw->gc, draw->c[ciDark3DColor]);
1080 XDrawLine( DISP, win, draw->gc, 1, y, mx-1, y);
1081 y++;
1082 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
1083 XDrawLine( DISP, win, draw->gc, 1, y, mx-1, y);
1084 y += MENU_ITEM_GAP;
1085 }
1086 return true;
1087 }
1088
DECL_DRAW(image)1089 DECL_DRAW(image)
1090 {
1091 DEFMM;
1092 int Y, H, W;
1093 PMenuBitmap bm = (PMenuBitmap) param;
1094 Drawable dummy_p;
1095 UnixSysData dummy_s;
1096
1097 if ( !bm-> xor )
1098 return true;
1099 W = X(bm->xor)->size.x;
1100 H = X(bm->xor)->size.y;
1101 Y = y + ( ix->height - H ) / 2;
1102
1103 bzero(&dummy_p, sizeof(dummy_p));
1104 bzero(&dummy_s, sizeof(dummy_s));
1105 dummy_p.sysData = &dummy_s;
1106 dummy_s.component.type.drawable = 1;
1107 dummy_s.component.type.widget = 1;
1108 dummy_s.drawable.flags.paint = 1;
1109 dummy_s.drawable.gc = draw->gc;
1110 dummy_s.drawable.gdrawable = win;
1111 dummy_s.drawable.size = w->sz;
1112 dummy_s.drawable.current_region = rgn;
1113 if ( draw->layered ) {
1114 dummy_s.drawable.flags.layered = 1;
1115 }
1116 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
1117 dummy_s.drawable.argb_picture = w-> argb_picture;
1118 #endif
1119
1120 if ( bm-> is_mono ) {
1121 if ( m-> flags. disabled ) {
1122 dummy_s.drawable.back.primary = 0x00000000;
1123 dummy_s.drawable.fore.primary = XX->c[ciLight3DColor];
1124 apc_gp_put_image((Handle) &dummy_p, bm->xor, x + 1, w-> sz.y - H - Y - 1, 0, 0, W, H, ropOrPut);
1125 }
1126 dummy_s.drawable.fore.primary = 0x00000000;
1127 dummy_s.drawable.back.primary = 0xffffffff;
1128 apc_gp_put_image((Handle) &dummy_p, bm->xor, x, w-> sz.y - H - Y, 0, 0, W, H, ropAndPut);
1129 dummy_s.drawable.back.primary = 0x00000000;
1130 dummy_s.drawable.fore.primary = clr;
1131 apc_gp_put_image((Handle) &dummy_p, bm->xor, x, w-> sz.y - H - Y, 0, 0, W, H, ropXorPut);
1132 } else {
1133 dummy_s.drawable.fore.primary = 0x00000000;
1134 dummy_s.drawable.back.primary = 0xffffffff;
1135 if ( bm-> and ) {
1136 XSetPlaneMask( DISP, draw-> gc,
1137 guts.argb_bits.red_mask|
1138 guts.argb_bits.green_mask|
1139 guts.argb_bits.blue_mask
1140 );
1141 apc_gp_put_image((Handle) &dummy_p, bm->and,
1142 x, w-> sz.y - H - Y,
1143 0, 0, W, H, ropAndPut);
1144 apc_gp_put_image((Handle) &dummy_p, bm->xor,
1145 x, w-> sz.y - H - Y,
1146 0, 0, W, H, ropXorPut);
1147 XSetPlaneMask( DISP, draw-> gc, AllPlanes);
1148 } else {
1149 apc_gp_put_image((Handle) &dummy_p, bm->xor,
1150 x, w-> sz.y - H - Y,
1151 0, 0, W, H, ropSrcOver);
1152 }
1153 if ( bm-> use_stippling ) {
1154 XSetStipple ( DISP, draw-> gc, prima_get_hatch( &fillPatterns[fpSimpleDots] ));
1155 XSetFillStyle ( DISP, draw-> gc, FillOpaqueStippled);
1156 XSetFunction ( DISP, draw-> gc, GXand );
1157 XSetForeground( DISP, draw-> gc, 0x00000000);
1158 XSetBackground( DISP, draw-> gc, 0xffffffff);
1159 XFillRectangle( DISP, win, draw-> gc, x, Y, W, H);
1160 XSetFunction ( DISP, draw-> gc, GXcopy );
1161 XSetFillStyle ( DISP, draw-> gc, FillSolid);
1162 }
1163 }
1164
1165 return true;
1166 }
1167
DECL_DRAW(bitmap)1168 DECL_DRAW(bitmap)
1169 {
1170 x += MENU_XOFFSET + (vertical ? ix-> icon_width : 0);
1171 return menuitem_draw_image(self,win,w,rgn,m,ix,x,y,str_size,str_ptr,draw,vertical,selected,descent,clr,rgb,index,&ix->bitmap);
1172 }
1173
DECL_DRAW(icon)1174 DECL_DRAW(icon)
1175 {
1176 x += MENU_XOFFSET;
1177 return menuitem_draw_image(self,win,w,rgn,m,ix,x,y,str_size,str_ptr,draw,vertical,selected,descent,clr,rgb,index,&ix->icon);
1178 }
1179
DECL_DRAW(background)1180 DECL_DRAW(background)
1181 {
1182 XSetForeground( DISP, draw->gc, draw->c[ciBack]);
1183 XSetBackground( DISP, draw->gc, draw->c[ciBack]);
1184 if ( vertical) {
1185 int mx = w->sz.x - 1;
1186 int my = w->sz.y - 1;
1187 XFillRectangle( DISP, win, draw->gc, 2, 2, mx-1, my-1);
1188 XSetForeground( DISP, draw->gc, draw->c[ciDark3DColor]);
1189 XDrawLine( DISP, win, draw->gc, 0, 0, 0, my);
1190 XDrawLine( DISP, win, draw->gc, 0, 0, mx-1, 0);
1191 XDrawLine( DISP, win, draw->gc, mx-1, my-1, 2, my-1);
1192 XDrawLine( DISP, win, draw->gc, mx-1, my-1, mx-1, 1);
1193 XSetForeground( DISP, draw->gc, guts. monochromeMap[0]);
1194 XDrawLine( DISP, win, draw->gc, mx, my, 1, my);
1195 XDrawLine( DISP, win, draw->gc, mx, my, mx, 0);
1196 XSetForeground( DISP, draw->gc, draw->c[ciLight3DColor]);
1197 XDrawLine( DISP, win, draw->gc, 1, 1, 1, my-1);
1198 XDrawLine( DISP, win, draw->gc, 1, 1, mx-2, 1);
1199 } else
1200 XFillRectangle( DISP, win, draw->gc, 0, 0, w-> sz.x, w-> sz.y);
1201
1202 return true;
1203 }
1204
1205 typedef struct {
1206 XWindow win;
1207 Bool layered;
1208 Handle self;
1209 } PaintEvent;
1210
DECL_DRAW(custom)1211 DECL_DRAW(custom)
1212 {
1213 Point offset, size;
1214 Event ev = { cmMenuItemPaint };
1215 PaintEvent rec = { win, draw-> layered, self };
1216
1217 offset = menu_item_offset( M(self), w, index);
1218 size = menu_item_size( M(self), w, index);
1219 ev.gen.P = w-> sz;
1220 ev.gen.i = m-> id;
1221 offset.y = w-> sz.y - offset.y - 1;
1222 ev.gen.R.left = offset.x;
1223 ev.gen.R.bottom = offset.y - size.y + 1;
1224 ev.gen.R.right = offset.x + size.x - 1;
1225 ev.gen.R.top = offset.y;
1226 ev.gen.p = &rec;
1227 ev.gen.B = selected;
1228 CComponent(w->self)-> message(w->self,&ev);
1229
1230 return true;
1231 }
1232
1233 #undef DECL_DRAW
1234
1235 static void
handle_menu_expose(XEvent * ev,XWindow win,Handle self)1236 handle_menu_expose( XEvent *ev, XWindow win, Handle self)
1237 {
1238 DEFMM;
1239 PMenuWindow w;
1240 PUnixMenuItem ix;
1241 PMenuItemReg m;
1242 Bool vertical;
1243 int sz = 1024, x, y;
1244 char *s;
1245 int right, last = 0;
1246 int descent;
1247 PCachedFont kf;
1248 MenuDrawRec draw;
1249 unsigned long clr = 0;
1250 Color rgb = 0;
1251 PWindow owner;
1252 Bool selected = false;
1253 XRectangle r;
1254 Region rgn;
1255
1256 kf = XX-> font;
1257 XX-> paint_pending = false;
1258 if ( XX-> wstatic. w == win) {
1259 w = XX-> w;
1260 vertical = false;
1261 } else {
1262 if ( !( w = get_menu_window( self, win))) return;
1263 vertical = true;
1264 }
1265 ix = w-> um;
1266 right = vertical ? 0 : w-> right;
1267 m = w-> m;
1268 if ( !ix) return;
1269
1270 owner = ( PWindow)(PComponent( w-> self)-> owner);
1271 bzero( &draw, sizeof( draw));
1272 draw. win = win;
1273 draw. layered = vertical ? false : X(owner)->flags. layered;
1274 if ( draw.layered ) {
1275 XGCValues gcv;
1276 gcv. graphics_exposures = false;
1277 draw. gc = XCreateGC( DISP, X(owner)->gdrawable, GCGraphicsExposures, &gcv);
1278 draw. c = XX->argb_c;
1279 } else {
1280 draw. gc = guts. menugc;
1281 draw. c = XX->c;
1282 }
1283 #ifdef USE_XFT
1284 if ( kf-> xft) {
1285 char * encoding = ( XX-> type. popup) ?
1286 owner-> popupFont. encoding :
1287 owner-> menuFont. encoding;
1288 draw. xft_map8 = prima_xft_map8( encoding);
1289 draw. xft_drawable = XftDrawCreate( DISP, win,
1290 draw.layered ? guts. argb_visual. visual : guts. visual. visual,
1291 draw.layered ? guts. argbColormap : guts. defaultColormap);
1292 descent = kf-> xft-> descent;
1293 } else
1294 #endif
1295 descent = kf-> fs->max_bounds.descent;
1296
1297 r. x = ev-> xexpose. x;
1298 r. y = ev-> xexpose. y;
1299 r. width = ev-> xexpose. width;
1300 r. height = ev-> xexpose. height;
1301 rgn = XCreateRegion();
1302 XUnionRectWithRegion( &r, rgn, rgn);
1303 XSetRegion( DISP, draw.gc, rgn);
1304 #ifdef USE_XFT
1305 if ( draw. xft_drawable) XftDrawSetClip(( XftDraw*) draw.xft_drawable, rgn);
1306 #endif
1307 CLIP_ARGB_PICTURE(w->argb_picture, rgn);
1308
1309 #ifdef USE_XFT
1310 if ( !kf-> xft)
1311 #endif
1312 XSetFont( DISP, draw.gc, kf-> id);
1313
1314 y = vertical ? 2 : 0;
1315 x = 0;
1316 if ( !( s = malloc( sz))) goto EXIT;
1317 DRAW(background);
1318 while ( m) {
1319 int deltaY = ix-> height;
1320
1321 /* printf("%d %d %d %s\n", last, w-> selected, w-> last, m-> text); */
1322 selected = last == w-> selected;
1323 if ( selected ) {
1324 if ( !m-> flags. custom_draw ) {
1325 Point sz = menu_item_size( XX, w, last);
1326 Point p = menu_item_offset( XX, w, last);
1327 XSetForeground( DISP, draw.gc, draw.c[ciHilite]);
1328 XFillRectangle( DISP, win, draw.gc, p.x, p.y, sz. x, sz.y);
1329 }
1330 clr = draw.c[ciHiliteText];
1331 rgb = XX-> rgb[ciHiliteText];
1332 } else {
1333 clr = draw.c[ciFore];
1334 rgb = XX-> rgb[ciFore];
1335 }
1336
1337 if ( last > w-> last) {
1338 DRAW(guillemots);
1339 break;
1340 }
1341
1342 if ( m-> flags. disabled) {
1343 clr = draw.c[ciDisabledText];
1344 rgb = XX-> rgb[ ciDisabledText];
1345 }
1346
1347 if ( m-> flags. custom_draw ) {
1348 if ( vertical ) {
1349 y += ix-> height;
1350 if (m-> down) DRAW(submenu);
1351 }
1352 DRAW(custom);
1353 if ( !vertical )
1354 x += ix-> width + 2 * MENU_XOFFSET;
1355 } else if ( m-> flags. divider) {
1356 DRAW(divider);
1357 if ( vertical )
1358 y += MENU_ITEM_GAP * 2;
1359 else if ( right > 0) {
1360 x += right;
1361 right = 0;
1362 }
1363 } else {
1364 if ( vertical ) {
1365 /* don't draw check marks on horizontal menus - they look ugly */
1366 if ( m-> icon )
1367 DRAW(icon);
1368 else if ( m-> flags. checked)
1369 DRAW(check);
1370 else if ( m-> flags. autotoggle)
1371 DRAW(checkbox);
1372 }
1373
1374 if ( m-> text) {
1375 if ( !DRAW(text)) goto EXIT;
1376 } else if ( m-> bitmap)
1377 DRAW(bitmap);
1378 if ( !vertical)
1379 x += ix-> width + MENU_XOFFSET;
1380
1381 if ( m-> accel) {
1382 if ( !DRAW(accel))
1383 goto EXIT;
1384 if ( !vertical)
1385 x += ix-> accel_width + MENU_XOFFSET/2;
1386 }
1387 if ( !vertical)
1388 x += MENU_XOFFSET;
1389
1390 if ( vertical && m-> down)
1391 DRAW(submenu);
1392
1393 if ( vertical) y += deltaY;
1394 }
1395 m = m-> next;
1396 ix++;
1397 last++;
1398 }
1399 free(s);
1400 EXIT:;
1401
1402 XDestroyRegion( rgn);
1403 #ifdef USE_XFT
1404 if ( draw. xft_drawable)
1405 XftDrawDestroy( draw. xft_drawable);
1406 #endif
1407 if ( draw. layered ) XFreeGC( DISP, draw. gc);
1408 XFlush(DISP);
1409 }
1410
1411 #undef DRAW
1412
1413 static void
handle_menu_configure(XEvent * ev,XWindow win,Handle self)1414 handle_menu_configure( XEvent *ev, XWindow win, Handle self)
1415 {
1416 DEFMM;
1417 if ( XX-> wstatic. w == win) {
1418 PMenuWindow w = XX-> w;
1419 PMenuItemReg m;
1420 PUnixMenuItem ix;
1421 int x = 0;
1422 int stage = 0;
1423 if ( w-> sz. x == ev-> xconfigure. width &&
1424 w-> sz. y == ev-> xconfigure. height) return;
1425 if ( guts. currentMenu == self) prima_end_menu();
1426 w-> sz. x = ev-> xconfigure. width;
1427 w-> sz. y = ev-> xconfigure. height;
1428 XClearArea( DISP, win, 0, 0, 0, 0, true);
1429
1430 AGAIN:
1431 w-> last = -1;
1432 m = w-> m;
1433 ix = w-> um;
1434 while ( m) {
1435 int dx = 0;
1436 if ( !m-> flags. divider) {
1437 dx += MENU_XOFFSET * 2 + ix-> width;
1438 if ( m-> accel) dx += MENU_XOFFSET / 2 + ix-> accel_width;
1439 }
1440 if ( x + dx >= w-> sz.x) {
1441 if ( stage == 0) { /* now we are sure that >> should be drawn - check again */
1442 x = MENU_XOFFSET * 2 + XX-> guillemots;
1443 stage++;
1444 goto AGAIN;
1445 }
1446 break;
1447 }
1448 x += dx;
1449 w-> last++;
1450 m = m-> next;
1451 ix++;
1452 }
1453 m = w-> m;
1454 ix = w-> um;
1455 w-> right = 0;
1456 if ( w-> last >= w-> num - 1) {
1457 Bool hit = false;
1458 x = 0;
1459 while ( m) {
1460 if ( m-> flags. divider) {
1461 hit = true;
1462 break;
1463 } else {
1464 x += MENU_XOFFSET * 2 + ix-> width;
1465 if ( m-> accel) x += MENU_XOFFSET / 2 + ix-> accel_width;
1466 }
1467 m = m-> next;
1468 ix++;
1469 }
1470 if ( hit) {
1471 w-> right = 0;
1472 while ( m) {
1473 if ( !m-> flags. divider) {
1474 w-> right += MENU_XOFFSET * 2 + ix-> width;
1475 if ( m-> accel) w-> right += MENU_XOFFSET / 2 + ix-> accel_width;
1476 }
1477 m = m-> next;
1478 ix++;
1479 }
1480 w-> right = w-> sz.x - w-> right - x;
1481 }
1482 }
1483 }
1484 }
1485
1486
1487 static void
handle_menu_motion(XEvent * ev,XWindow win,Handle self)1488 handle_menu_motion( XEvent *ev, XWindow win, Handle self)
1489 {
1490 DEFMM;
1491 PMenuItemReg m;
1492 PMenuWindow w;
1493 int px;
1494 if ( guts. currentMenu != self) return;
1495
1496 w = get_menu_window( self, win);
1497 px = menu_point2item( XX, w, ev-> xmotion.x, ev-> xmotion.y, NULL);
1498 menu_select_item( XX, w, px);
1499 m = w-> m;
1500 if ( px >= 0) {
1501 int x = px;
1502 while ( x--) m = m-> next;
1503 if ( px != w-> last + 1) m = m-> down;
1504 if ( !w-> next || w-> next-> m != m) {
1505 apc_timer_set_timeout( MENU_TIMER, (XX-> wstatic. w == win) ? 2 : guts. menu_timeout);
1506 XX-> focused = w;
1507 }
1508 }
1509 while ( w-> next) {
1510 menu_select_item( XX, w-> next, -1);
1511 w = w-> next;
1512 }
1513 }
1514
1515 static void
handle_menu_button(XEvent * ev,XWindow win,Handle self)1516 handle_menu_button( XEvent *ev, XWindow win, Handle self)
1517 {
1518 DEFMM;
1519 int px, first = 0;
1520 PMenuWindow w;
1521 XWindow focus = NULL_HANDLE;
1522 if ( prima_no_input( X(PComponent(self)->owner), false, true)) return;
1523 if ( ev-> xbutton. button != Button1) return;
1524
1525 if ( XX-> wstatic. w == win) {
1526 Handle x = guts. focused, owner = PComponent(self)-> owner;
1527 while ( x && !X(x)-> type. window) x = PComponent( x)-> owner;
1528 if ( x != owner) {
1529 XSetInputFocus( DISP, focus = PComponent( owner)-> handle,
1530 RevertToNone, ev-> xbutton. time);
1531 }
1532 }
1533
1534 if ( !( w = get_menu_window( self, win))) {
1535 prima_end_menu();
1536 return;
1537 }
1538 px = menu_point2item( XX, w, ev-> xbutton. x, ev-> xbutton.y, NULL);
1539 if ( px < 0) {
1540 if ( XX-> wstatic. w == win)
1541 prima_end_menu();
1542 return;
1543 }
1544 if ( guts. currentMenu != self) {
1545 int rev;
1546 if ( ev-> type == ButtonRelease) return;
1547 if ( guts. currentMenu)
1548 prima_end_menu();
1549 if ( focus)
1550 XX-> focus = focus;
1551 else
1552 XGetInputFocus( DISP, &XX-> focus, &rev);
1553 if ( !XX-> type. popup) {
1554 Handle topl = PComponent( self)-> owner;
1555 Handle who = ( Handle) hash_fetch( guts.windows, (void*)&XX-> focus, sizeof(XX-> focus));
1556 while ( who) {
1557 if ( XT_IS_WINDOW(X(who))) {
1558 if ( who != topl) XX-> focus = PComponent( topl)-> handle;
1559 break;
1560 }
1561 who = PComponent( who)-> owner;
1562 }
1563 }
1564 first = 1;
1565 }
1566 XSetInputFocus( DISP, XX-> w-> w, RevertToNone, CurrentTime);
1567 guts. currentMenu = self;
1568 if ( first && ( ev-> type == ButtonPress) && ( !send_cmMenu( self, NULL)))
1569 return;
1570 if ( !first && ( ev-> type == ButtonPress)) return;
1571 apc_timer_stop( MENU_TIMER);
1572 menu_select_item( XX, w, px);
1573 if ( !ev-> xbutton. send_event) {
1574 if ( !menu_enter_item( XX, w, px, ( ev-> type == ButtonPress) ? 0 : 1))
1575 return;
1576 } else
1577 XX-> focused = w;
1578
1579 ev-> xbutton. x += w-> pos. x;
1580 ev-> xbutton. y += w-> pos. y;
1581 if ( w-> next &&
1582 ev-> xbutton. x >= w-> next-> pos.x &&
1583 ev-> xbutton. y >= w-> next-> pos.y &&
1584 ev-> xbutton. x < w-> next-> pos.x + w-> next-> sz.x &&
1585 ev-> xbutton. y < w-> next-> pos.y + w-> next-> sz.y
1586 ) {
1587 /* simulate mouse move, as X are stupid enough to not do it */
1588 int x = ev-> xbutton.x, y = ev-> xbutton. y;
1589 ev-> xmotion. x = x - w-> next-> pos. x;
1590 ev-> xmotion. y = y - w-> next-> pos. y;
1591 win = w-> next-> w;
1592 handle_menu_motion(ev, win, self);
1593 }
1594 }
1595
1596 static void
handle_menu_focus_out(XEvent * ev,XWindow win,Handle self)1597 handle_menu_focus_out( XEvent *ev, XWindow win, Handle self)
1598 {
1599 if ( self != guts. currentMenu) return;
1600
1601 switch ( ev-> xfocus. detail) {
1602 case NotifyVirtual:
1603 case NotifyPointer:
1604 case NotifyPointerRoot:
1605 case NotifyDetailNone:
1606 case NotifyNonlinearVirtual:
1607 return;
1608 }
1609 apc_timer_stop( MENU_UNFOCUS_TIMER);
1610 apc_timer_start( MENU_UNFOCUS_TIMER);
1611 guts. unfocusedMenu = self;
1612 }
1613
1614 static void
handle_menu_focus_in(XEvent * ev,XWindow win,Handle self)1615 handle_menu_focus_in( XEvent *ev, XWindow win, Handle self)
1616 {
1617 if ( guts. unfocusedMenu && self == guts. unfocusedMenu && self == guts. currentMenu) {
1618 switch ( ev-> xfocus. detail) {
1619 case NotifyVirtual:
1620 case NotifyPointer:
1621 case NotifyPointerRoot:
1622 case NotifyDetailNone:
1623 case NotifyNonlinearVirtual:
1624 return;
1625 }
1626 apc_timer_stop( MENU_UNFOCUS_TIMER);
1627 guts. unfocusedMenu = NULL_HANDLE;
1628 }
1629 }
1630
1631 static void
handle_menu_key(XEvent * ev,XWindow win,Handle self)1632 handle_menu_key( XEvent *ev, XWindow win, Handle self)
1633 {
1634 DEFMM;
1635 char str_buf[ 256];
1636 KeySym keysym;
1637 int str_len, d = 0, piles = 0;
1638 PMenuWindow w;
1639 PMenuItemReg m;
1640
1641 str_len = XLookupString( &ev-> xkey, str_buf, 256, &keysym, NULL);
1642 if ( prima_handle_menu_shortcuts( PComponent(self)-> owner, ev, keysym) != 0)
1643 return;
1644
1645 if ( self != guts. currentMenu) return;
1646 apc_timer_stop( MENU_TIMER);
1647 if ( !XX-> focused) return;
1648 /* navigation */
1649 w = XX-> focused;
1650 m = w-> m;
1651 switch (keysym) {
1652 case XK_Left:
1653 case XK_KP_Left:
1654 if ( w == &XX-> wstatic) { /* horizontal menu */
1655 d--;
1656 } else if ( w != XX-> w) { /* not a popup root */
1657 if ( w-> prev) menu_window_delete_downlinks( XX, w-> prev);
1658 if ( w-> prev == &XX-> wstatic) {
1659 d--;
1660 piles = 1;
1661 } else
1662 return;
1663 }
1664 break;
1665 case XK_Right:
1666 case XK_KP_Right:
1667 if ( w == &XX-> wstatic) { /* horizontal menu */
1668 d++;
1669 } else if ( w-> selected >= 0) {
1670 int sel;
1671 sel = w-> selected;
1672 if ( sel >= 0) {
1673 while ( sel--) m = m-> next;
1674 }
1675 if ( m-> down || w-> selected == w-> last + 1) {
1676 if ( menu_enter_item( XX, w, w-> selected, 1) && w-> next)
1677 menu_select_item( XX, w-> next, 0);
1678 } else if ( w-> prev == &XX-> wstatic) {
1679 menu_window_delete_downlinks( XX, XX-> w);
1680 piles = 1;
1681 d++;
1682 } else
1683 return;
1684 }
1685 break;
1686 case XK_Up:
1687 case XK_KP_Up:
1688 if ( w != &XX-> wstatic) d--;
1689 break;
1690 case XK_Down:
1691 case XK_KP_Down:
1692 if ( w == &XX-> wstatic) {
1693 int sel = w-> selected;
1694 if ( sel >= 0) {
1695 while ( sel--) m = m-> next;
1696 }
1697 if ( m-> down || w-> selected == w-> last + 1) {
1698 if ( menu_enter_item( XX, w, w-> selected, 1) && w-> next)
1699 menu_select_item( XX, w-> next, 0);
1700 }
1701 } else
1702 d++;
1703 break;
1704 case XK_KP_Enter:
1705 case XK_Return:
1706 menu_enter_item( XX, w, w-> selected, 1);
1707 return;
1708 case XK_Escape:
1709 if ( w-> prev)
1710 menu_window_delete_downlinks( XX, w-> prev);
1711 else
1712 prima_end_menu();
1713 return;
1714 default:
1715 goto NEXT_STAGE;
1716 }
1717
1718 if ( piles) w = XX-> focused = w-> prev;
1719
1720 if ( d != 0) {
1721 int sel = w-> selected;
1722 PMenuItemReg m;
1723 int z, last = w-> last + (( w-> num == w-> last + 1) ? 0 : 1);
1724
1725 if ( sel < -1) sel = -1;
1726 while ( 1) {
1727 if ( d > 0) {
1728 sel = ( sel >= last) ? 0 : sel + 1;
1729 } else {
1730 sel = ( sel <= 0) ? last : sel - 1;
1731 }
1732 m = w-> m;
1733 z = sel;
1734 while ( z--) m = m-> next;
1735 if ( sel == w-> last + 1 || !m-> flags. divider) {
1736 menu_select_item( XX, w, sel);
1737 menu_window_delete_downlinks( XX, w);
1738 if ( piles) {
1739 if ( menu_enter_item( XX, w, sel, 0) && w-> next)
1740 menu_select_item( XX, w-> next, 0);
1741 }
1742 break;
1743 }
1744 }
1745
1746 }
1747 return;
1748 NEXT_STAGE:
1749
1750 if ( str_len == 1) {
1751 int i;
1752 char c = tolower( str_buf[0]);
1753 for ( i = 0; i <= w-> last; i++) {
1754 if ( m-> text) {
1755 int j = 0;
1756 char * t = m-> text, z = 0;
1757 while ( t[j]) {
1758 if ( t[j] == '~' && t[j+1]) {
1759 if ( t[j+1] == '~')
1760 j += 2;
1761 else {
1762 z = tolower(t[j+1]);
1763 break;
1764 }
1765 }
1766 j++;
1767 }
1768 if ( z == c) {
1769 if ( menu_enter_item( XX, w, i, 1) && w-> next)
1770 menu_select_item( XX, w, i);
1771 return;
1772 }
1773 }
1774 m = m-> next;
1775 }
1776 }
1777 }
1778
1779 static void
handle_menu_timer(XEvent * ev,XWindow win,Handle self)1780 handle_menu_timer( XEvent *ev, XWindow win, Handle self)
1781 {
1782 DEFMM;
1783 PMenuWindow w;
1784 PMenuItemReg m;
1785 int s;
1786 if ( self != guts. currentMenu) return;
1787
1788 if ( !( w = XX-> focused)) return;
1789 m = w-> m;
1790 s = w-> selected;
1791 if ( s < 0) return;
1792 while ( s--) m = m-> next;
1793 if ( m-> down || w-> selected == w-> last + 1)
1794 menu_enter_item( XX, w, w-> selected, 0);
1795 else
1796 menu_window_delete_downlinks( XX, w);
1797 }
1798
1799 void
prima_handle_menu_event(XEvent * ev,XWindow win,Handle self)1800 prima_handle_menu_event( XEvent *ev, XWindow win, Handle self)
1801 {
1802 switch ( ev-> type) {
1803 case Expose:
1804 handle_menu_expose(ev, win, self);
1805 break;
1806 case ConfigureNotify:
1807 handle_menu_configure(ev, win, self);
1808 break;
1809 case ButtonPress:
1810 case ButtonRelease:
1811 handle_menu_button(ev, win, self);
1812 break;
1813 case MotionNotify:
1814 handle_menu_motion(ev, win, self);
1815 break;
1816 case FocusOut:
1817 handle_menu_focus_out(ev, win, self);
1818 break;
1819 case FocusIn:
1820 handle_menu_focus_in(ev, win, self);
1821 break;
1822 case KeyPress:
1823 handle_menu_key(ev, win, self);
1824 break;
1825 case MenuTimerMessage:
1826 handle_menu_timer(ev, win, self);
1827 break;
1828 }
1829 }
1830
1831 /* local menu access hacks; it's good idea to have
1832 hot keys changeable through resources, but have no
1833 idea ( and desire :) how to plough throgh it */
1834 int
prima_handle_menu_shortcuts(Handle self,XEvent * ev,KeySym keysym)1835 prima_handle_menu_shortcuts( Handle self, XEvent * ev, KeySym keysym)
1836 {
1837 int ret = 0;
1838 int mod =
1839 (( ev-> xkey. state & ShiftMask) ? kmShift : 0) |
1840 (( ev-> xkey. state & ControlMask)? kmCtrl : 0) |
1841 (( ev-> xkey. state & Mod1Mask) ? kmAlt : 0);
1842
1843 if ( mod == kmShift && keysym == XK_F9) {
1844 Event e;
1845 bzero( &e, sizeof(e));
1846 e. cmd = cmPopup;
1847 e. gen. B = false;
1848 e. gen. P = apc_pointer_get_pos( application);
1849 e. gen. H = self;
1850 apc_widget_map_points( self, false, 1, &e. gen. P);
1851 CComponent( self)-> message( self, &e);
1852 if ( PObject( self)-> stage == csDead) return -1;
1853 ret = 1;
1854 }
1855
1856 if ( mod == 0 && keysym == XK_F10) {
1857 Handle ps = self;
1858 while ( PComponent( self)-> owner) {
1859 ps = self;
1860 if ( XT_IS_WINDOW(X(self))) break;
1861 self = PComponent( self)-> owner;
1862 }
1863 self = ps;
1864
1865 if ( XT_IS_WINDOW(X(self)) && PWindow(self)-> menu) {
1866 if ( !guts. currentMenu) {
1867 XEvent ev;
1868 bzero( &ev, sizeof( ev));
1869 ev. type = ButtonPress;
1870 ev. xbutton. button = Button1;
1871 ev. xbutton. send_event = true;
1872 prima_handle_menu_event( &ev, M(PWindow(self)-> menu)-> w-> w, PWindow(self)-> menu);
1873 } else
1874 prima_end_menu();
1875 ret = 1;
1876 }
1877 }
1878
1879 if ( !guts. currentMenu && mod == kmAlt) { /* handle menu bar keys */
1880 KeySym keysym;
1881 char str_buf[ 256];
1882 Handle ps = self;
1883
1884 while ( PComponent( self)-> owner) {
1885 ps = self;
1886 if ( XT_IS_WINDOW(X(self))) break;
1887 self = PComponent( self)-> owner;
1888 }
1889 self = ps;
1890
1891 if ( XT_IS_WINDOW(X(self)) && PWindow(self)-> menu &&
1892 1 == XLookupString( &ev-> xkey, str_buf, 256, &keysym, NULL)) {
1893 int i;
1894 PMenuSysData selfxx = M(PWindow(self)-> menu);
1895 char c = tolower( str_buf[0]);
1896 PMenuWindow w = XX-> w;
1897 PMenuItemReg m = w-> m;
1898
1899 for ( i = 0; i <= w-> last; i++) {
1900 if ( m-> text) {
1901 int j = 0;
1902 char * t = m-> text, z = 0;
1903 while ( t[j]) {
1904 if ( t[j] == '~' && t[j+1]) {
1905 if ( t[j+1] == '~')
1906 j += 2;
1907 else {
1908 z = tolower(t[j+1]);
1909 break;
1910 }
1911 }
1912 j++;
1913 }
1914 if ( z == c) {
1915 XEvent ev;
1916 bzero( &ev, sizeof( ev));
1917 ev. type = ButtonPress;
1918 ev. xbutton. button = Button1;
1919 ev. xbutton. send_event = true;
1920 prima_handle_menu_event( &ev, w-> w, PWindow(self)-> menu);
1921 if ( menu_enter_item( XX, w, i, 1) && w-> next)
1922 menu_select_item( XX, w, i);
1923 return 1;
1924 }
1925 }
1926 m = m-> next;
1927 }
1928 }
1929 }
1930 return ret;
1931 }
1932
1933 void
prima_end_menu(void)1934 prima_end_menu(void)
1935 {
1936 PMenuSysData XX;
1937 PMenuWindow w;
1938 apc_timer_stop( MENU_TIMER);
1939 apc_timer_stop( MENU_UNFOCUS_TIMER);
1940 guts. unfocusedMenu = NULL_HANDLE;
1941 if ( !guts. currentMenu) return;
1942 XX = M(guts. currentMenu);
1943 {
1944 XRectangle r;
1945 Region rgn;
1946 unsigned long * c = XX->layered ? XX->argb_c : XX->c;
1947 r. x = 0;
1948 r. y = 0;
1949 r. width = guts. displaySize. x;
1950 r. height = guts. displaySize. y;
1951 rgn = XCreateRegion();
1952 XUnionRectWithRegion( &r, rgn, rgn);
1953 XSetRegion( DISP, guts. menugc, rgn);
1954 XDestroyRegion( rgn);
1955 XSetForeground( DISP, guts. menugc, c[ciBack]);
1956 }
1957 w = XX-> w;
1958 if ( XX-> focus)
1959 XSetInputFocus( DISP, XX-> focus, RevertToNone, CurrentTime);
1960 menu_window_delete_downlinks( XX, XX-> w);
1961 XX-> focus = NULL_HANDLE;
1962 XX-> focused = NULL;
1963 if ( XX-> w != &XX-> wstatic) {
1964 hash_delete( guts. menu_windows, &w-> w, sizeof( w-> w), false);
1965 DELETE_ARGB_PICTURE(w->argb_picture);
1966 XDestroyWindow( DISP, w-> w);
1967 free_unix_items( w);
1968 free( w);
1969 XX-> w = NULL;
1970 } else {
1971 XX-> w-> next = NULL;
1972 menu_select_item( XX, XX-> w, -100);
1973 }
1974 guts. currentMenu = NULL_HANDLE;
1975 XFlush(DISP);
1976 }
1977
1978 static unsigned long
argb_color(Color color)1979 argb_color(Color color)
1980 {
1981 int a[3];
1982
1983 a[0] = COLOR_R(color);
1984 a[1] = COLOR_G(color);
1985 a[2] = COLOR_B(color);
1986
1987 return
1988 (((a[0] << guts. argb_bits. red_range ) >> 8) << guts. argb_bits. red_shift) |
1989 (((a[1] << guts. argb_bits. green_range) >> 8) << guts. argb_bits. green_shift) |
1990 (((a[2] << guts. argb_bits. blue_range ) >> 8) << guts. argb_bits. blue_shift) |
1991 (((0xff << guts. argb_bits. alpha_range ) >> 8) << guts. argb_bits. alpha_shift);
1992 }
1993
1994 Bool
apc_menu_create(Handle self,Handle owner)1995 apc_menu_create( Handle self, Handle owner)
1996 {
1997 DEFMM;
1998 int i;
1999 apc_menu_destroy( self);
2000 XX-> type.menu = true;
2001 XX-> w = &XX-> wstatic;
2002 XX-> w-> self = self;
2003 XX-> w-> m = TREE;
2004 XX-> w-> first = 0;
2005 XX-> w-> sz.x = 0;
2006 XX-> w-> sz.y = 0;
2007 for ( i = 0; i <= ciMaxId; i++)
2008 XX-> c[i] = prima_allocate_color(
2009 NULL_HANDLE,
2010 prima_map_color( PWindow(owner)-> menuColor[i], NULL),
2011 NULL);
2012 XX-> layered = X(owner)->flags. layered;
2013 if ( XX-> layered ) {
2014 for ( i = 0; i <= ciMaxId; i++)
2015 XX-> argb_c[i] = argb_color(
2016 prima_map_color( PWindow(owner)-> menuColor[i], NULL)
2017 );
2018 }
2019 apc_menu_set_font( self, &PWindow(owner)-> menuFont);
2020 return true;
2021 }
2022
2023 Bool
apc_menu_destroy(Handle self)2024 apc_menu_destroy( Handle self)
2025 {
2026 if ( guts. currentMenu == self) prima_end_menu();
2027 return true;
2028 }
2029
2030 PFont
apc_menu_default_font(PFont f)2031 apc_menu_default_font( PFont f)
2032 {
2033 memcpy( f, &guts. default_menu_font, sizeof( Font));
2034 return f;
2035 }
2036
2037 Color
apc_menu_get_color(Handle self,int index)2038 apc_menu_get_color( Handle self, int index)
2039 {
2040 Color c;
2041 if ( index < 0 || index > ciMaxId) return clInvalid;
2042 c = M(self)-> c[index];
2043 if ( guts. palSize > 0)
2044 return guts. palette[c]. composite;
2045 return
2046 ((((c & guts. visual. blue_mask) >> guts. screen_bits. blue_shift) << 8) >> guts. screen_bits. blue_range) |
2047 (((((c & guts. visual. green_mask) >> guts. screen_bits. green_shift) << 8) >> guts. screen_bits. green_range) << 8) |
2048 (((((c & guts. visual. red_mask) >> guts. screen_bits. red_shift) << 8) >> guts. screen_bits. red_range) << 16);
2049 }
2050
2051 PFont
apc_menu_get_font(Handle self,PFont font)2052 apc_menu_get_font( Handle self, PFont font)
2053 {
2054 DEFMM;
2055 if ( !XX-> font)
2056 return apc_menu_default_font( font);
2057 memcpy( font, &XX-> font-> font, sizeof( Font));
2058 return font;
2059 }
2060
2061 Bool
apc_menu_set_color(Handle self,Color color,int i)2062 apc_menu_set_color( Handle self, Color color, int i)
2063 {
2064 DEFMM;
2065 if ( i < 0 || i > ciMaxId) return false;
2066 XX-> rgb[i] = prima_map_color( color, NULL);
2067 if ( !XX-> type.popup) {
2068 if ( X(PWidget(self)-> owner)-> menuColorImmunity) {
2069 X(PWidget(self)-> owner)-> menuColorImmunity--;
2070 return true;
2071 }
2072 if ( X_WINDOW) {
2073 prima_palette_replace( PWidget(self)-> owner, false);
2074 if ( !XX-> paint_pending) {
2075 XClearArea( DISP, X_WINDOW, 0, 0, XX-> w-> sz.x, XX-> w-> sz.y, true);
2076 XX-> paint_pending = true;
2077 }
2078 }
2079 } else {
2080 XX-> c[i] = prima_allocate_color( NULL_HANDLE, XX-> rgb[i], NULL);
2081 if ( XX-> layered )
2082 XX-> argb_c[i] = argb_color( prima_map_color( XX->rgb[i], NULL));
2083 }
2084 return true;
2085 }
2086
2087 /* apc_menu_set_font is in apc_font.c */
2088
2089 void
menu_touch(Handle self,PMenuItemReg who,Bool kill)2090 menu_touch( Handle self, PMenuItemReg who, Bool kill)
2091 {
2092 DEFMM;
2093 PMenuWindow w, lw = NULL;
2094
2095 if ( guts. currentMenu != self) return;
2096
2097 w = XX-> w;
2098 while ( w) {
2099 if ( w-> m == who) {
2100 if ( kill || lw == NULL)
2101 prima_end_menu();
2102 else
2103 menu_window_delete_downlinks( M(self), lw);
2104 return;
2105 }
2106 lw = w;
2107 w = w-> next;
2108 }
2109 }
2110
2111 static void
menu_reconfigure(Handle self)2112 menu_reconfigure( Handle self)
2113 {
2114 XEvent ev;
2115 DEFMM;
2116 ev. type = ConfigureNotify;
2117 ev. xconfigure. width = X(PComponent(self)-> owner)-> size.x;
2118 ev. xconfigure. height = X(PComponent(self)-> owner)-> size.y;
2119 XX-> w-> sz. x = ev. xconfigure. width - 1; /* force cache flush */
2120 prima_handle_menu_event( &ev, PMenu(self)-> handle, self);
2121 }
2122
2123 static void
menubar_repaint(Handle self)2124 menubar_repaint( Handle self)
2125 {
2126 DEFMM;
2127 if ( !XT_IS_POPUP(XX) && XX-> w == &XX-> wstatic && PMenu(self)-> handle) {
2128 XClearArea( DISP, X_WINDOW, 0, 0, XX-> w-> sz.x, XX-> w-> sz.y, true);
2129 XX-> paint_pending = true;
2130 }
2131 }
2132
2133 static void
menu_update_item(Handle self,PMenuItemReg m)2134 menu_update_item( Handle self, PMenuItemReg m)
2135 {
2136 DEFMM;
2137 PUnixMenuItem ix;
2138 PMenuWindow w;
2139 PMenuItemReg mm;
2140 Handle owner;
2141 Bool layered;
2142
2143 if ( !PMenu(self)-> handle) return; /* horizontal updates only */
2144
2145 w = XX-> w;
2146 ix = w-> um;
2147 mm = w->m;
2148 owner = PComponent( w-> self)-> owner;
2149 layered = X(owner)->flags. layered;
2150 while (mm) {
2151 int w, h;
2152 if ( mm != m ) {
2153 ix++;
2154 mm = mm->next;
2155 continue;
2156 }
2157 kill_menu_bitmap( &ix->icon);
2158 kill_menu_bitmap( &ix->bitmap);
2159 create_menu_bitmap(m->bitmap, &ix->bitmap, layered, m->flags.disabled, &w, &h);
2160 create_menu_bitmap(m->icon, &ix->icon, layered, m->flags.disabled, &w, &h);
2161 break;
2162 }
2163 }
2164
2165 Bool
apc_menu_update(Handle self,PMenuItemReg oldBranch,PMenuItemReg newBranch)2166 apc_menu_update( Handle self, PMenuItemReg oldBranch, PMenuItemReg newBranch)
2167 {
2168 DEFMM;
2169 if ( !XT_IS_POPUP(XX) && XX-> w-> m == oldBranch) {
2170 if ( guts. currentMenu == self) prima_end_menu();
2171 XX-> w-> m = newBranch;
2172 if ( PMenu(self)-> handle) {
2173 update_menu_window( XX, XX-> w);
2174 menu_reconfigure( self);
2175 XClearArea( DISP, X_WINDOW, 0, 0, XX-> w-> sz.x, XX-> w-> sz.y, true);
2176 XX-> paint_pending = true;
2177 }
2178 }
2179 menu_touch( self, oldBranch, true);
2180 return true;
2181 }
2182
2183 Bool
apc_menu_item_delete(Handle self,PMenuItemReg m)2184 apc_menu_item_delete( Handle self, PMenuItemReg m)
2185 {
2186 DEFMM;
2187 if ( !XT_IS_POPUP(XX) && XX-> w-> m == m) {
2188 if ( guts. currentMenu == self) prima_end_menu();
2189 XX-> w-> m = TREE;
2190 if ( PMenu(self)-> handle) {
2191 update_menu_window( XX, XX-> w);
2192 menu_reconfigure( self);
2193 XClearArea( DISP, X_WINDOW, 0, 0, XX-> w-> sz.x, XX-> w-> sz.y, true);
2194 XX-> paint_pending = true;
2195 }
2196 }
2197 menu_touch( self, m, true);
2198 return true;
2199 }
2200
2201 Bool
apc_menu_item_set_accel(Handle self,PMenuItemReg m)2202 apc_menu_item_set_accel( Handle self, PMenuItemReg m)
2203 {
2204 menu_touch( self, m, false);
2205 return true;
2206 }
2207
2208 Bool
apc_menu_item_set_autotoggle(Handle self,PMenuItemReg m)2209 apc_menu_item_set_autotoggle( Handle self, PMenuItemReg m)
2210 {
2211 menu_touch( self, m, false);
2212 return true;
2213 }
2214
2215 Bool
apc_menu_item_set_check(Handle self,PMenuItemReg m)2216 apc_menu_item_set_check( Handle self, PMenuItemReg m)
2217 {
2218 menu_touch( self, m, false);
2219 return true;
2220 }
2221
2222 Bool
apc_menu_item_set_enabled(Handle self,PMenuItemReg m)2223 apc_menu_item_set_enabled( Handle self, PMenuItemReg m)
2224 {
2225 menu_touch( self, m, false);
2226 menu_update_item(self, m);
2227 menubar_repaint( self);
2228 return true;
2229 }
2230
2231 Bool
apc_menu_item_set_icon(Handle self,PMenuItemReg m)2232 apc_menu_item_set_icon( Handle self, PMenuItemReg m)
2233 {
2234 menu_touch( self, m, false);
2235 menu_update_item(self, m);
2236 menubar_repaint( self);
2237 return true;
2238 }
2239
2240
2241 Bool
apc_menu_item_set_image(Handle self,PMenuItemReg m)2242 apc_menu_item_set_image( Handle self, PMenuItemReg m)
2243 {
2244 menu_touch( self, m, false);
2245 menu_update_item(self, m);
2246 menubar_repaint( self);
2247 return true;
2248 }
2249
2250 Bool
apc_menu_item_set_key(Handle self,PMenuItemReg m)2251 apc_menu_item_set_key( Handle self, PMenuItemReg m)
2252 {
2253 menu_touch( self, m, false);
2254 return true;
2255 }
2256
2257 Bool
apc_menu_item_set_text(Handle self,PMenuItemReg m)2258 apc_menu_item_set_text( Handle self, PMenuItemReg m)
2259 {
2260 menu_touch( self, m, false);
2261 menubar_repaint( self);
2262 return true;
2263 }
2264
2265 ApiHandle
apc_menu_get_handle(Handle self)2266 apc_menu_get_handle( Handle self)
2267 {
2268 return NULL_HANDLE;
2269 }
2270
2271 Bool
apc_popup_create(Handle self,Handle owner)2272 apc_popup_create( Handle self, Handle owner)
2273 {
2274 DEFMM;
2275 apc_menu_destroy( self);
2276 XX-> type.menu = true;
2277 XX-> type.popup = true;
2278 return true;
2279 }
2280
2281 PFont
apc_popup_default_font(PFont f)2282 apc_popup_default_font( PFont f)
2283 {
2284 memcpy( f, &guts. default_menu_font, sizeof( Font));
2285 return f;
2286 }
2287
2288 Bool
apc_popup(Handle self,int x,int y,Rect * anchor)2289 apc_popup( Handle self, int x, int y, Rect *anchor)
2290 {
2291 DEFMM;
2292 PMenuItemReg m;
2293 PMenuWindow w;
2294 XWindow dummy;
2295 PDrawableSysData owner;
2296 int dx, dy;
2297
2298 prima_end_menu();
2299 if (!(m=TREE)) return false;
2300 guts. currentMenu = self;
2301 if ( !send_cmMenu( self, NULL)) return false;
2302 if (!(w = get_window(self,m))) return false;
2303 update_menu_window(XX, w);
2304 if ( anchor-> left == 0 && anchor-> right == 0 && anchor-> top == 0 && anchor-> bottom == 0) {
2305 anchor-> left = anchor-> right = x;
2306 anchor-> top = anchor-> bottom = y;
2307 } else {
2308 if ( x < anchor-> left) anchor-> left = x;
2309 if ( x > anchor-> right) anchor-> right = x;
2310 if ( y < anchor-> bottom) anchor-> bottom = y;
2311 if ( y > anchor-> top) anchor-> top = y;
2312 }
2313 owner = X(PComponent(self)->owner);
2314 y = owner-> size. y - y;
2315 anchor-> bottom = owner-> size. y - anchor-> bottom;
2316 anchor-> top = owner-> size. y - anchor-> top;
2317 dx = dy = 0;
2318 XTranslateCoordinates( DISP, owner->udrawable, guts. root, dx, dy, &dx, &dy, &dummy);
2319 x += dx;
2320 y += dy;
2321 anchor-> left += dx;
2322 anchor-> right += dx;
2323 anchor-> top += dy;
2324 anchor-> bottom += dy;
2325 if ( anchor-> bottom + w-> sz.y <= guts. displaySize.y)
2326 y = anchor-> bottom;
2327 else if ( w-> sz. y < anchor-> top)
2328 y = anchor-> top - w-> sz. y;
2329 else
2330 y = 0;
2331 if ( anchor-> right + w-> sz.x <= guts. displaySize.x)
2332 x = anchor-> right;
2333 else if ( w-> sz.x < anchor-> left)
2334 x = anchor-> left - w-> sz. x;
2335 else
2336 x = 0;
2337 w-> pos. x = x;
2338 w-> pos. y = y;
2339 XX-> focused = w;
2340 XGetInputFocus( DISP, &XX-> focus, &dx);
2341 XMoveWindow( DISP, w->w, x, y);
2342 XMapRaised( DISP, w->w);
2343 XSetInputFocus( DISP, w->w, RevertToNone, CurrentTime);
2344 XFlush( DISP);
2345 XCHECKPOINT;
2346 return true;
2347 }
2348
2349 Bool
apc_window_set_menu(Handle self,Handle menu)2350 apc_window_set_menu( Handle self, Handle menu)
2351 {
2352 DEFXX;
2353 int y = XX-> menuHeight;
2354 Bool repal = false;
2355
2356 if ( XX-> menuHeight > 0) {
2357 PMenu m = ( PMenu) PWindow( self)-> menu;
2358 PMenuWindow w = M(m)-> w;
2359 if ( m-> handle == guts. currentMenu) prima_end_menu();
2360 hash_delete( guts. menu_windows, &w-> w, sizeof( w-> w), false);
2361 DELETE_ARGB_PICTURE(w->argb_picture);
2362 XDestroyWindow( DISP, w-> w);
2363 free_unix_items( w);
2364 m-> handle = NULL_HANDLE;
2365 M(m)-> paint_pending = false;
2366 M(m)-> focused = NULL;
2367 y = 0;
2368 repal = true;
2369 }
2370
2371 if ( menu) {
2372 PMenu m = ( PMenu) menu;
2373 XSetWindowAttributes attrs;
2374 unsigned long valuemask;
2375 attrs. event_mask = KeyPressMask | ButtonPressMask | ButtonReleaseMask
2376 | EnterWindowMask | LeaveWindowMask | ButtonMotionMask | ExposureMask
2377 | StructureNotifyMask | FocusChangeMask | OwnerGrabButtonMask
2378 | PointerMotionMask;
2379 attrs. do_not_propagate_mask = attrs. event_mask;
2380 attrs. win_gravity = NorthWestGravity;
2381 valuemask = CWWinGravity| CWEventMask;
2382 if ( XF_LAYERED(XX)) {
2383 valuemask |= CWBackPixel | CWBorderPixel;
2384 attrs. background_pixel = 0xFFFFFFFF;
2385 attrs. border_pixel = 0xFFFFFFFF;
2386 }
2387 y = PWindow(self)-> menuFont. height + MENU_ITEM_GAP * 2;
2388 M(m)-> w-> w = m-> handle = XCreateWindow( DISP, X_WINDOW,
2389 0, 0, 1, 1, 0, CopyFromParent,
2390 InputOutput, CopyFromParent, valuemask, &attrs);
2391 CREATE_ARGB_PICTURE(M(m)->w->w, 0, M(m)->w->argb_picture);
2392 hash_store( guts. menu_windows, &m-> handle, sizeof( m-> handle), m);
2393 XResizeWindow( DISP, m-> handle, XX-> size.x, y);
2394 XMapRaised( DISP, m-> handle);
2395 M(m)-> paint_pending = true;
2396 M(m)-> focused = NULL;
2397 update_menu_window(M(m), M(m)-> w);
2398 menu_reconfigure( menu);
2399 repal = true;
2400 /* make it immune to necessary color change calls */
2401 XX-> menuColorImmunity = ciMaxId + 1;
2402 }
2403 prima_window_reset_menu( self, y);
2404 if ( repal) prima_palette_replace( self, false);
2405 if ( menu) {
2406 int i;
2407 Bool layered = XF_LAYERED(XX);
2408 XX->flags.layered = false; /* we need non-layered colors for popup menus */
2409 for ( i = 0; i <= ciMaxId; i++) {
2410 M(menu)-> c[i] = prima_allocate_color( self,
2411 prima_map_color( PWindow(self)-> menuColor[i], NULL), NULL);
2412 }
2413 i = MENU_PALETTE_SIZE - 1;
2414 M(menu)-> c[i] = prima_allocate_color( self, 0x404040, NULL);
2415 XX->flags.layered = layered;
2416 M(menu)-> layered = XX->flags. layered;
2417 if ( M(menu)-> layered ) {
2418 for ( i = 0; i <= ciMaxId; i++)
2419 M(menu)-> argb_c[i] = argb_color(
2420 prima_map_color( PWindow(self)-> menuColor[i], NULL)
2421 );
2422 i = MENU_PALETTE_SIZE - 1;
2423 M(menu)-> argb_c[i] = prima_allocate_color( self, 0x404040, NULL);
2424 }
2425 }
2426 return true;
2427 }
2428
2429 Bool
apc_menu_item_begin_paint(Handle self,PEvent event)2430 apc_menu_item_begin_paint( Handle self, PEvent event)
2431 {
2432 PaintEvent * pe = (PaintEvent*) event-> gen.p;
2433 PDrawableSysData YY = X(event->gen.H);
2434
2435 YY-> type.drawable = 1;
2436 YY-> type.widget = 1;
2437 YY-> flags.paint = 1;
2438 YY-> flags.layered = pe->layered;
2439 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
2440 YY-> argb_picture = M(pe->self)->w->argb_picture;
2441 #endif
2442 YY-> gdrawable = pe->win;
2443 YY-> size = event-> gen.P;
2444 YY-> visual = pe->layered ? &guts. argb_visual : &guts. visual;
2445 YY-> colormap = pe->layered ? guts. argbColormap : guts. defaultColormap;
2446 prima_prepare_drawable_for_painting(event-> gen.H, false );
2447
2448 return true;
2449
2450 }
2451
2452 Bool
apc_menu_item_end_paint(Handle self,PEvent event)2453 apc_menu_item_end_paint( Handle self, PEvent event)
2454 {
2455 prima_cleanup_drawable_after_painting(event->gen.H);
2456 return true;
2457 }
2458