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