Fix segfault when creating a tearoff menu using a Pixmap background.
[fvwm.git] / fvwm / menuitem.c
blob3e4ea2cee0eb4b79affa59e2c7ac37c8721b742b
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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* ---------------------------- included header files ---------------------- */
19 #include "config.h"
21 #include <stdio.h>
23 #include "libs/fvwmlib.h"
24 #include "libs/Picture.h"
25 #include "libs/Graphics.h"
26 #include "libs/PictureGraphics.h"
27 #include "libs/Rectangles.h"
28 #include "fvwm.h"
29 #include "externs.h"
30 #include "execcontext.h"
31 #include "misc.h"
32 #include "screen.h"
33 #include "menudim.h"
34 #include "menustyle.h"
35 #include "menuitem.h"
36 #include "decorations.h"
38 /* ---------------------------- local definitions -------------------------- */
40 /* ---------------------------- local macros ------------------------------- */
42 /* ---------------------------- imports ------------------------------------ */
44 /* ---------------------------- included code files ------------------------ */
46 /* ---------------------------- local types -------------------------------- */
48 /* ---------------------------- forward declarations ----------------------- */
50 /* ---------------------------- local variables ---------------------------- */
52 /* ---------------------------- exported variables (globals) --------------- */
54 /* ---------------------------- local functions ---------------------------- */
55 static void clear_menu_item_background(
56 MenuPaintItemParameters *mpip, int x, int y, int w, int h)
58 MenuStyle *ms = mpip->ms;
60 if (!ST_HAS_MENU_CSET(ms) &&
61 ST_FACE(ms).type == GradientMenu &&
62 (ST_FACE(ms).gradient_type == D_GRADIENT ||
63 ST_FACE(ms).gradient_type == B_GRADIENT))
65 XEvent e;
67 e.xexpose.x = x;
68 e.xexpose.y = y;
69 e.xexpose.width = w;
70 e.xexpose.height = h;
71 mpip->cb_reset_bg(mpip->cb_mr, &e);
73 else
75 XClearArea(dpy, mpip->w, x, y, w, h, False);
81 * Draws two horizontal lines to form a separator
84 static void draw_separator(
85 Window w, GC TopGC, GC BottomGC, int x1, int y, int x2)
87 XDrawLine(dpy, w, TopGC , x1, y, x2, y);
88 XDrawLine(dpy, w, BottomGC, x1-1, y+1, x2+1, y+1);
90 return;
95 * Draws a tear off bar. Similar to a separator, but with a dashed line.
98 static void draw_tear_off_bar(
99 Window w, GC TopGC, GC BottomGC, int x1, int y, int x2)
101 XGCValues xgcv;
102 int width;
103 int offset;
105 xgcv.line_style = LineOnOffDash;
106 xgcv.dashes = MENU_TEAR_OFF_BAR_DASH_WIDTH;
107 XChangeGC(dpy, TopGC, GCLineStyle | GCDashList, &xgcv);
108 XChangeGC(dpy, BottomGC, GCLineStyle | GCDashList, &xgcv);
109 width = (x2 - x1 + 1);
110 offset = (width / MENU_TEAR_OFF_BAR_DASH_WIDTH) *
111 MENU_TEAR_OFF_BAR_DASH_WIDTH;
112 offset = (width - offset) / 2;
113 x1 += offset;
114 x2 += offset;
115 XDrawLine(dpy, w, TopGC, x1, y, x2, y);
116 XDrawLine(dpy, w, BottomGC, x1, y + 1, x2, y + 1);
117 xgcv.line_style = LineSolid;
118 XChangeGC(dpy, TopGC, GCLineStyle, &xgcv);
119 XChangeGC(dpy, BottomGC, GCLineStyle, &xgcv);
121 return;
124 static void draw_highlight_background(
125 struct MenuPaintItemParameters *mpip, int x, int y, int width,
126 int height, colorset_t *cs, GC gc)
128 if (cs != NULL && cs->pixmap && cs->pixmap_type != PIXMAP_TILED)
130 Pixmap p;
132 p = CreateOffsetBackgroundPixmap(
133 dpy, mpip->w, 0, 0, width, height, cs, Pdepth, gc,
134 False);
135 switch (cs->pixmap_type)
137 case PIXMAP_STRETCH_X:
138 /* todo: optimize to only create one pixmap and gc per
139 * mr. */
140 case PIXMAP_STRETCH_Y:
142 XGCValues gcv;
143 int gcm;
144 GC bgc;
146 gcv.tile = p;
147 gcv.fill_style = FillTiled;
148 gcm = GCFillStyle | GCTile;
150 /* vertcal gradients has to be aligned properly */
151 if (cs->pixmap_type == PIXMAP_STRETCH_Y)
153 gcv.ts_y_origin = y;
154 gcm|=GCTileStipYOrigin;
156 else if (cs->pixmap_type == PIXMAP_STRETCH_X)
158 gcv.ts_x_origin = x;
159 gcm|=GCTileStipXOrigin;
161 bgc = fvwmlib_XCreateGC(dpy, mpip->w, gcm, &gcv);
162 XFillRectangle(dpy, mpip->w, bgc, x, y, width, height);
163 XFreeGC(dpy, bgc);
164 break;
166 default:
167 XCopyArea(dpy, p, mpip->w, gc, 0, 0, width, height,
168 x, y);
169 break;
171 XFreePixmap(dpy, p);
173 else
175 XFillRectangle(dpy, mpip->w, gc, x, y, width, height);
179 /* ---------------------------- interface functions ------------------------ */
181 /* Allocates a new, empty menu item */
182 struct MenuItem *menuitem_create(void)
184 MenuItem *mi;
186 mi = (MenuItem *)safemalloc(sizeof(MenuItem));
187 memset(mi, 0, sizeof(MenuItem));
189 return mi;
192 /* Frees a menu item and all of its allocated resources. */
193 void menuitem_free(struct MenuItem *mi)
195 int i;
197 if (!mi)
199 return;
201 for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
203 if (MI_LABEL(mi)[i] != NULL)
205 free(MI_LABEL(mi)[i]);
208 if (MI_ACTION(mi) != NULL)
210 free(MI_ACTION(mi));
212 if (MI_PICTURE(mi))
214 PDestroyFvwmPicture(dpy, MI_PICTURE(mi));
216 for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
218 if (MI_MINI_ICON(mi)[i])
220 PDestroyFvwmPicture(dpy, MI_MINI_ICON(mi)[i]);
223 free(mi);
225 return;
228 /* Duplicate a menu item into newly allocated memory. The new item is
229 * completely independent of the old one. */
230 struct MenuItem *menuitem_clone(struct MenuItem *mi)
232 MenuItem *new_mi;
233 int i;
235 /* copy everything */
236 new_mi = (MenuItem *)safemalloc(sizeof(MenuItem));
237 memcpy(new_mi, mi, sizeof(MenuItem));
238 /* special treatment for a few parts */
239 MI_NEXT_ITEM(new_mi) = NULL;
240 MI_PREV_ITEM(new_mi) = NULL;
241 MI_WAS_DESELECTED(new_mi) = 0;
242 if (MI_ACTION(mi) != NULL)
244 MI_ACTION(new_mi) = safestrdup(MI_ACTION(mi));
246 for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
248 if (MI_LABEL(mi)[i] != NULL)
250 MI_LABEL(new_mi)[i] = strdup(MI_LABEL(mi)[i]);
253 if (MI_PICTURE(mi) != NULL)
255 MI_PICTURE(new_mi) = PCloneFvwmPicture(MI_PICTURE(mi));
257 for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
259 if (MI_MINI_ICON(mi)[i] != NULL)
261 MI_MINI_ICON(new_mi)[i] =
262 PCloneFvwmPicture(MI_MINI_ICON(mi)[i]);
266 return new_mi;
269 /* Calculate the size of the various parts of the item. The sizes are returned
270 * through mipst. */
271 void menuitem_get_size(
272 struct MenuItem *mi, struct MenuItemPartSizesT *mipst,
273 FlocaleFont *font, Bool do_reverse_icon_order)
275 int i;
276 int j;
277 int w;
279 memset(mipst, 0, sizeof(MenuItemPartSizesT));
280 if (MI_IS_POPUP(mi))
282 mipst->triangle_width = MENU_TRIANGLE_WIDTH;
284 else if (MI_IS_TITLE(mi) && !MI_HAS_PICTURE(mi))
286 Bool is_formatted = False;
288 /* titles stretch over the whole menu width, so count the
289 * maximum separately if the title is unformatted. */
290 for (j = 1; j < MAX_MENU_ITEM_LABELS; j++)
292 if (MI_LABEL(mi)[j] != NULL)
294 is_formatted = True;
295 break;
297 else
299 MI_LABEL_OFFSET(mi)[j] = 0;
302 if (!is_formatted && MI_LABEL(mi)[0] != NULL)
304 MI_LABEL_STRLEN(mi)[0] =
305 strlen(MI_LABEL(mi)[0]);
306 w = FlocaleTextWidth(
307 font, MI_LABEL(mi)[0], MI_LABEL_STRLEN(mi)[0]);
308 MI_LABEL_OFFSET(mi)[0] = w;
309 MI_IS_TITLE_CENTERED(mi) = True;
310 if (mipst->title_width < w)
312 mipst->title_width = w;
314 return;
317 /* regular item or formatted title */
318 for (i = 0; i < MAX_MENU_ITEM_LABELS; i++)
320 if (MI_LABEL(mi)[i])
322 MI_LABEL_STRLEN(mi)[i] = strlen(MI_LABEL(mi)[i]);
323 w = FlocaleTextWidth(
324 font, MI_LABEL(mi)[i], MI_LABEL_STRLEN(mi)[i]);
325 MI_LABEL_OFFSET(mi)[i] = w;
326 if (mipst->label_width[i] < w)
328 mipst->label_width[i] = w;
332 if (MI_PICTURE(mi) && mipst->picture_width < MI_PICTURE(mi)->width)
334 mipst->picture_width = MI_PICTURE(mi)->width;
336 for (i = 0; i < MAX_MENU_ITEM_MINI_ICONS; i++)
338 if (MI_MINI_ICON(mi)[i])
340 int k;
342 /* Reverse mini icon order for left submenu style. */
343 k = (do_reverse_icon_order == True) ?
344 MAX_MENU_ITEM_MINI_ICONS - 1 - i : i;
345 mipst->icon_width[k] = MI_MINI_ICON(mi)[i]->width;
349 return;
354 * Procedure:
355 * menuitem_paint - draws a single entry in a popped up menu
357 * mr - the menu instance that holds the menu item
358 * mi - the menu item to redraw
359 * fw - the FvwmWindow structure to check against allowed functions
362 void menuitem_paint(
363 struct MenuItem *mi, struct MenuPaintItemParameters *mpip)
365 struct MenuStyle *ms = mpip->ms;
366 struct MenuDimensions *dim = mpip->dim;
368 static FlocaleWinString *fws = NULL;
369 int y_offset;
370 int text_y;
371 int y_height;
372 int x;
373 int y;
374 int lit_x_start;
375 int lit_x_end;
376 gc_quad_t gcs;
377 gc_quad_t off_gcs;
378 int cs = -1;
379 int off_cs;
380 FvwmRenderAttributes fra;
381 /*Pixel fg, fgsh;*/
382 int relief_thickness = ST_RELIEF_THICKNESS(ms);
383 Bool is_item_selected;
384 Bool item_cleared = False;
385 Bool xft_clear = False;
386 Bool empty_inter = False;
387 XRectangle b;
388 Region region = None;
389 int i;
390 int sx1;
391 int sx2;
392 FlocaleFont* font;
394 if (!mi)
396 return;
398 is_item_selected = (mi == mpip->selected_item);
400 if (MI_IS_TITLE(mi))
402 font = ST_PTITLEFONT(ms);
404 else
406 font = ST_PSTDFONT(ms);
409 y_offset = MI_Y_OFFSET(mi);
410 y_height = MI_HEIGHT(mi);
411 if (MI_IS_SELECTABLE(mi))
413 text_y = y_offset + MDIM_ITEM_TEXT_Y_OFFSET(*dim);
415 else
417 text_y = y_offset + font->ascent +
418 ST_TITLE_GAP_ABOVE(ms);
420 /* center text vertically if the pixmap is taller */
421 if (MI_PICTURE(mi))
423 text_y += MI_PICTURE(mi)->height;
425 for (i = 0; i < mpip->used_mini_icons; i++)
427 y = 0;
428 if (MI_MINI_ICON(mi)[i])
430 if (MI_MINI_ICON(mi)[i]->height > y)
432 y = MI_MINI_ICON(mi)[i]->height;
435 y -= font->height;
436 if (y > 1)
438 text_y += y / 2;
442 off_cs = ST_HAS_MENU_CSET(ms) ? ST_CSET_MENU(ms) : -1;
443 /* Note: it's ok to pass a NULL label to is_function_allowed. */
444 if (
445 !IS_EWMH_DESKTOP_FW(mpip->fw) &&
446 !is_function_allowed(
447 MI_FUNC_TYPE(mi), MI_LABEL(mi)[0], mpip->fw,
448 RQORIG_PROGRAM_US, False))
450 gcs = ST_MENU_STIPPLE_GCS(ms);
451 off_gcs = gcs;
452 off_cs = ST_HAS_GREYED_CSET(ms) ? ST_CSET_GREYED(ms) : -1;
454 else if (is_item_selected)
456 gcs = ST_MENU_ACTIVE_GCS(ms);
457 off_gcs = ST_MENU_INACTIVE_GCS(ms);
459 else if (MI_IS_TITLE(mi))
461 gcs = ST_MENU_TITLE_GCS(ms);
462 off_gcs = ST_MENU_INACTIVE_GCS(ms);
464 else
466 gcs = ST_MENU_INACTIVE_GCS(ms);
467 off_gcs = ST_MENU_INACTIVE_GCS(ms);
469 if (is_item_selected)
471 cs = (ST_HAS_ACTIVE_CSET(ms)) ? ST_CSET_ACTIVE(ms) : -1;
473 else if (MI_IS_TITLE(mi))
475 cs = (ST_HAS_TITLE_CSET(ms)) ? ST_CSET_TITLE(ms) : off_cs;
477 else
479 cs = off_cs;
483 * Hilight the item.
485 if (FftSupport && ST_PSTDFONT(ms)->fftf.fftfont != NULL)
487 xft_clear = True;
490 /* Hilight or clear the background. */
491 lit_x_start = -1;
492 lit_x_end = -1;
493 if (is_item_selected &&
494 (ST_DO_HILIGHT_BACK(ms) || ST_DO_HILIGHT_FORE(ms)))
496 /* Hilight the background. */
497 if (MDIM_HILIGHT_WIDTH(*dim) - 2 * relief_thickness > 0)
499 lit_x_start = MDIM_HILIGHT_X_OFFSET(*dim) +
500 relief_thickness;
501 lit_x_end = lit_x_start + MDIM_HILIGHT_WIDTH(*dim) -
502 2 * relief_thickness;
503 if (ST_DO_HILIGHT_BACK(ms))
505 draw_highlight_background(
506 mpip, lit_x_start,
507 y_offset + relief_thickness,
508 lit_x_end - lit_x_start,
509 y_height - relief_thickness,
510 (cs >= 0 ? &Colorset[cs] : NULL),
511 gcs.back_gc);
512 item_cleared = True;
516 else if ((MI_WAS_DESELECTED(mi) &&
517 (relief_thickness > 0 ||
518 ST_DO_HILIGHT_BACK(ms) || ST_DO_HILIGHT_FORE(ms)) &&
519 (ST_FACE(ms).type != GradientMenu || ST_HAS_MENU_CSET(ms))))
521 int x1;
522 int x2;
523 /* we clear if xft_clear and !ST_HAS_MENU_CSET(ms) as the
524 * non colorset code is too complicate ... olicha */
525 int d = 0;
527 if (MI_PREV_ITEM(mi) &&
528 mpip->selected_item == MI_PREV_ITEM(mi))
530 /* Don't paint over the hilight relief. */
531 d = relief_thickness;
533 /* Undo the hilighting. */
534 x1 = min(
535 MDIM_HILIGHT_X_OFFSET(*dim), MDIM_ITEM_X_OFFSET(*dim));
536 x2 = max(
537 MDIM_HILIGHT_X_OFFSET(*dim) + MDIM_HILIGHT_WIDTH(*dim),
538 MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim));
539 clear_menu_item_background(
540 mpip, x1, y_offset + d, x2 - x1,
541 y_height + relief_thickness - d);
542 item_cleared = True;
544 else if (MI_IS_TITLE(mi))
546 lit_x_start = MDIM_ITEM_X_OFFSET(*dim);
547 lit_x_end = lit_x_start + MDIM_ITEM_WIDTH(*dim);
548 /* Hilight the background. */
549 if (
550 MDIM_HILIGHT_WIDTH(*dim) > 0 &&
551 ST_DO_HILIGHT_TITLE_BACK(ms))
553 draw_highlight_background(
554 mpip, lit_x_start,
555 y_offset + relief_thickness,
556 lit_x_end - lit_x_start,
557 y_height - relief_thickness,
558 (cs >= 0 ? &Colorset[cs] : NULL),
559 gcs.back_gc);
560 item_cleared = True;
564 MI_WAS_DESELECTED(mi) = False;
565 memset(&fra, 0, sizeof(fra));
566 fra.mask = 0;
568 /* Hilight 3D */
569 if (is_item_selected && relief_thickness > 0)
571 GC rgc;
572 GC sgc;
574 rgc = gcs.hilight_gc;
575 sgc = gcs.shadow_gc;
576 if (ST_IS_ITEM_RELIEF_REVERSED(ms))
578 GC tgc = rgc;
580 /* swap gcs for reversed relief */
581 rgc = sgc;
582 sgc = tgc;
584 if (MDIM_HILIGHT_WIDTH(*dim) - 2 * relief_thickness > 0)
586 /* The relief reaches down into the next item, hence
587 * the value for the second y coordinate:
588 * MI_HEIGHT(mi) + 1 */
589 RelieveRectangle(
590 dpy, mpip->w, MDIM_HILIGHT_X_OFFSET(*dim),
591 y_offset, MDIM_HILIGHT_WIDTH(*dim) - 1,
592 MI_HEIGHT(mi) - 1 + relief_thickness, rgc, sgc,
593 relief_thickness);
599 * Draw the item itself.
602 /* Calculate the separator offsets. */
603 if (ST_HAS_LONG_SEPARATORS(ms))
605 sx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness;
606 sx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
607 relief_thickness;
609 else
611 sx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness +
612 MENU_SEPARATOR_SHORT_X_OFFSET;
613 sx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
614 relief_thickness - MENU_SEPARATOR_SHORT_X_OFFSET;
616 if (MI_IS_SEPARATOR(mi))
618 if (sx1 < sx2)
620 /* It's a separator. */
621 draw_separator(
622 mpip->w, gcs.shadow_gc, gcs.hilight_gc, sx1,
623 y_offset + y_height - MENU_SEPARATOR_HEIGHT,
624 sx2);
625 /* Nothing else to do. */
627 return;
629 else if (MI_IS_TEAR_OFF_BAR(mi))
631 int tx1;
632 int tx2;
634 tx1 = MDIM_ITEM_X_OFFSET(*dim) + relief_thickness +
635 MENU_TEAR_OFF_BAR_X_OFFSET;
636 tx2 = MDIM_ITEM_X_OFFSET(*dim) + MDIM_ITEM_WIDTH(*dim) - 1 -
637 relief_thickness -
638 MENU_TEAR_OFF_BAR_X_OFFSET;
639 if (tx1 < tx2)
642 /* It's a tear off bar. */
643 draw_tear_off_bar(
644 mpip->w, gcs.shadow_gc, gcs.hilight_gc, tx1,
645 y_offset + relief_thickness +
646 MENU_TEAR_OFF_BAR_Y_OFFSET, tx2);
648 /* Nothing else to do. */
649 return;
651 else if (MI_IS_TITLE(mi))
653 /* Separate the title. */
654 if (ST_TITLE_UNDERLINES(ms) > 0 && !mpip->flags.is_first_item)
656 int add = (MI_IS_SELECTABLE(MI_PREV_ITEM(mi))) ?
657 relief_thickness : 0;
659 text_y += MENU_SEPARATOR_HEIGHT + add;
660 y = y_offset + add;
661 if (sx1 < sx2)
663 draw_separator(
664 mpip->w, gcs.shadow_gc, gcs.hilight_gc,
665 sx1, y, sx2);
668 /* Underline the title. */
669 switch (ST_TITLE_UNDERLINES(ms))
671 case 0:
672 break;
673 case 1:
674 if (MI_NEXT_ITEM(mi) != NULL)
676 y = y_offset + y_height - MENU_SEPARATOR_HEIGHT;
677 draw_separator(
678 mpip->w, gcs.shadow_gc, gcs.hilight_gc,
679 sx1, y, sx2);
681 break;
682 default:
683 for (i = ST_TITLE_UNDERLINES(ms); i-- > 0; )
685 y = y_offset + y_height - 1 -
686 i * MENU_UNDERLINE_HEIGHT;
687 XDrawLine(
688 dpy, mpip->w, gcs.shadow_gc, sx1, y,
689 sx2, y);
691 break;
696 * Draw the labels.
698 if (fws == NULL)
700 FlocaleAllocateWinString(&fws);
702 fws->win = mpip->w;
703 fws->y = text_y;
704 fws->flags.has_colorset = 0;
705 b.y = text_y - font->ascent;
706 b.height = font->height + 1; /* ? */
707 if (!item_cleared && mpip->ev)
709 int u,v;
710 if (!frect_get_seg_intersection(
711 mpip->ev->xexpose.y, mpip->ev->xexpose.height,
712 b.y, b.height, &u, &v))
714 /* empty intersection */
715 empty_inter = True;
717 b.y = u;
718 b.height = v;
721 for (i = MAX_MENU_ITEM_LABELS; i-- > 0; )
723 if (!empty_inter && MI_LABEL(mi)[i] && *(MI_LABEL(mi)[i]))
725 Bool draw_string = True;
726 int text_width;
727 int tmp_cs;
729 if (MI_LABEL_OFFSET(mi)[i] >= lit_x_start &&
730 MI_LABEL_OFFSET(mi)[i] < lit_x_end)
732 /* label is in hilighted area */
733 fws->gc = gcs.fore_gc;
734 tmp_cs = cs;
736 else
738 /* label is in unhilighted area */
739 fws->gc = off_gcs.fore_gc;
740 tmp_cs = off_cs;
742 if (tmp_cs >= 0)
744 fws->colorset = &Colorset[tmp_cs];
745 fws->flags.has_colorset = 1;
747 fws->str = MI_LABEL(mi)[i];
748 b.x = fws->x = MI_LABEL_OFFSET(mi)[i];
749 b.width = text_width = FlocaleTextWidth(
750 font, fws->str, strlen(fws->str));
752 if (!item_cleared && mpip->ev)
754 int s_x,s_w;
755 if (frect_get_seg_intersection(
756 mpip->ev->xexpose.x,
757 mpip->ev->xexpose.width,
758 fws->x, text_width,
759 &s_x, &s_w))
761 b.x = s_x;
762 b.width = s_w;
763 region = XCreateRegion();
764 XUnionRectWithRegion(
765 &b, region, region);
766 fws->flags.has_clip_region = True;
767 fws->clip_region = region;
768 draw_string = True;
769 XSetRegion(dpy, fws->gc, region);
771 else
773 /* empty intersection */
774 draw_string = False;
777 if (draw_string)
779 if (!item_cleared && xft_clear)
781 clear_menu_item_background(
782 mpip, b.x, b.y, b.width,
783 b.height);
785 FlocaleDrawString(dpy, font, fws, 0);
787 /* hot key */
788 if (MI_HAS_HOTKEY(mi) && !MI_IS_TITLE(mi) &&
789 (!MI_IS_HOTKEY_AUTOMATIC(mi) ||
790 ST_USE_AUTOMATIC_HOTKEYS(ms)) &&
791 MI_HOTKEY_COLUMN(mi) == i)
793 FlocaleDrawUnderline(
794 dpy, ST_PSTDFONT(ms), fws,
795 MI_HOTKEY_COFFSET(mi));
799 if (region)
801 XDestroyRegion(region);
802 region = None;
803 fws->flags.has_clip_region = False;
804 fws->clip_region = None;
805 XSetClipMask(dpy, fws->gc, None);
810 * Draw the submenu triangle.
813 if (MI_IS_POPUP(mi))
815 GC tmp_gc;
817 if (MDIM_TRIANGLE_X_OFFSET(*dim) >= lit_x_start &&
818 MDIM_TRIANGLE_X_OFFSET(*dim) < lit_x_end &&
819 is_item_selected)
821 /* triangle is in hilighted area */
822 if (ST_TRIANGLES_USE_FORE(ms))
824 tmp_gc = gcs.fore_gc;
826 else
828 tmp_gc = gcs.hilight_gc;
831 else
833 /* triangle is in unhilighted area */
834 if (ST_TRIANGLES_USE_FORE(ms))
836 tmp_gc = off_gcs.fore_gc;
838 else
840 tmp_gc = off_gcs.hilight_gc;
843 y = y_offset + (y_height - MENU_TRIANGLE_HEIGHT +
844 relief_thickness) / 2;
846 if (ST_TRIANGLES_USE_FORE(ms))
848 DrawTrianglePattern(
849 dpy, mpip->w, tmp_gc, tmp_gc, tmp_gc,
850 MDIM_TRIANGLE_X_OFFSET(*dim), y,
851 MENU_TRIANGLE_WIDTH, MENU_TRIANGLE_HEIGHT, 0,
852 (mpip->flags.is_left_triangle) ? 'l' : 'r',
853 ST_HAS_TRIANGLE_RELIEF(ms),
854 !ST_HAS_TRIANGLE_RELIEF(ms), is_item_selected);
857 else
859 DrawTrianglePattern(
860 dpy, mpip->w, gcs.hilight_gc, gcs.shadow_gc,
861 tmp_gc, MDIM_TRIANGLE_X_OFFSET(*dim), y,
862 MENU_TRIANGLE_WIDTH, MENU_TRIANGLE_HEIGHT, 0,
863 (mpip->flags.is_left_triangle) ? 'l' : 'r',
864 ST_HAS_TRIANGLE_RELIEF(ms),
865 !ST_HAS_TRIANGLE_RELIEF(ms), is_item_selected);
870 * Draw the item picture.
873 if (MI_PICTURE(mi))
875 GC tmp_gc;
876 int tmp_cs;
877 Bool draw_picture = True;
879 x = menudim_middle_x_offset(mpip->dim) -
880 MI_PICTURE(mi)->width / 2;
881 y = y_offset + ((MI_IS_SELECTABLE(mi)) ? relief_thickness : 0);
882 if (x >= lit_x_start && x < lit_x_end)
884 tmp_gc = gcs.fore_gc;
885 tmp_cs = cs;
887 else
889 tmp_gc = off_gcs.fore_gc;
890 tmp_cs = off_cs;
892 fra.mask = FRAM_DEST_IS_A_WINDOW;
893 if (tmp_cs >= 0)
895 fra.mask |= FRAM_HAVE_ICON_CSET;
896 fra.colorset = &Colorset[tmp_cs];
898 b.x = x;
899 b.y = y;
900 b.width = MI_PICTURE(mi)->width;
901 b.height = MI_PICTURE(mi)->height;
902 if (!item_cleared && mpip->ev)
904 if (!frect_get_intersection(
905 mpip->ev->xexpose.x, mpip->ev->xexpose.y,
906 mpip->ev->xexpose.width,
907 mpip->ev->xexpose.height,
908 b.x, b.y, b.width, b.height, &b))
910 draw_picture = False;
913 if (draw_picture)
915 if (
916 !item_cleared &&
917 (MI_PICTURE(mi)->alpha != None ||
918 (tmp_cs >=0 &&
919 Colorset[tmp_cs].icon_alpha_percent < 100)))
921 clear_menu_item_background(
922 mpip, b.x, b.y, b.width, b.height);
924 PGraphicsRenderPicture(
925 dpy, mpip->w, MI_PICTURE(mi), &fra,
926 mpip->w, tmp_gc, Scr.MonoGC, Scr.AlphaGC,
927 b.x - x, b.y - y, b.width, b.height,
928 b.x, b.y, b.width, b.height, False);
933 * Draw the mini icons.
936 for (i = 0; i < mpip->used_mini_icons; i++)
938 int k;
939 Bool draw_picture = True;
941 /* We need to reverse the mini icon order for left submenu
942 * style. */
943 k = (ST_USE_LEFT_SUBMENUS(ms)) ?
944 mpip->used_mini_icons - 1 - i : i;
946 if (MI_MINI_ICON(mi)[i])
948 GC tmp_gc;
949 int tmp_cs;
951 if (MI_PICTURE(mi))
953 y = y_offset + MI_HEIGHT(mi) -
954 MI_MINI_ICON(mi)[i]->height;
956 else
958 y = y_offset +
959 (MI_HEIGHT(mi) +
960 ((MI_IS_SELECTABLE(mi)) ?
961 relief_thickness : 0) -
962 MI_MINI_ICON(mi)[i]->height) / 2;
964 if (MDIM_ICON_X_OFFSET(*dim)[k] >= lit_x_start &&
965 MDIM_ICON_X_OFFSET(*dim)[k] < lit_x_end)
967 /* icon is in hilighted area */
968 tmp_gc = gcs.fore_gc;
969 tmp_cs = cs;
971 else
973 /* icon is in unhilighted area */
974 tmp_gc = off_gcs.fore_gc;
975 tmp_cs = off_cs;
977 fra.mask = FRAM_DEST_IS_A_WINDOW;
978 if (tmp_cs >= 0)
980 fra.mask |= FRAM_HAVE_ICON_CSET;
981 fra.colorset = &Colorset[tmp_cs];
983 b.x = MDIM_ICON_X_OFFSET(*dim)[k];
984 b.y = y;
985 b.width = MI_MINI_ICON(mi)[i]->width;
986 b.height = MI_MINI_ICON(mi)[i]->height;
987 if (!item_cleared && mpip->ev)
989 if (!frect_get_intersection(
990 mpip->ev->xexpose.x,
991 mpip->ev->xexpose.y,
992 mpip->ev->xexpose.width,
993 mpip->ev->xexpose.height,
994 b.x, b.y, b.width, b.height, &b))
996 draw_picture = False;
999 if (draw_picture)
1001 if (!item_cleared &&
1002 (MI_MINI_ICON(mi)[i]->alpha != None
1003 || (tmp_cs >=0 &&
1004 Colorset[tmp_cs].icon_alpha_percent <
1005 100)))
1007 clear_menu_item_background(
1008 mpip,
1009 b.x, b.y, b.width, b.height);
1011 PGraphicsRenderPicture(
1012 dpy, mpip->w, MI_MINI_ICON(mi)[i],
1013 &fra, mpip->w, tmp_gc, Scr.MonoGC,
1014 Scr.AlphaGC,
1015 b.x - MDIM_ICON_X_OFFSET(*dim)[k],
1016 b.y - y, b.width, b.height,
1017 b.x, b.y, b.width, b.height, False);
1022 return;
1025 /* returns the center y coordinate of the menu item */
1026 int menuitem_middle_y_offset(struct MenuItem *mi, struct MenuStyle *ms)
1028 int r;
1030 if (!mi)
1032 return ST_BORDER_WIDTH(ms);
1034 r = (MI_IS_SELECTABLE(mi)) ? ST_RELIEF_THICKNESS(ms) : 0;
1036 return MI_Y_OFFSET(mi) + (MI_HEIGHT(mi) + r) / 2;