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