1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 
16 /* ---------------------------- included header files ---------------------- */
17 
18 #include "config.h"
19 
20 #include <stdio.h>
21 
22 #include "libs/fvwmlib.h"
23 #include "libs/Picture.h"
24 #include "libs/Graphics.h"
25 #include "libs/PictureGraphics.h"
26 #include "libs/Rectangles.h"
27 #include "fvwm.h"
28 #include "externs.h"
29 #include "execcontext.h"
30 #include "misc.h"
31 #include "screen.h"
32 #include "menudim.h"
33 #include "menustyle.h"
34 #include "menuitem.h"
35 #include "decorations.h"
36 
37 /* ---------------------------- local definitions -------------------------- */
38 
39 /* ---------------------------- local macros ------------------------------- */
40 
41 /* ---------------------------- imports ------------------------------------ */
42 
43 /* ---------------------------- included code files ------------------------ */
44 
45 /* ---------------------------- local types -------------------------------- */
46 
47 /* ---------------------------- forward declarations ----------------------- */
48 
49 /* ---------------------------- local variables ---------------------------- */
50 
51 /* ---------------------------- exported variables (globals) --------------- */
52 
53 /* ---------------------------- local functions ---------------------------- */
clear_menu_item_background(MenuPaintItemParameters * mpip,int x,int y,int w,int h)54 static void clear_menu_item_background(
55 	MenuPaintItemParameters *mpip, int x, int y, int w, int h)
56 {
57 	MenuStyle *ms = mpip->ms;
58 
59 	if (!ST_HAS_MENU_CSET(ms) &&
60 	    ST_FACE(ms).type == GradientMenu &&
61 	    (ST_FACE(ms).gradient_type == D_GRADIENT ||
62 	     ST_FACE(ms).gradient_type == B_GRADIENT))
63 	{
64 		XEvent e;
65 
66 		e.xexpose.x = x;
67 		e.xexpose.y = y;
68 		e.xexpose.width = w;
69 		e.xexpose.height = h;
70 		mpip->cb_reset_bg(mpip->cb_mr, &e);
71 	}
72 	else
73 	{
74 		XClearArea(dpy, mpip->w, x, y, w, h, False);
75 	}
76 }
77 
78 /*
79  *
80  *  Draws two horizontal lines to form a separator
81  *
82  */
draw_separator(Window w,GC TopGC,GC BottomGC,int x1,int y,int x2)83 static void draw_separator(
84 	Window w, GC TopGC, GC BottomGC, int x1, int y, int x2)
85 {
86 	XDrawLine(dpy, w, TopGC   , x1,   y,   x2,   y);
87 	XDrawLine(dpy, w, BottomGC, x1-1, y+1, x2+1, y+1);
88 
89 	return;
90 }
91 
92 /*
93  *
94  *  Draws a tear off bar.  Similar to a separator, but with a dashed line.
95  *
96  */
draw_tear_off_bar(Window w,GC TopGC,GC BottomGC,int x1,int y,int x2)97 static void draw_tear_off_bar(
98 	Window w, GC TopGC, GC BottomGC, int x1, int y, int x2)
99 {
100 	XGCValues xgcv;
101 	int width;
102 	int offset;
103 
104 	xgcv.line_style = LineOnOffDash;
105 	xgcv.dashes = MENU_TEAR_OFF_BAR_DASH_WIDTH;
106 	XChangeGC(dpy, TopGC, GCLineStyle | GCDashList, &xgcv);
107 	XChangeGC(dpy, BottomGC, GCLineStyle | GCDashList, &xgcv);
108 	width = (x2 - x1 + 1);
109 	offset = (width / MENU_TEAR_OFF_BAR_DASH_WIDTH) *
110 		MENU_TEAR_OFF_BAR_DASH_WIDTH;
111 	offset = (width - offset) / 2;
112 	x1 += offset;
113 	x2 += offset;
114 	XDrawLine(dpy, w, TopGC,    x1, y,     x2, y);
115 	XDrawLine(dpy, w, BottomGC, x1, y + 1, x2, y + 1);
116 	xgcv.line_style = LineSolid;
117 	XChangeGC(dpy, TopGC, GCLineStyle, &xgcv);
118 	XChangeGC(dpy, BottomGC, GCLineStyle, &xgcv);
119 
120 	return;
121 }
122 
draw_highlight_background(struct MenuPaintItemParameters * mpip,int x,int y,int width,int height,colorset_t * cs,GC gc)123 static void draw_highlight_background(
124 	struct MenuPaintItemParameters *mpip, int x, int y, int width,
125 	int height, colorset_t *cs, GC gc)
126 {
127 	if (cs != NULL && cs->pixmap && cs->pixmap_type != PIXMAP_TILED)
128 	{
129 		Pixmap p;
130 
131 		p = CreateOffsetBackgroundPixmap(
132 			dpy, mpip->w, 0, 0, width, height, cs, Pdepth, gc,
133 			False);
134 		switch (cs->pixmap_type)
135 		{
136 		case PIXMAP_STRETCH_X:
137 			/* todo: optimize to only create one pixmap and gc per
138 			 * mr. */
139 		case PIXMAP_STRETCH_Y:
140 		{
141 			XGCValues gcv;
142 			int gcm;
143 			GC bgc;
144 
145 			gcv.tile = p;
146 			gcv.fill_style = FillTiled;
147 			gcm = GCFillStyle | GCTile;
148 
149 			/* vertcal gradients has to be aligned properly */
150 			if (cs->pixmap_type == PIXMAP_STRETCH_Y)
151 			{
152 				gcv.ts_y_origin = y;
153 				gcm|=GCTileStipYOrigin;
154 			}
155 			else if (cs->pixmap_type == PIXMAP_STRETCH_X)
156 			{
157 				gcv.ts_x_origin = x;
158 				gcm|=GCTileStipXOrigin;
159 			}
160 			bgc = fvwmlib_XCreateGC(dpy, mpip->w, gcm, &gcv);
161 			XFillRectangle(dpy, mpip->w, bgc, x, y, width, height);
162 			XFreeGC(dpy, bgc);
163 			break;
164 		}
165 		default:
166 			XCopyArea(dpy, p, mpip->w, gc, 0, 0, width, height,
167 				  x, y);
168 			break;
169 		}
170 		XFreePixmap(dpy, p);
171 	}
172 	else
173 	{
174 		XFillRectangle(dpy, mpip->w, gc, x, y, width, height);
175 	}
176 }
177 
178 /* ---------------------------- interface functions ------------------------ */
179 
180 /* Allocates a new, empty menu item */
menuitem_create(void)181 struct MenuItem *menuitem_create(void)
182 {
183 	MenuItem *mi;
184 
185 	mi = (MenuItem *)safemalloc(sizeof(MenuItem));
186 	memset(mi, 0, sizeof(MenuItem));
187 
188 	return mi;
189 }
190 
191 /* Frees a menu item and all of its allocated resources. */
menuitem_free(struct MenuItem * mi)192 void menuitem_free(struct MenuItem *mi)
193 {
194 	int i;
195 
196 	if (!mi)
197 	{
198 		return;
199 	}
200 	for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
201 	{
202 		if (MI_LABEL(mi)[i] != NULL)
203 		{
204 			free(MI_LABEL(mi)[i]);
205 		}
206 	}
207 	if (MI_ACTION(mi) != NULL)
208 	{
209 		free(MI_ACTION(mi));
210 	}
211 	if (MI_PICTURE(mi))
212 	{
213 		PDestroyFvwmPicture(dpy, MI_PICTURE(mi));
214 	}
215 	for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
216 	{
217 		if (MI_MINI_ICON(mi)[i])
218 		{
219 			PDestroyFvwmPicture(dpy, MI_MINI_ICON(mi)[i]);
220 		}
221 	}
222 	free(mi);
223 
224 	return;
225 }
226 
227 /* Duplicate a menu item into newly allocated memory.  The new item is
228  * completely independent of the old one. */
menuitem_clone(struct MenuItem * mi)229 struct MenuItem *menuitem_clone(struct MenuItem *mi)
230 {
231 	MenuItem *new_mi;
232 	int i;
233 
234 	/* copy everything */
235 	new_mi = (MenuItem *)safemalloc(sizeof(MenuItem));
236 	memcpy(new_mi, mi, sizeof(MenuItem));
237 	/* special treatment for a few parts */
238 	MI_NEXT_ITEM(new_mi) = NULL;
239 	MI_PREV_ITEM(new_mi) = NULL;
240 	MI_WAS_DESELECTED(new_mi) = 0;
241 	if (MI_ACTION(mi) != NULL)
242 	{
243 		MI_ACTION(new_mi) = safestrdup(MI_ACTION(mi));
244 	}
245 	for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
246 	{
247 		if (MI_LABEL(mi)[i] != NULL)
248 		{
249 			MI_LABEL(new_mi)[i] = strdup(MI_LABEL(mi)[i]);
250 		}
251 	}
252 	if (MI_PICTURE(mi) != NULL)
253 	{
254 		MI_PICTURE(new_mi) = PCloneFvwmPicture(MI_PICTURE(mi));
255 	}
256 	for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
257 	{
258 		if (MI_MINI_ICON(mi)[i] != NULL)
259 		{
260 			MI_MINI_ICON(new_mi)[i] =
261 				PCloneFvwmPicture(MI_MINI_ICON(mi)[i]);
262 		}
263 	}
264 
265 	return new_mi;
266 }
267 
268 /* Calculate the size of the various parts of the item.  The sizes are returned
269  * through mipst. */
menuitem_get_size(struct MenuItem * mi,struct MenuItemPartSizesT * mipst,FlocaleFont * font,Bool do_reverse_icon_order)270 void menuitem_get_size(
271 	struct MenuItem *mi, struct MenuItemPartSizesT *mipst,
272 	FlocaleFont *font, Bool do_reverse_icon_order)
273 {
274 	int i;
275 	int j;
276 	int w;
277 
278 	memset(mipst, 0, sizeof(MenuItemPartSizesT));
279 	if (MI_IS_POPUP(mi))
280 	{
281 		mipst->triangle_width = MENU_TRIANGLE_WIDTH;
282 	}
283 	else if (MI_IS_TITLE(mi) && !MI_HAS_PICTURE(mi))
284 	{
285 		Bool is_formatted = False;
286 
287 		/* titles stretch over the whole menu width, so count the
288 		 * maximum separately if the title is unformatted. */
289 		for (j = 1; j < MAX_MENU_ITEM_LABELS; j++)
290 		{
291 			if (MI_LABEL(mi)[j] != NULL)
292 			{
293 				is_formatted = True;
294 				break;
295 			}
296 			else
297 			{
298 				MI_LABEL_OFFSET(mi)[j] = 0;
299 			}
300 		}
301 		if (!is_formatted && MI_LABEL(mi)[0] != NULL)
302 		{
303 			MI_LABEL_STRLEN(mi)[0] =
304 				strlen(MI_LABEL(mi)[0]);
305 			w = FlocaleTextWidth(
306 				font, MI_LABEL(mi)[0], MI_LABEL_STRLEN(mi)[0]);
307 			MI_LABEL_OFFSET(mi)[0] = w;
308 			MI_IS_TITLE_CENTERED(mi) = True;
309 			if (mipst->title_width < w)
310 			{
311 				mipst->title_width = w;
312 			}
313 			return;
314 		}
315 	}
316 	/* regular item or formatted title */
317 	for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
318 	{
319 		if (MI_LABEL(mi)[i])
320 		{
321 			MI_LABEL_STRLEN(mi)[i] = strlen(MI_LABEL(mi)[i]);
322 			w = FlocaleTextWidth(
323 				font, MI_LABEL(mi)[i], MI_LABEL_STRLEN(mi)[i]);
324 			MI_LABEL_OFFSET(mi)[i] = w;
325 			if (mipst->label_width[i] < w)
326 			{
327 				mipst->label_width[i] = w;
328 			}
329 		}
330 	}
331 	if (MI_PICTURE(mi) && mipst->picture_width < MI_PICTURE(mi)->width)
332 	{
333 		mipst->picture_width = MI_PICTURE(mi)->width;
334 	}
335 	for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
336 	{
337 		if (MI_MINI_ICON(mi)[i])
338 		{
339 			int k;
340 
341 			/* Reverse mini icon order for left submenu style. */
342 			k = (do_reverse_icon_order == True) ?
343 				MAX_MENU_ITEM_MINI_ICONS - 1 - i : i;
344 			mipst->icon_width[k] = MI_MINI_ICON(mi)[i]->width;
345 		}
346 	}
347 
348 	return;
349 }
350 
351 /*
352  *
353  *  Procedure:
354  *      menuitem_paint - draws a single entry in a popped up menu
355  *
356  *      mr - the menu instance that holds the menu item
357  *      mi - the menu item to redraw
358  *      fw - the FvwmWindow structure to check against allowed functions
359  *
360  */
menuitem_paint(struct MenuItem * mi,struct MenuPaintItemParameters * mpip)361 void menuitem_paint(
362 	struct MenuItem *mi, struct MenuPaintItemParameters *mpip)
363 {
364 	struct MenuStyle *ms = mpip->ms;
365 	struct MenuDimensions *dim = mpip->dim;
366 
367 	static FlocaleWinString *fws = NULL;
368 	int y_offset;
369 	int text_y;
370 	int y_height;
371 	int x;
372 	int y;
373 	int lit_x_start;
374 	int lit_x_end;
375 	gc_quad_t gcs;
376 	gc_quad_t off_gcs;
377 	int cs = -1;
378 	int off_cs;
379 	FvwmRenderAttributes fra;
380 	/*Pixel fg, fgsh;*/
381 	int relief_thickness = ST_RELIEF_THICKNESS(ms);
382 	Bool is_item_selected;
383 	Bool item_cleared = False;
384 	Bool xft_clear = False;
385 	Bool empty_inter = False;
386 	XRectangle b;
387 	Region region = None;
388 	int i;
389 	int sx1;
390 	int sx2;
391 	FlocaleFont* font;
392 
393 	if (!mi)
394 	{
395 		return;
396 	}
397 	is_item_selected = (mi == mpip->selected_item);
398 
399 	if (MI_IS_TITLE(mi))
400 	{
401 		font = ST_PTITLEFONT(ms);
402 	}
403 	else
404 	{
405 		font = ST_PSTDFONT(ms);
406 	}
407 
408 	y_offset = MI_Y_OFFSET(mi);
409 	y_height = MI_HEIGHT(mi);
410 	if (MI_IS_SELECTABLE(mi))
411 	{
412 		text_y = y_offset + MDIM_ITEM_TEXT_Y_OFFSET(*dim);
413 	}
414 	else
415 	{
416 		text_y = y_offset + font->ascent +
417 			ST_TITLE_GAP_ABOVE(ms);
418 	}
419 	/* center text vertically if the pixmap is taller */
420 	if (MI_PICTURE(mi))
421 	{
422 		text_y += MI_PICTURE(mi)->height;
423 	}
424 	for (i = 0; i < mpip->used_mini_icons; i++)
425 	{
426 		y = 0;
427 		if (MI_MINI_ICON(mi)[i])
428 		{
429 			if (MI_MINI_ICON(mi)[i]->height > y)
430 			{
431 				y = MI_MINI_ICON(mi)[i]->height;
432 			}
433 		}
434 		y -= font->height;
435 		if (y > 1)
436 		{
437 			text_y += y / 2;
438 		}
439 	}
440 
441 	off_cs = ST_HAS_MENU_CSET(ms) ? ST_CSET_MENU(ms) : -1;
442 	/* Note: it's ok to pass a NULL label to is_function_allowed. */
443 	if (
444 		!IS_EWMH_DESKTOP_FW(mpip->fw) &&
445 		!is_function_allowed(
446 			MI_FUNC_TYPE(mi), MI_LABEL(mi)[0], mpip->fw,
447 			RQORIG_PROGRAM_US, False))
448 	{
449 		gcs = ST_MENU_STIPPLE_GCS(ms);
450 		off_gcs = gcs;
451 		off_cs = ST_HAS_GREYED_CSET(ms) ? ST_CSET_GREYED(ms) : -1;
452 	}
453 	else if (is_item_selected)
454 	{
455 		gcs = ST_MENU_ACTIVE_GCS(ms);
456 		off_gcs = ST_MENU_INACTIVE_GCS(ms);
457 	}
458 	else if (MI_IS_TITLE(mi))
459 	{
460 		gcs = ST_MENU_TITLE_GCS(ms);
461 		off_gcs = ST_MENU_INACTIVE_GCS(ms);
462 	}
463 	else
464 	{
465 		gcs = ST_MENU_INACTIVE_GCS(ms);
466 		off_gcs = ST_MENU_INACTIVE_GCS(ms);
467 	}
468 	if (is_item_selected)
469 	{
470 		cs = (ST_HAS_ACTIVE_CSET(ms)) ? ST_CSET_ACTIVE(ms) : -1;
471 	}
472 	else if (MI_IS_TITLE(mi))
473 	{
474 		cs = (ST_HAS_TITLE_CSET(ms)) ? ST_CSET_TITLE(ms) : off_cs;
475 	}
476 	else
477 	{
478 		cs = off_cs;
479 	}
480 
481 	/*
482 	 * Hilight the item.
483 	 */
484 	if (FftSupport && ST_PSTDFONT(ms)->fftf.fftfont != NULL)
485 	{
486 		xft_clear = True;
487 	}
488 
489 	/* Hilight or clear the background. */
490 	lit_x_start = -1;
491 	lit_x_end = -1;
492 	if (is_item_selected &&
493 	    (ST_DO_HILIGHT_BACK(ms) || ST_DO_HILIGHT_FORE(ms)))
494 	{
495 		/* Hilight the background. */
496 		if (MDIM_HILIGHT_WIDTH(*dim) - 2 * relief_thickness > 0)
497 		{
498 			lit_x_start = MDIM_HILIGHT_X_OFFSET(*dim) +
499 				relief_thickness;
500 			lit_x_end = lit_x_start + MDIM_HILIGHT_WIDTH(*dim) -
501 				2 * relief_thickness;
502 			if (ST_DO_HILIGHT_BACK(ms))
503 			{
504 				draw_highlight_background(
505 					mpip, lit_x_start,
506 					y_offset + relief_thickness,
507 					lit_x_end - lit_x_start,
508 					y_height - relief_thickness,
509 					(cs >= 0 ? &Colorset[cs] : NULL),
510 					gcs.back_gc);
511 				item_cleared = True;
512 			}
513 		}
514 	}
515 	else if ((MI_WAS_DESELECTED(mi) &&
516 		  (relief_thickness > 0 ||
517 		   ST_DO_HILIGHT_BACK(ms) || ST_DO_HILIGHT_FORE(ms)) &&
518 		  (ST_FACE(ms).type != GradientMenu || ST_HAS_MENU_CSET(ms))))
519 	{
520 		int x1;
521 		int x2;
522 		/* we clear if xft_clear and !ST_HAS_MENU_CSET(ms) as the
523 		 * non colorset code is too complicate ... olicha */
524 		int d = 0;
525 
526 		if (MI_PREV_ITEM(mi) &&
527 		    mpip->selected_item == MI_PREV_ITEM(mi))
528 		{
529 			/* Don't paint over the hilight relief. */
530 			d = relief_thickness;
531 		}
532 		/* Undo the hilighting. */
533 		x1 = min(
534 			MDIM_HILIGHT_X_OFFSET(*dim), MDIM_ITEM_X_OFFSET(*dim));
535 		x2 = max(
536 			MDIM_HILIGHT_X_OFFSET(*dim) + MDIM_HILIGHT_WIDTH(*dim),
537 			MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim));
538 		clear_menu_item_background(
539 			mpip, x1, y_offset + d, x2 - x1,
540 			y_height + relief_thickness - d);
541 		item_cleared = True;
542 	}
543 	else if (MI_IS_TITLE(mi))
544 	{
545 		lit_x_start = MDIM_ITEM_X_OFFSET(*dim);
546 		lit_x_end = lit_x_start + MDIM_ITEM_WIDTH(*dim);
547 		/* Hilight the background. */
548 		if (
549 			MDIM_HILIGHT_WIDTH(*dim) > 0 &&
550 			ST_DO_HILIGHT_TITLE_BACK(ms))
551 		{
552 			draw_highlight_background(
553 				mpip, lit_x_start,
554 				y_offset + relief_thickness,
555 				lit_x_end - lit_x_start,
556 				y_height - relief_thickness,
557 				(cs >= 0 ? &Colorset[cs] : NULL),
558 				gcs.back_gc);
559 			item_cleared = True;
560 		}
561 	}
562 
563 	MI_WAS_DESELECTED(mi) = False;
564 	memset(&fra, 0, sizeof(fra));
565 	fra.mask = 0;
566 
567 	/* Hilight 3D */
568 	if (is_item_selected && relief_thickness > 0)
569 	{
570 		GC rgc;
571 		GC sgc;
572 
573 		rgc = gcs.hilight_gc;
574 		sgc = gcs.shadow_gc;
575 		if (ST_IS_ITEM_RELIEF_REVERSED(ms))
576 		{
577 			GC tgc = rgc;
578 
579 			/* swap gcs for reversed relief */
580 			rgc = sgc;
581 			sgc = tgc;
582 		}
583 		if (MDIM_HILIGHT_WIDTH(*dim) - 2 * relief_thickness > 0)
584 		{
585 			/* The relief reaches down into the next item, hence
586 			 * the value for the second y coordinate:
587 			 * MI_HEIGHT(mi) + 1 */
588 			RelieveRectangle(
589 				dpy, mpip->w,  MDIM_HILIGHT_X_OFFSET(*dim),
590 				y_offset, MDIM_HILIGHT_WIDTH(*dim) - 1,
591 				MI_HEIGHT(mi) - 1 + relief_thickness, rgc, sgc,
592 				relief_thickness);
593 		}
594 	}
595 
596 
597 	/*
598 	 * Draw the item itself.
599 	 */
600 
601 	/* Calculate the separator offsets. */
602 	if (ST_HAS_LONG_SEPARATORS(ms))
603 	{
604 		sx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness;
605 		sx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
606 			relief_thickness;
607 	}
608 	else
609 	{
610 		sx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness +
611 			MENU_SEPARATOR_SHORT_X_OFFSET;
612 		sx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
613 			relief_thickness - MENU_SEPARATOR_SHORT_X_OFFSET;
614 	}
615 	if (MI_IS_SEPARATOR(mi))
616 	{
617 		if (sx1 < sx2)
618 		{
619 			/* It's a separator. */
620 			draw_separator(
621 				mpip->w, gcs.shadow_gc, gcs.hilight_gc, sx1,
622 				y_offset + y_height - MENU_SEPARATOR_HEIGHT,
623 				sx2);
624 			/* Nothing else to do. */
625 		}
626 		return;
627 	}
628 	else if (MI_IS_TEAR_OFF_BAR(mi))
629 	{
630 		int tx1;
631 		int tx2;
632 
633 		tx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness +
634 			MENU_TEAR_OFF_BAR_X_OFFSET;
635 		tx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
636 			relief_thickness -
637 			MENU_TEAR_OFF_BAR_X_OFFSET;
638 		if (tx1 < tx2)
639 		{
640 
641 			/* It's a tear off bar. */
642 			draw_tear_off_bar(
643 				mpip->w, gcs.shadow_gc, gcs.hilight_gc, tx1,
644 				y_offset + relief_thickness +
645 				MENU_TEAR_OFF_BAR_Y_OFFSET, tx2);
646 		}
647 		/* Nothing else to do. */
648 		return;
649 	}
650 	else if (MI_IS_TITLE(mi))
651 	{
652 		/* Separate the title. */
653 		if (ST_TITLE_UNDERLINES(ms) > 0 && !mpip->flags.is_first_item)
654 		{
655 			int add = (MI_IS_SELECTABLE(MI_PREV_ITEM(mi))) ?
656 				relief_thickness : 0;
657 
658 			text_y += MENU_SEPARATOR_HEIGHT + add;
659 			y = y_offset + add;
660 			if (sx1 < sx2)
661 			{
662 				draw_separator(
663 					mpip->w, gcs.shadow_gc, gcs.hilight_gc,
664 					sx1, y, sx2);
665 			}
666 		}
667 		/* Underline the title. */
668 		switch (ST_TITLE_UNDERLINES(ms))
669 		{
670 		case 0:
671 			break;
672 		case 1:
673 			if (MI_NEXT_ITEM(mi) != NULL)
674 			{
675 				y = y_offset + y_height - MENU_SEPARATOR_HEIGHT;
676 				draw_separator(
677 					mpip->w, gcs.shadow_gc, gcs.hilight_gc,
678 					sx1, y, sx2);
679 			}
680 			break;
681 		default:
682 			for (i = ST_TITLE_UNDERLINES(ms); i-- > 0; )
683 			{
684 				y = y_offset + y_height - 1 -
685 					i * MENU_UNDERLINE_HEIGHT;
686 				XDrawLine(
687 					dpy, mpip->w, gcs.shadow_gc, sx1, y,
688 					sx2, y);
689 			}
690 			break;
691 		}
692 	}
693 
694 	/*
695 	 * Draw the labels.
696 	 */
697 	if (fws == NULL)
698 	{
699 		FlocaleAllocateWinString(&fws);
700 	}
701 	fws->win = mpip->w;
702 	fws->y = text_y;
703 	fws->flags.has_colorset = 0;
704 	b.y = text_y - font->ascent;
705 	b.height = font->height + 1; /* ? */
706 	if (!item_cleared && mpip->ev)
707 	{
708 		int u,v;
709 		if (!frect_get_seg_intersection(
710 			    mpip->ev->xexpose.y, mpip->ev->xexpose.height,
711 			    b.y, b.height, &u, &v))
712 		{
713 			/* empty intersection */
714 			empty_inter = True;
715 		}
716 		b.y = u;
717 		b.height = v;
718 	}
719 
720 	for (i = MAX_MENU_ITEM_LABELS; i-- > 0; )
721 	{
722 		if (!empty_inter && MI_LABEL(mi)[i] && *(MI_LABEL(mi)[i]))
723 		{
724 			Bool draw_string = True;
725 			int text_width;
726 			int tmp_cs;
727 
728 			if (MI_LABEL_OFFSET(mi)[i] >= lit_x_start &&
729 			    MI_LABEL_OFFSET(mi)[i] < lit_x_end)
730 			{
731 				/* label is in hilighted area */
732 				fws->gc = gcs.fore_gc;
733 				tmp_cs = cs;
734 			}
735 			else
736 			{
737 				/* label is in unhilighted area */
738 				fws->gc = off_gcs.fore_gc;
739 				tmp_cs = off_cs;
740 			}
741 			if (tmp_cs >= 0)
742 			{
743 				fws->colorset = &Colorset[tmp_cs];
744 				fws->flags.has_colorset = 1;
745 			}
746 			fws->str = MI_LABEL(mi)[i];
747 			b.x = fws->x = MI_LABEL_OFFSET(mi)[i];
748 			b.width = text_width = FlocaleTextWidth(
749 				font, fws->str, strlen(fws->str));
750 
751 			if (!item_cleared && mpip->ev)
752 			{
753 				int s_x,s_w;
754 				if (frect_get_seg_intersection(
755 					    mpip->ev->xexpose.x,
756 					    mpip->ev->xexpose.width,
757 					    fws->x, text_width,
758 					    &s_x, &s_w))
759 				{
760 					b.x = s_x;
761 					b.width = s_w;
762 					region = XCreateRegion();
763 					XUnionRectWithRegion(
764 						&b, region, region);
765 					fws->flags.has_clip_region = True;
766 					fws->clip_region = region;
767 					draw_string = True;
768 					XSetRegion(dpy, fws->gc, region);
769 				}
770 				else
771 				{
772 					/* empty intersection */
773 					draw_string = False;
774 				}
775 			}
776 			if (draw_string)
777 			{
778 				if (!item_cleared && xft_clear)
779 				{
780 					clear_menu_item_background(
781 						mpip, b.x, b.y, b.width,
782 						b.height);
783 				}
784 				FlocaleDrawString(dpy, font, fws, 0);
785 
786 				/* hot key */
787 				if (MI_HAS_HOTKEY(mi) && !MI_IS_TITLE(mi) &&
788 				    (!MI_IS_HOTKEY_AUTOMATIC(mi) ||
789 				     ST_USE_AUTOMATIC_HOTKEYS(ms)) &&
790 				    MI_HOTKEY_COLUMN(mi) == i)
791 				{
792 					FlocaleDrawUnderline(
793 						dpy, ST_PSTDFONT(ms), fws,
794 						MI_HOTKEY_COFFSET(mi));
795 				}
796 			}
797 		}
798 		if (region)
799 		{
800 			XDestroyRegion(region);
801 			region = None;
802 			fws->flags.has_clip_region = False;
803 			fws->clip_region = None;
804 			XSetClipMask(dpy, fws->gc, None);
805 		}
806 	}
807 
808 	/*
809 	 * Draw the submenu triangle.
810 	 */
811 
812 	if (MI_IS_POPUP(mi))
813 	{
814 		GC tmp_gc;
815 
816 		if (MDIM_TRIANGLE_X_OFFSET(*dim) >= lit_x_start &&
817 		    MDIM_TRIANGLE_X_OFFSET(*dim) < lit_x_end &&
818 		    is_item_selected)
819 		{
820 			/* triangle is in hilighted area */
821 			if (ST_TRIANGLES_USE_FORE(ms))
822 			{
823 				tmp_gc = gcs.fore_gc;
824 			}
825 			else
826 			{
827 				tmp_gc = gcs.hilight_gc;
828 			}
829 		}
830 		else
831 		{
832 			/* triangle is in unhilighted area */
833 			if (ST_TRIANGLES_USE_FORE(ms))
834 			{
835 				tmp_gc = off_gcs.fore_gc;
836 			}
837 			else
838 			{
839 				tmp_gc = off_gcs.hilight_gc;
840 			}
841 		}
842 		y = y_offset + (y_height - MENU_TRIANGLE_HEIGHT +
843 				relief_thickness) / 2;
844 
845 		if (ST_TRIANGLES_USE_FORE(ms))
846 		{
847 			DrawTrianglePattern(
848 				dpy, mpip->w, tmp_gc, tmp_gc, tmp_gc,
849 				MDIM_TRIANGLE_X_OFFSET(*dim), y,
850 				MENU_TRIANGLE_WIDTH, MENU_TRIANGLE_HEIGHT, 0,
851 				(mpip->flags.is_left_triangle) ? 'l' : 'r',
852 				ST_HAS_TRIANGLE_RELIEF(ms),
853 				!ST_HAS_TRIANGLE_RELIEF(ms), is_item_selected);
854 
855 		}
856 		else
857 		{
858 			DrawTrianglePattern(
859 				dpy, mpip->w, gcs.hilight_gc, gcs.shadow_gc,
860 				tmp_gc,	MDIM_TRIANGLE_X_OFFSET(*dim), y,
861 				MENU_TRIANGLE_WIDTH, MENU_TRIANGLE_HEIGHT, 0,
862 				(mpip->flags.is_left_triangle) ? 'l' : 'r',
863 				ST_HAS_TRIANGLE_RELIEF(ms),
864 				!ST_HAS_TRIANGLE_RELIEF(ms), is_item_selected);
865 		}
866 	}
867 
868 	/*
869 	 * Draw the item picture.
870 	 */
871 
872 	if (MI_PICTURE(mi))
873 	{
874 		GC tmp_gc;
875 		int tmp_cs;
876 		Bool draw_picture = True;
877 
878 		x = menudim_middle_x_offset(mpip->dim) -
879 			MI_PICTURE(mi)->width / 2;
880 		y = y_offset + ((MI_IS_SELECTABLE(mi)) ? relief_thickness : 0);
881 		if (x >= lit_x_start && x < lit_x_end)
882 		{
883 			tmp_gc = gcs.fore_gc;
884 			tmp_cs = cs;
885 		}
886 		else
887 		{
888 			tmp_gc = off_gcs.fore_gc;
889 			tmp_cs = off_cs;
890 		}
891 		fra.mask = FRAM_DEST_IS_A_WINDOW;
892 		if (tmp_cs >= 0)
893 		{
894 			fra.mask |= FRAM_HAVE_ICON_CSET;
895 			fra.colorset = &Colorset[tmp_cs];
896 		}
897 		b.x = x;
898 		b.y = y;
899 		b.width = MI_PICTURE(mi)->width;
900 		b.height = MI_PICTURE(mi)->height;
901 		if (!item_cleared && mpip->ev)
902 		{
903 			if (!frect_get_intersection(
904 				    mpip->ev->xexpose.x, mpip->ev->xexpose.y,
905 				    mpip->ev->xexpose.width,
906 				    mpip->ev->xexpose.height,
907 				    b.x, b.y, b.width, b.height, &b))
908 			{
909 				draw_picture = False;
910 			}
911 		}
912 		if (draw_picture)
913 		{
914 			if (
915 				!item_cleared &&
916 				(MI_PICTURE(mi)->alpha != None ||
917 				 (tmp_cs >=0 &&
918 				  Colorset[tmp_cs].icon_alpha_percent < 100)))
919 			{
920 				clear_menu_item_background(
921 					mpip, b.x, b.y, b.width, b.height);
922 			}
923 			PGraphicsRenderPicture(
924 				dpy, mpip->w, MI_PICTURE(mi), &fra,
925 				mpip->w, tmp_gc, Scr.MonoGC, Scr.AlphaGC,
926 				b.x - x, b.y - y, b.width, b.height,
927 				b.x, b.y, b.width, b.height, False);
928 		}
929 	}
930 
931 	/*
932 	 * Draw the mini icons.
933 	 */
934 
935 	for (i = 0; i < mpip->used_mini_icons; i++)
936 	{
937 		int k;
938 		Bool draw_picture = True;
939 
940 		/* We need to reverse the mini icon order for left submenu
941 		 * style. */
942 		k = (ST_USE_LEFT_SUBMENUS(ms)) ?
943 			mpip->used_mini_icons - 1 - i : i;
944 
945 		if (MI_MINI_ICON(mi)[i])
946 		{
947 			GC tmp_gc;
948 			int tmp_cs;
949 
950 			if (MI_PICTURE(mi))
951 			{
952 				y = y_offset + MI_HEIGHT(mi) -
953 					MI_MINI_ICON(mi)[i]->height;
954 			}
955 			else
956 			{
957 				y = y_offset +
958 					(MI_HEIGHT(mi) +
959 					 ((MI_IS_SELECTABLE(mi)) ?
960 					  relief_thickness : 0) -
961 					 MI_MINI_ICON(mi)[i]->height) / 2;
962 			}
963 			if (MDIM_ICON_X_OFFSET(*dim)[k] >= lit_x_start &&
964 			    MDIM_ICON_X_OFFSET(*dim)[k] < lit_x_end)
965 			{
966 				/* icon is in hilighted area */
967 				tmp_gc = gcs.fore_gc;
968 				tmp_cs = cs;
969 			}
970 			else
971 			{
972 				/* icon is in unhilighted area */
973 				tmp_gc = off_gcs.fore_gc;
974 				tmp_cs = off_cs;
975 			}
976 			fra.mask = FRAM_DEST_IS_A_WINDOW;
977 			if (tmp_cs >= 0)
978 			{
979 				fra.mask |= FRAM_HAVE_ICON_CSET;
980 				fra.colorset = &Colorset[tmp_cs];
981 			}
982 			b.x = MDIM_ICON_X_OFFSET(*dim)[k];
983 			b.y = y;
984 			b.width = MI_MINI_ICON(mi)[i]->width;
985 			b.height = MI_MINI_ICON(mi)[i]->height;
986 			if (!item_cleared && mpip->ev)
987 			{
988 				if (!frect_get_intersection(
989 					    mpip->ev->xexpose.x,
990 					    mpip->ev->xexpose.y,
991 					    mpip->ev->xexpose.width,
992 					    mpip->ev->xexpose.height,
993 					    b.x, b.y, b.width, b.height, &b))
994 				{
995 					draw_picture = False;
996 				}
997 			}
998 			if (draw_picture)
999 			{
1000 				if (!item_cleared &&
1001 				    (MI_MINI_ICON(mi)[i]->alpha != None
1002 				     || (tmp_cs >=0 &&
1003 					 Colorset[tmp_cs].icon_alpha_percent <
1004 					 100)))
1005 				{
1006 					clear_menu_item_background(
1007 						mpip,
1008 						b.x, b.y, b.width, b.height);
1009 				}
1010 				PGraphicsRenderPicture(
1011 					dpy, mpip->w, MI_MINI_ICON(mi)[i],
1012 					&fra, mpip->w, tmp_gc, Scr.MonoGC,
1013 					Scr.AlphaGC,
1014 					b.x - MDIM_ICON_X_OFFSET(*dim)[k],
1015 					b.y - y, b.width, b.height,
1016 					b.x, b.y, b.width, b.height, False);
1017 			}
1018 		}
1019 	}
1020 
1021 	return;
1022 }
1023 
1024 /* returns the center y coordinate of the menu item */
menuitem_middle_y_offset(struct MenuItem * mi,struct MenuStyle * ms)1025 int menuitem_middle_y_offset(struct MenuItem *mi, struct MenuStyle *ms)
1026 {
1027 	int r;
1028 
1029 	if (!mi)
1030 	{
1031 		return ST_BORDER_WIDTH(ms);
1032 	}
1033 	r = (MI_IS_SELECTABLE(mi)) ? ST_RELIEF_THICKNESS(ms) : 0;
1034 
1035 	return MI_Y_OFFSET(mi) + (MI_HEIGHT(mi) + r) / 2;
1036 }
1037