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
18 * Copyright 1993, Robert Nation
19 * You may use this code for any purpose, as long as the original
20 * copyright remains in the source code and all documentation
23 /* IMPORTANT NOTE: Do *not* use any constant numbers in this file. All values
24 * have to be #defined in the section below or in defaults.h to ensure full
25 * control over the menus. */
27 /* ---------------------------- included header files ---------------------- */
33 #include <X11/keysym.h>
35 #include "libs/ftime.h"
36 #include "libs/fvwmlib.h"
37 #include "libs/FScreen.h"
38 #include "libs/Grab.h"
39 #include "libs/Parse.h"
40 #include "libs/ColorUtils.h"
41 #include "libs/Picture.h"
42 #include "libs/PictureUtils.h"
43 #include "libs/Graphics.h"
44 #include "libs/PictureGraphics.h"
45 #include "libs/charmap.h"
46 #include "libs/wcontext.h"
49 #include "execcontext.h"
51 #include "eventhandler.h"
52 #include "eventmask.h"
54 #include "functions.h"
58 #include "colormaps.h"
60 #include "move_resize.h"
64 #include "menustyle.h"
66 #include "menubindings.h"
67 #include "menugeometry.h"
68 #include "menuparameters.h"
70 #include "libs/FGettext.h"
72 /* ---------------------------- local definitions -------------------------- */
74 /* used in float to int arithmetic */
75 #define ROUNDING_ERROR_TOLERANCE 0.005
77 /* ---------------------------- local macros ------------------------------- */
79 #define SCTX_SET_MI(ctx,item) ((ctx).type = SCTX_MENU_ITEM, \
80 (ctx).menu_item.menu_item = (item))
81 #define SCTX_GET_MI(ctx) ((ctx).type == SCTX_MENU_ITEM ? \
82 (ctx).menu_item.menu_item : NULL)
83 #define SCTX_SET_MR(ctx,root) ((ctx).type = SCTX_MENU_ROOT, \
84 (ctx).menu_root.menu_root = (root))
85 #define SCTX_GET_MR(ctx) ((ctx).type == SCTX_MENU_ROOT ? \
86 (ctx).menu_root.menu_root : NULL)
88 /* ---------------------------- imports ------------------------------------ */
90 /* This external is safe. It's written only during startup. */
91 extern XContext MenuContext
;
93 /* ---------------------------- included code files ------------------------ */
95 /* ---------------------------- local types -------------------------------- */
97 /* patch to pass the last popups position hints to popup_func */
102 unsigned is_last_menu_pos_hints_valid
: 1;
103 unsigned do_ignore_pos_hints
: 1;
104 unsigned do_warp_title
: 1;
106 struct MenuPosHints pos_hints
;
109 typedef struct MenuInfo
112 int n_destroyed_menus
;
115 typedef struct MenuSizingParameters
118 /* number of item labels present in the item format */
119 int used_item_labels
;
120 /* same for mini icons */
125 MenuItemPartSizesT i
;
129 unsigned is_popup_indicator_used
: 1;
131 } MenuSizingParameters
;
137 } string_context_type_t
;
141 string_context_type_t type
;
144 string_context_type_t type
;
149 string_context_type_t type
;
157 Bool (*string_handler
)(
158 char *string
, char delimiter
,
159 string_context_t
*user_data
);
162 /* ---------------------------- menu loop types ---------------------------- */
166 MENU_MLOOP_RET_NORMAL
,
173 unsigned do_popup_immediately
: 1;
174 /* used for delay popups, to just popup the menu */
175 unsigned do_popup_now
: 1;
176 unsigned do_popdown_now
: 1;
177 /* used for keystrokes, to popup and move to that menu */
178 unsigned do_popup_and_warp
: 1;
179 unsigned do_force_reposition
: 1;
180 unsigned do_force_popup
: 1;
181 unsigned do_popdown
: 1;
182 unsigned do_popup
: 1;
183 unsigned do_menu
: 1;
184 unsigned do_recycle_event
: 1;
185 unsigned do_propagate_event_into_submenu
: 1;
186 unsigned has_mouse_moved
: 1;
187 unsigned is_off_menu_allowed
: 1;
188 unsigned is_key_press
: 1;
189 unsigned is_item_entered_by_key_press
: 1;
190 unsigned is_motion_faked
: 1;
191 unsigned is_popped_up_by_timeout
: 1;
192 unsigned is_pointer_in_active_item_area
: 1;
193 unsigned is_motion_first
: 1;
194 unsigned is_release_first
: 1;
195 unsigned is_submenu_mapped
: 1;
196 unsigned was_item_unposted
: 1;
197 unsigned is_button_release
: 1;
205 int popdown_delay_10ms
;
206 int popup_delay_10ms
;
214 /* used to reduce network traffic with delayed popup/popdown */
215 MenuItem
*mi_with_popup
;
216 MenuItem
*mi_wants_popup
;
217 MenuItem
*miRemovedSubmenu
;
220 /* values that are set once when the menu loop is entered */
221 typedef struct mloop_static_info_t
226 unsigned int event_mask
;
227 } mloop_static_info_t
;
229 /* ---------------------------- forward declarations ----------------------- */
231 /* ---------------------------- local variables ---------------------------- */
233 /* This global is saved and restored every time a function is called that
234 * might modify them, so we can safely let it live outside a function. */
235 static saved_pos_hints last_saved_pos_hints
=
238 { 0, 0, 0.0, 0.0, 0, 0, False
, False
, False
, False
}
241 /* structures for menus */
242 static MenuInfo Menus
;
244 /* ---------------------------- exported variables (globals) --------------- */
246 /* ---------------------------- local functions ---------------------------- */
248 static void __menu_execute_function(const exec_context_t
**pexc
, char *action
)
250 const exec_context_t
*exc
;
251 exec_context_changes_t ecc
;
254 ecc
.w
.w
= ((*pexc
)->w
.fw
) ? FW_W((*pexc
)->w
.fw
) : None
;
255 exc
= exc_clone_context(*pexc
, &ecc
, ECC_W
);
256 old_emf
= Scr
.flags
.is_executing_menu_function
;
257 Scr
.flags
.is_executing_menu_function
= 1;
258 execute_function(NULL
, exc
, action
, FUNC_DONT_EXPAND_COMMAND
);
259 Scr
.flags
.is_executing_menu_function
= old_emf
;
260 exc_destroy_context(exc
);
261 /* See if the window has been deleted */
262 if (!check_if_fvwm_window_exists((*pexc
)->w
.fw
))
267 exc
= exc_clone_context(
268 *pexc
, &ecc
, ECC_FW
| ECC_W
| ECC_WCONTEXT
);
269 exc_destroy_context(*pexc
);
276 static Bool
pointer_in_active_item_area(int x_offset
, MenuRoot
*mr
)
278 float ratio
= (float)MST_ACTIVE_AREA_PERCENT(mr
) / 100.0;
280 if (MST_ACTIVE_AREA_PERCENT(mr
) >= 100)
284 if (MST_USE_LEFT_SUBMENUS(mr
))
287 MR_ITEM_X_OFFSET(mr
) + MR_ITEM_WIDTH(mr
) -
288 MR_ITEM_WIDTH(mr
) * ratio
);
293 MR_ITEM_X_OFFSET(mr
) + MR_ITEM_WIDTH(mr
) * ratio
);
297 static Bool
pointer_in_passive_item_area(int x_offset
, MenuRoot
*mr
)
299 float ratio
= (float)MST_ACTIVE_AREA_PERCENT(mr
) / 100.0;
301 if (MST_ACTIVE_AREA_PERCENT(mr
) >= 100)
305 if (MST_USE_LEFT_SUBMENUS(mr
))
308 MR_ITEM_X_OFFSET(mr
) + MR_ITEM_WIDTH(mr
) * ratio
);
313 MR_ITEM_X_OFFSET(mr
) + MR_ITEM_WIDTH(mr
) -
314 MR_ITEM_WIDTH(mr
) * ratio
);
322 static void warp_pointer_to_title(MenuRoot
*mr
)
325 dpy
, 0, MR_WINDOW(mr
), 0, 0, 0, 0,
326 menudim_middle_x_offset(&MR_DIM(mr
)),
327 menuitem_middle_y_offset(MR_FIRST_ITEM(mr
), MR_STYLE(mr
)));
330 static MenuItem
*warp_pointer_to_item(
331 MenuRoot
*mr
, MenuItem
*mi
, Bool do_skip_title
)
335 while (MI_NEXT_ITEM(mi
) != NULL
&&
336 (!MI_IS_SELECTABLE(mi
) || MI_IS_TEAR_OFF_BAR(mi
)))
338 /* skip separators, titles and tear off bars until the
339 * first 'real' item is found */
340 mi
= MI_NEXT_ITEM(mi
);
345 mi
= MR_LAST_ITEM(mr
);
352 dpy
, 0, MR_WINDOW(mr
), 0, 0, 0, 0,
353 menudim_middle_x_offset(&MR_DIM(mr
)),
354 menuitem_middle_y_offset(mi
, MR_STYLE(mr
)));
360 * menu animation functions
363 /* prepares the parameters to be passed to AnimatedMoveOfWindow
364 * mr - the menu instance that holds the menu item
365 * fw - the FvwmWindow structure to check against allowed functions */
366 static void get_menu_repaint_transparent_parameters(
367 MenuRepaintTransparentParameters
*pmrtp
, MenuRoot
*mr
, FvwmWindow
*fw
)
376 /* Undo the animation of a menu */
377 static void animated_move_back(
378 MenuRoot
*mr
, Bool do_warp_pointer
, FvwmWindow
*fw
)
380 MenuRepaintTransparentParameters mrtp
;
384 if (MR_XANIMATION(mr
) == 0)
388 if (menu_get_geometry(
389 mr
, &JunkRoot
, &act_x
, &act_y
, &JunkWidth
, &JunkHeight
,
390 &JunkBW
, &JunkDepth
))
392 Bool transparent_bg
= False
;
395 if (ST_HAS_MENU_CSET(MR_STYLE(mr
)) &&
396 CSET_IS_TRANSPARENT(ST_CSET_MENU(MR_STYLE(mr
))))
398 transparent_bg
= True
;
399 get_menu_repaint_transparent_parameters(
402 AnimatedMoveOfWindow(
403 MR_WINDOW(mr
), act_x
, act_y
, act_x
- MR_XANIMATION(mr
),
404 act_y
, do_warp_pointer
, -1, NULL
,
405 (transparent_bg
)? &mrtp
:NULL
);
406 MR_XANIMATION(mr
) = 0;
412 /* move a menu or a tear-off menu preserving transparency.
413 * tear-off menus are moved with their frame coordinates. */
414 static void move_any_menu(
415 MenuRoot
*mr
, MenuParameters
*pmp
, int endX
, int endY
)
417 if (MR_IS_TEAR_OFF_MENU(mr
))
421 /* this moves the tearoff menu, updating of transparency
422 * will not be as good as if menu repaint parameters
424 AnimatedMoveFvwmWindow(
425 pmp
->tear_off_root_menu_window
,
426 FW_W_FRAME(pmp
->tear_off_root_menu_window
),
427 -1, -1, endX
, endY
, False
, 0, &fFull
);
435 menu_get_geometry(mr
, &JunkRoot
, &x
, &y
, &JunkWidth
,
436 &JunkHeight
, &JunkBW
, &JunkDept
);
437 if (x
== endX
&& y
== endY
)
441 if (ST_HAS_MENU_CSET(MR_STYLE(mr
)) &&
442 CSET_IS_TRANSPARENT(ST_CSET_MENU(MR_STYLE(mr
))))
444 MenuRepaintTransparentParameters mrtp
;
446 get_menu_repaint_transparent_parameters(
447 &mrtp
, mr
, (*pmp
->pexc
)->w
.fw
);
448 update_transparent_menu_bg(
449 &mrtp
, x
, y
, endX
, endY
, endX
, endY
);
450 XMoveWindow(dpy
, MR_WINDOW(mr
), endX
, endY
);
451 repaint_transparent_menu(
452 &mrtp
, False
, endX
,endY
, endX
, endY
, True
);
456 XMoveWindow(dpy
, MR_WINDOW(mr
), endX
, endY
);
461 /* ---------------------------- submenu function --------------------------- */
463 /* Search for a submenu that was popped up by the given item in the given
464 * instance of the menu. */
465 static MenuRoot
*seek_submenu_instance(
466 MenuRoot
*parent_menu
, MenuItem
*parent_item
)
470 for (mr
= Menus
.all
; mr
!= NULL
; mr
= MR_NEXT_MENU(mr
))
472 if (MR_PARENT_MENU(mr
) == parent_menu
&&
473 MR_PARENT_ITEM(mr
) == parent_item
)
483 static Bool
is_submenu_mapped(MenuRoot
*parent_menu
, MenuItem
*parent_item
)
485 XWindowAttributes win_attribs
;
488 mr
= seek_submenu_instance(parent_menu
, parent_item
);
494 if (MR_WINDOW(mr
) == None
)
498 if (!XGetWindowAttributes(dpy
, MR_WINDOW(mr
), &win_attribs
))
503 return (win_attribs
.map_state
== IsViewable
);
506 /* Returns the menu root that a given menu item pops up */
507 static MenuRoot
*mr_popup_for_mi(MenuRoot
*mr
, MenuItem
*mi
)
510 MenuRoot
*menu
= NULL
;
512 /* This checks if mi is != NULL too */
513 if (!mi
|| !MI_IS_POPUP(mi
))
518 /* first look for a menu that is aleady mapped */
519 menu
= seek_submenu_instance(mr
, mi
);
525 /* just look past "Popup " in the action, and find that menu root */
526 menu_name
= PeekToken(SkipNTokens(MI_ACTION(mi
), 1), NULL
);
527 menu
= menus_find_menu(menu_name
);
532 /* ---------------------------- item handling ------------------------------ */
537 * Returns the menu item the pointer is over and optionally the offset
538 * from the left side of the menu entry (if px_offset is != NULL) and
539 * the MenuRoot the pointer is over (if pmr is != NULL).
541 static MenuItem
*find_entry(
543 int *px_offset
/*NULL means don't return this value */,
544 MenuRoot
**pmr
/*NULL means don't return this value */,
545 /* values passed in from caller it FQueryPointer was already called
547 Window p_child
, int p_rx
, int p_ry
)
556 /* x_offset returns the x offset of the pointer in the found menu item
566 /* get the pointer position */
570 dpy
, Scr
.Root
, &JunkRoot
, &Child
,
571 &root_x
, &root_y
, &JunkX
, &JunkY
, &JunkMask
))
573 /* pointer is on a different screen */
583 /* find out the menu the pointer is in */
584 if (pmp
->tear_off_root_menu_window
!= NULL
&&
585 Child
== FW_W_FRAME(pmp
->tear_off_root_menu_window
))
587 /* we're in the top level torn off menu */
588 Child
= FW_W(pmp
->tear_off_root_menu_window
);
590 if (XFindContext(dpy
, Child
, MenuContext
, (caddr_t
*)&mr
) == XCNOENT
)
594 /* get position in that child window */
595 if (!XTranslateCoordinates(
596 dpy
, Scr
.Root
, MR_WINDOW(mr
), root_x
, root_y
, &x
, &y
,
601 if (x
< 0 || y
< 0 || x
>= MR_WIDTH(mr
) || y
>= MR_HEIGHT(mr
))
609 r
= MST_RELIEF_THICKNESS(mr
);
610 /* look for the entry that the mouse is in */
611 for (mi
= MR_FIRST_ITEM(mr
); mi
; mi
= MI_NEXT_ITEM(mi
))
616 a
= (MI_PREV_ITEM(mi
) &&
617 MI_IS_SELECTABLE(MI_PREV_ITEM(mi
))) ? r
/ 2 : 0;
618 if (!MI_IS_SELECTABLE(mi
))
622 else if (MI_NEXT_ITEM(mi
) &&
623 MI_IS_SELECTABLE(MI_NEXT_ITEM(mi
)))
631 if (y
>= MI_Y_OFFSET(mi
) - a
&&
632 y
< MI_Y_OFFSET(mi
) + MI_HEIGHT(mi
) + b
)
637 if (x
< MR_ITEM_X_OFFSET(mr
) ||
638 x
>= MR_ITEM_X_OFFSET(mr
) + MR_ITEM_WIDTH(mr
) - 1)
650 /* ---------------------------- keyboard shortcuts ------------------------- */
652 static Bool
is_double_click(
653 Time t0
, MenuItem
*mi
, MenuParameters
*pmp
, MenuReturn
*pmret
,
654 double_keypress
*pdkp
, Bool has_mouse_moved
)
656 if ((*pmp
->pexc
)->x
.elast
->type
== KeyPress
)
660 if (fev_get_evtime() - t0
>= MST_DOUBLE_CLICK_TIME(pmp
->menu
))
668 if (!pmp
->flags
.has_default_action
&&
669 (mi
&& mi
== MR_FIRST_ITEM(pmp
->menu
) && MI_IS_SELECTABLE(mi
)))
673 if (pmp
->flags
.is_submenu
)
677 if (pmp
->flags
.is_invoked_by_key_press
&& pdkp
->timestamp
== 0)
685 /* ---------------------------- item label parsing ------------------------- */
689 * scanForHotkeys - Look for hotkey markers in a MenuItem
693 * it - MenuItem to scan
694 * column - The column number in which to look for a hotkey.
697 static void scanForHotkeys(
698 MenuItem
*it
, int column
)
704 /* Get start of string */
705 start
= MI_LABEL(it
)[column
];
706 /* Scan whole string */
707 for (s
= start
; *s
!= '\0'; s
++)
715 /* found a hotkey - only one hotkey per item */
718 /* Just an escaped '&'; copy the string down over it */
719 for (t
= s
; *t
!= '\0'; t
++)
727 /* It's a hot key marker - work out the offset value */
728 MI_HOTKEY_COFFSET(it
) = s
- start
;
729 MI_HOTKEY_COLUMN(it
) = column
;
730 MI_HAS_HOTKEY(it
) = (s
[1] != '\0');
731 MI_IS_HOTKEY_AUTOMATIC(it
) = 0;
732 for ( ; *s
!= '\0'; s
++)
742 static void __copy_down(char *remove_from
, char *remove_to
)
747 for (t1
= remove_from
, t2
= remove_to
; *t2
!= '\0'; t2
++, t1
++)
756 static int __check_for_delimiter(char *s
, const string_def_t
*string_defs
)
760 for (type
= 0; string_defs
[type
].delimiter
!= '\0'; type
++)
762 if (s
[0] == string_defs
[type
].delimiter
)
764 if (s
[1] != string_defs
[type
].delimiter
)
770 /* escaped delimiter, copy the
771 * string down over it */
782 /* This scans for strings within delimiters and calls a callback based
783 * on the delimiter found for each found string */
784 static void scanForStrings(
785 char *instring
, const string_def_t
*string_defs
,
786 string_context_t
*context
)
793 /* string is set whenever type >= 0, and unused otherwise
794 * set to NULL to supress compiler warning */
796 for (s
= instring
; *s
!= '\0'; s
++)
800 /* look for starting delimiters */
801 type
= __check_for_delimiter(s
, string_defs
);
804 /* start of a string */
809 s
[0] == string_defs
[type
].delimiter
&&
810 s
[1] != string_defs
[type
].delimiter
)
812 /* found ending delimiter */
815 /* terminate the string pointer */
817 is_valid
= string_defs
[type
].string_handler(
818 string
, string_defs
[type
].delimiter
, context
);
819 /* restore the string */
820 s
[0] = string_defs
[type
].delimiter
;
824 /* the string was OK, remove it from
826 __copy_down(string
- 1, s
+ 1);
827 /* continue next iteration at the
828 * first character after the string */
834 else if (s
[0] == string_defs
[type
].delimiter
)
836 /* escaped delimiter, copy the string down over
838 __copy_down(s
, s
+ 1);
843 /* Side picture support: this scans for a color int the menu name
845 static Bool
__scan_for_color(
846 char *name
, char type
, string_context_t
*context
)
848 if (type
!= '^' || SCTX_GET_MR(*context
) == NULL
)
853 if (MR_HAS_SIDECOLOR(SCTX_GET_MR(*context
)))
858 MR_SIDECOLOR(SCTX_GET_MR(*context
)) = GetColor(name
);
859 MR_HAS_SIDECOLOR(SCTX_GET_MR(*context
)) = True
;
864 static Bool
__scan_for_pixmap(
865 char *name
, char type
, string_context_t
*context
)
868 FvwmPictureAttributes fpa
;
869 int current_mini_icon
;
871 /* check that more pictures are allowed before trying to load the
873 current_mini_icon
= -999999999;
877 if (SCTX_GET_MR(*context
) == NULL
)
881 if (MR_SIDEPIC(SCTX_GET_MR(*context
)))
887 /* menu item picture, requires menu item */
888 if (SCTX_GET_MI(*context
) == NULL
)
892 if (MI_PICTURE(SCTX_GET_MI(*context
)))
898 /* mini icon - look for next free spot */
899 if (SCTX_GET_MI(*context
) == NULL
)
903 current_mini_icon
= 0;
904 while (current_mini_icon
< MAX_MENU_ITEM_MINI_ICONS
)
907 MI_MINI_ICON(SCTX_GET_MI(*context
))
917 if (current_mini_icon
== MAX_MENU_ITEM_MINI_ICONS
)
927 p
= PCacheFvwmPicture(
928 dpy
, Scr
.NoFocusWin
, NULL
, name
, fpa
);
931 fvwm_msg(WARN
, "scanForPixmap",
932 "Couldn't load image from %s", name
);
934 /* return true to make missing pictures not appear in the
942 MR_SIDEPIC(SCTX_GET_MR(*context
)) = p
;
946 MI_PICTURE(SCTX_GET_MI(*context
)) = p
;
947 MI_HAS_PICTURE(SCTX_GET_MI(*context
)) = True
;
951 MI_MINI_ICON(SCTX_GET_MI(*context
))[current_mini_icon
] = p
;
952 MI_HAS_PICTURE(SCTX_GET_MI(*context
)) = True
;
960 /* ---------------------------- item list handling ------------------------- */
962 static void unlink_item_from_menu(
963 MenuRoot
*mr
, MenuItem
*mi
)
968 next
= MI_NEXT_ITEM(mi
);
969 prev
= MI_PREV_ITEM(mi
);
972 MI_PREV_ITEM(next
) = prev
;
976 MR_LAST_ITEM(mr
) = prev
;
980 MI_NEXT_ITEM(prev
) = next
;
984 MR_FIRST_ITEM(mr
) = next
;
986 MI_NEXT_ITEM(mi
) = NULL
;
987 MI_PREV_ITEM(mi
) = NULL
;
993 /* Add the given menu item to the menu. If the first item of the menu is a
994 * title, and the do_replace_title flag is True, the old title is deleted and
995 * replaced by the new item. Otherwise the item is appended at the end of the
997 static void append_item_to_menu(
998 MenuRoot
*mr
, MenuItem
*mi
, Bool do_replace_title
)
1000 if (MR_FIRST_ITEM(mr
) == NULL
)
1002 MR_FIRST_ITEM(mr
) = mi
;
1003 MR_LAST_ITEM(mr
) = mi
;
1004 MI_NEXT_ITEM(mi
) = NULL
;
1005 MI_PREV_ITEM(mi
) = NULL
;
1007 else if (do_replace_title
)
1009 if (MI_IS_TITLE(MR_FIRST_ITEM(mr
)))
1011 if (MR_FIRST_ITEM(mr
) == MR_LAST_ITEM(mr
))
1013 MR_LAST_ITEM(mr
) = mi
;
1015 if (MI_NEXT_ITEM(MR_FIRST_ITEM(mr
)) != NULL
)
1017 MI_PREV_ITEM(MI_NEXT_ITEM(
1018 MR_FIRST_ITEM(mr
))) = mi
;
1020 MI_NEXT_ITEM(mi
) = MI_NEXT_ITEM(MR_FIRST_ITEM(mr
));
1021 menuitem_free(MR_FIRST_ITEM(mr
));
1025 MI_PREV_ITEM(MR_FIRST_ITEM(mr
)) = mi
;
1026 MI_NEXT_ITEM(mi
) = MR_FIRST_ITEM(mr
);
1028 MI_PREV_ITEM(mi
) = NULL
;
1029 MR_FIRST_ITEM(mr
) = mi
;
1033 MI_NEXT_ITEM(MR_LAST_ITEM(mr
)) = mi
;
1034 MI_PREV_ITEM(mi
) = MR_LAST_ITEM(mr
);
1035 MR_LAST_ITEM(mr
) = mi
;
1041 static void clone_menu_item_list(
1042 MenuRoot
*dest_mr
, MenuRoot
*src_mr
)
1045 MenuItem
*cloned_mi
;
1048 MR_FIRST_ITEM(dest_mr
) = NULL
;
1049 MR_LAST_ITEM(dest_mr
) = NULL
;
1050 /* traverse the menu and all its continuations */
1051 for (mr
= src_mr
; mr
!= NULL
; mr
= MR_CONTINUATION_MENU(mr
))
1053 /* duplicate all items in the current menu */
1054 for (mi
= MR_FIRST_ITEM(mr
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
1056 if (MI_IS_CONTINUATION(mi
))
1058 /* skip this item */
1061 cloned_mi
= menuitem_clone(mi
);
1062 append_item_to_menu(dest_mr
, cloned_mi
, False
);
1069 /* ---------------------------- MenuRoot maintenance functions ------------- */
1071 /* Extract interesting values from the item format string that are needed by
1072 * the size_menu_... functions. */
1073 static void calculate_item_sizes(MenuSizingParameters
*msp
)
1076 MenuItemPartSizesT mipst
;
1078 Bool do_reverse_icon_order
=
1079 (MST_USE_LEFT_SUBMENUS(msp
->menu
)) ? True
: False
;
1081 memset(&(msp
->max
), 0, sizeof(msp
->max
));
1082 /* Calculate the widths for all columns of all items. */
1083 for (mi
= MR_FIRST_ITEM(msp
->menu
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
1085 if (MI_IS_TITLE(mi
))
1088 mi
, &mipst
, MST_PTITLEFONT(msp
->menu
),
1089 do_reverse_icon_order
);
1094 mi
, &mipst
, MST_PSTDFONT(msp
->menu
),
1095 do_reverse_icon_order
);
1097 /* adjust maximums */
1098 if (msp
->max
.i
.triangle_width
< mipst
.triangle_width
)
1100 msp
->max
.i
.triangle_width
= mipst
.triangle_width
;
1102 if (msp
->max
.i
.title_width
< mipst
.title_width
)
1104 msp
->max
.i
.title_width
= mipst
.title_width
;
1106 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
1108 if (msp
->max
.i
.label_width
[i
] < mipst
.label_width
[i
])
1110 msp
->max
.i
.label_width
[i
] =
1111 mipst
.label_width
[i
];
1114 if (msp
->max
.i
.picture_width
< mipst
.picture_width
)
1116 msp
->max
.i
.picture_width
= mipst
.picture_width
;
1118 for (i
= 0; i
< MAX_MENU_ITEM_MINI_ICONS
; i
++)
1120 if (msp
->max
.i
.icon_width
[i
] < mipst
.icon_width
[i
])
1122 msp
->max
.i
.icon_width
[i
] = mipst
.icon_width
[i
];
1126 if (MR_SIDEPIC(msp
->menu
))
1128 msp
->max
.sidepic_width
= MR_SIDEPIC(msp
->menu
)->width
;
1130 else if (MST_SIDEPIC(msp
->menu
))
1132 msp
->max
.sidepic_width
= MST_SIDEPIC(msp
->menu
)->width
;
1140 * Calculate the positions of the columns in the menu.
1141 * Called by make_menu().
1144 static void size_menu_horizontally(MenuSizingParameters
*msp
)
1147 Bool sidepic_is_left
= True
;
1149 int sidepic_space
= 0;
1150 int label_offset
[MAX_MENU_ITEM_LABELS
];
1151 char lcr_column
[MAX_MENU_ITEM_LABELS
];
1154 int relief_thickness
= MST_RELIEF_THICKNESS(msp
->menu
);
1156 MAX_MENU_ITEM_LABELS
+ MAX_MENU_ITEM_MINI_ICONS
+
1157 1 /* triangle */ + 2 /* relief markers */];
1158 int used_objects
= 0;
1159 int left_objects
= 0;
1160 int right_objects
= 0;
1162 unsigned char icons_placed
= 0;
1163 Bool sidepic_placed
= False
;
1164 Bool triangle_placed
= False
;
1165 Bool relief_begin_placed
= False
;
1166 Bool relief_end_placed
= False
;
1170 Bool is_last_object_left
= True
;
1171 unsigned char columns_placed
= 0;
1177 memset(item_order
, 0, sizeof(item_order
));
1178 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
1180 lcr_column
[i
] = 'l';
1183 /* Now calculate the offsets for the columns. */
1184 format
= MST_ITEM_FORMAT(msp
->menu
);
1187 format
= (MST_USE_LEFT_SUBMENUS(msp
->menu
)) ?
1188 DEFAULT_LEFT_MENU_ITEM_FORMAT
:
1189 DEFAULT_MENU_ITEM_FORMAT
;
1191 /* Place the individual items off the menu in case they are not
1192 * set in the format string. */
1193 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
1195 label_offset
[i
] = 2 * Scr
.MyDisplayWidth
;
1198 x
= MST_BORDER_WIDTH(msp
->menu
);
1199 while (*format
&& !done
)
1209 /* Insert a gap of %d pixels. */
1210 if (sscanf(format
, "%d.%d%n", &gap_left
,
1211 &gap_right
, &chars
) >= 2 ||
1212 (sscanf(format
, "%d.%n", &gap_left
,
1213 &chars
) >= 1 && chars
> 0) ||
1214 sscanf(format
, "%d%n", &gap_left
,
1216 sscanf(format
, ".%d%n", &gap_right
,
1219 if (gap_left
> MR_SCREEN_WIDTH(msp
->menu
) ||
1220 gap_left
< -MR_SCREEN_WIDTH(msp
->menu
))
1224 if (gap_right
> MR_SCREEN_HEIGHT(msp
->menu
) ||
1225 gap_right
< -MR_SCREEN_HEIGHT(msp
->menu
))
1229 /* Skip the number. */
1232 else if (*format
== '.')
1234 /* Skip a dot without values */
1246 /* A left, center or right aligned column. */
1247 if (columns_placed
>=
1248 MAX_MENU_ITEM_LABELS
)
1253 msp
->max
.i
.label_width
[columns_placed
]
1259 lcr_column
[columns_placed
] = *format
;
1261 label_offset
[columns_placed
] = x
;
1262 x
+= msp
->max
.i
.label_width
[columns_placed
] +
1264 item_order
[used_objects
++] =
1265 &(label_offset
[columns_placed
]);
1266 if (is_last_object_left
&& (*format
== 'l'))
1272 is_last_object_left
= False
;
1290 sidepic_placed
= True
;
1291 if (msp
->max
.sidepic_width
<= 0)
1296 MR_SIDEPIC_X_OFFSET(msp
->menu
) = x
;
1297 sidepic_is_left
= first
;
1298 sidepic_space
= msp
->max
.sidepic_width
+
1299 ((sidepic_is_left
) ?
1300 gap_left
: gap_right
);
1301 x
+= msp
->max
.sidepic_width
+ gap_right
;
1306 MAX_MENU_ITEM_MINI_ICONS
)
1310 if (msp
->max
.i
.icon_width
[icons_placed
] > 0)
1313 MR_ICON_X_OFFSET(msp
->menu
)
1315 x
+= msp
->max
.i
.icon_width
1316 [icons_placed
] + gap_right
;
1317 item_order
[used_objects
++] =
1318 &(MR_ICON_X_OFFSET(msp
->menu
)
1320 if (is_last_object_left
)
1332 if (!relief_begin_placed
)
1334 relief_begin_placed
= True
;
1336 MR_HILIGHT_X_OFFSET(msp
->menu
) =
1338 x
+= relief_thickness
+
1340 relief_gap
+= gap_right
;
1341 item_order
[used_objects
++] =
1342 &(MR_HILIGHT_X_OFFSET(
1344 if (is_last_object_left
)
1353 else if (!relief_end_placed
)
1355 relief_end_placed
= True
;
1356 x
+= relief_thickness
+ gap_left
;
1357 /* This is a hack: for now we record
1358 * the x coordinate of the end of the
1359 * hilight area, but later we'll place
1360 * the width in here. */
1361 MR_HILIGHT_WIDTH(msp
->menu
) = x
;
1363 relief_gap
+= gap_left
;
1364 item_order
[used_objects
++] =
1365 &(MR_HILIGHT_WIDTH(msp
->menu
));
1371 /* the triangle for popup menus */
1372 if (triangle_placed
)
1376 triangle_placed
= True
;
1377 if (msp
->max
.i
.triangle_width
> 0)
1380 MR_TRIANGLE_X_OFFSET(
1382 MR_IS_LEFT_TRIANGLE(msp
->menu
) =
1384 x
+= msp
->max
.i
.triangle_width
+
1386 item_order
[used_objects
++] =
1387 &(MR_TRIANGLE_X_OFFSET(
1389 if (is_last_object_left
&&
1396 is_last_object_left
= False
;
1402 /* Simply add a gap. */
1403 x
+= gap_right
+ gap_left
;
1406 x
+= MENU_TAB_WIDTH
* FlocaleTextWidth(
1408 msp
->menu
), " ", 1);
1411 /* Advance the x position. */
1412 x
+= FlocaleTextWidth(
1413 MST_PSTDFONT(msp
->menu
), format
,
1417 /* Ignore unknown characters. */
1419 } /* switch (*format) */
1422 x
+= MENU_TAB_WIDTH
* FlocaleTextWidth(
1423 MST_PSTDFONT(msp
->menu
), " ", 1);
1426 /* Advance the x position. */
1427 x
+= FlocaleTextWidth(
1428 MST_PSTDFONT(msp
->menu
), format
, 1);
1431 /* Ignore unknown characters. */
1433 } /* switch (*format) */
1436 } /* while (*format) */
1437 /* stored for vertical sizing */
1438 msp
->used_item_labels
= columns_placed
;
1439 msp
->used_mini_icons
= icons_placed
;
1441 /* Hide unplaced parts of the menu. */
1442 if (!sidepic_placed
)
1444 MR_SIDEPIC_X_OFFSET(msp
->menu
) =
1445 2 * MR_SCREEN_WIDTH(msp
->menu
);
1447 for (i
= icons_placed
; i
< MAX_MENU_ITEM_MINI_ICONS
; i
++)
1449 MR_ICON_X_OFFSET(msp
->menu
)[i
] =
1450 2 * MR_SCREEN_WIDTH(msp
->menu
);
1452 if (!triangle_placed
)
1454 MR_TRIANGLE_X_OFFSET(msp
->menu
) =
1455 2 * MR_SCREEN_WIDTH(msp
->menu
);
1457 msp
->flags
.is_popup_indicator_used
= triangle_placed
;
1459 total_width
= x
- MST_BORDER_WIDTH(msp
->menu
);
1460 d
= (sidepic_space
+ 2 * relief_thickness
+
1461 max(msp
->max
.i
.title_width
, msp
->max
.i
.picture_width
)) -
1465 int m
= 1 - left_objects
;
1466 int n
= 1 + used_objects
- left_objects
- right_objects
;
1468 /* The title is larger than all menu items. Stretch the
1469 * gaps between the items up to the total width of the
1471 for (i
= 0; i
< used_objects
; i
++)
1473 if (i
< left_objects
)
1477 if (i
>= used_objects
- right_objects
)
1479 /* Right aligned item. */
1480 *(item_order
[i
]) += d
;
1484 /* Neither left nor right aligned item.
1485 * Divide the overhead gap evenly
1486 * between the items. */
1487 *(item_order
[i
]) += d
* (m
+ i
) / n
;
1491 if (!sidepic_is_left
)
1493 MR_SIDEPIC_X_OFFSET(msp
->menu
) += d
;
1496 MR_WIDTH(msp
->menu
) =
1497 total_width
+ 2 * MST_BORDER_WIDTH(msp
->menu
);
1498 MR_ITEM_WIDTH(msp
->menu
) = total_width
- sidepic_space
;
1499 MR_ITEM_X_OFFSET(msp
->menu
) = MST_BORDER_WIDTH(msp
->menu
);
1500 if (sidepic_is_left
)
1502 MR_ITEM_X_OFFSET(msp
->menu
) += sidepic_space
;
1504 if (!relief_begin_placed
)
1506 MR_HILIGHT_X_OFFSET(msp
->menu
) = MR_ITEM_X_OFFSET(msp
->menu
);
1508 if (relief_end_placed
)
1510 MR_HILIGHT_WIDTH(msp
->menu
) =
1511 MR_HILIGHT_WIDTH(msp
->menu
) -
1512 MR_HILIGHT_X_OFFSET(msp
->menu
);
1516 MR_HILIGHT_WIDTH(msp
->menu
) =
1517 MR_ITEM_WIDTH(msp
->menu
) +
1518 MR_ITEM_X_OFFSET(msp
->menu
) -
1519 MR_HILIGHT_X_OFFSET(msp
->menu
);
1522 /* Now calculate the offsets for the individual labels. */
1523 for (mi
= MR_FIRST_ITEM(msp
->menu
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
1525 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
1527 if (MI_LABEL(mi
)[i
] == NULL
)
1531 if (!MI_IS_TITLE(mi
) || !MI_IS_TITLE_CENTERED(mi
))
1533 switch (lcr_column
[i
])
1536 MI_LABEL_OFFSET(mi
)[i
] =
1540 MI_LABEL_OFFSET(mi
)[i
] =
1542 (msp
->max
.i
.label_width
[i
] -
1543 MI_LABEL_OFFSET(mi
)[i
]) / 2;
1546 MI_LABEL_OFFSET(mi
)[i
] =
1548 msp
->max
.i
.label_width
[i
] -
1549 MI_LABEL_OFFSET(mi
)[i
];
1555 /* This is a centered title item (indicated by
1556 * negative width). */
1557 MI_LABEL_OFFSET(mi
)[i
] =
1558 menudim_middle_x_offset(
1559 &MR_DIM(msp
->menu
)) -
1560 MI_LABEL_OFFSET(mi
)[i
] / 2;
1568 static int calc_more_item_height(MenuSizingParameters
*msp
)
1573 MST_PSTDFONT(msp
->menu
)->height
+
1574 MST_ITEM_GAP_ABOVE(msp
->menu
) +
1575 MST_ITEM_GAP_BELOW(msp
->menu
) +
1576 MST_RELIEF_THICKNESS(msp
->menu
);
1581 static int calc_normal_item_height(MenuSizingParameters
*msp
, MenuItem
*mi
)
1586 MST_ITEM_GAP_ABOVE(msp
->menu
) +
1587 MST_ITEM_GAP_BELOW(msp
->menu
) +
1588 MST_RELIEF_THICKNESS(msp
->menu
);
1589 /* Normal text entry or an entry with a sub menu triangle */
1591 (MI_HAS_TEXT(mi
) && msp
->used_item_labels
) ||
1593 msp
->flags
.is_popup_indicator_used
))
1595 height
+= MST_PSTDFONT(msp
->menu
)->height
;
1603 * Calculate the positions of the columns in the menu.
1604 * Called by make_menu().
1607 static Bool
size_menu_vertically(MenuSizingParameters
*msp
)
1612 int relief_thickness
= MST_RELIEF_THICKNESS(msp
->menu
);
1614 Bool has_continuation_menu
= False
;
1616 MR_ITEM_TEXT_Y_OFFSET(msp
->menu
) =
1617 MST_PSTDFONT(msp
->menu
)->ascent
+ relief_thickness
+
1618 MST_ITEM_GAP_ABOVE(msp
->menu
);
1620 /* mi_prev trails one behind mi, since we need to move that
1621 into a newly-made menu if we run out of space */
1622 y
= MST_BORDER_WIDTH(msp
->menu
) + MST_VERTICAL_MARGIN_TOP(msp
->menu
);
1624 cItems
= 0, mi
= MR_FIRST_ITEM(msp
->menu
); mi
!= NULL
;
1625 mi
= MI_NEXT_ITEM(mi
), cItems
++)
1627 Bool last_item_has_relief
=
1628 (MI_PREV_ITEM(mi
)) ?
1629 MI_IS_SELECTABLE(MI_PREV_ITEM(mi
)) : False
;
1630 Bool has_mini_icon
= False
;
1631 int separator_height
;
1634 separator_height
= (last_item_has_relief
) ?
1635 MENU_SEPARATOR_HEIGHT
+ relief_thickness
:
1636 MENU_SEPARATOR_TOTAL_HEIGHT
;
1637 MI_Y_OFFSET(mi
) = y
;
1638 if (MI_IS_TITLE(mi
))
1640 MI_HEIGHT(mi
) = MST_PTITLEFONT(msp
->menu
)->height
+
1641 MST_TITLE_GAP_ABOVE(msp
->menu
) +
1642 MST_TITLE_GAP_BELOW(msp
->menu
);
1644 else if (MI_IS_SEPARATOR(mi
))
1647 MI_HEIGHT(mi
) = separator_height
;
1649 else if (MI_IS_TEAR_OFF_BAR(mi
))
1652 MI_HEIGHT(mi
) = relief_thickness
+
1653 MENU_TEAR_OFF_BAR_HEIGHT
;
1657 MI_HEIGHT(mi
) = calc_normal_item_height(msp
, mi
);
1659 if (MI_IS_TITLE(mi
))
1661 /* add space for the underlines */
1662 switch (MST_TITLE_UNDERLINES(msp
->menu
))
1665 if (last_item_has_relief
)
1666 MI_HEIGHT(mi
) += relief_thickness
;
1669 if (mi
!= MR_FIRST_ITEM(msp
->menu
))
1671 /* Space to draw the separator plus a
1673 MI_HEIGHT(mi
) += separator_height
;
1675 if (MI_NEXT_ITEM(mi
) != NULL
)
1677 /* Space to draw the separator */
1678 MI_HEIGHT(mi
) += MENU_SEPARATOR_HEIGHT
;
1682 /* Space to draw n underlines. */
1684 MENU_UNDERLINE_HEIGHT
*
1685 MST_TITLE_UNDERLINES(msp
->menu
);
1686 if (last_item_has_relief
)
1687 MI_HEIGHT(mi
) += relief_thickness
;
1691 for (i
= 0; i
< msp
->used_mini_icons
; i
++)
1693 if (MI_MINI_ICON(mi
)[i
])
1695 has_mini_icon
= True
;
1697 if (MI_MINI_ICON(mi
)[i
] &&
1699 MI_MINI_ICON(mi
)[i
]->height
+ relief_thickness
)
1701 MI_HEIGHT(mi
) = MI_MINI_ICON(mi
)[i
]->height
+
1707 if ((MI_HAS_TEXT(mi
) && msp
->used_item_labels
) ||
1710 MI_HEIGHT(mi
) += MI_PICTURE(mi
)->height
;
1714 MI_HEIGHT(mi
) = MI_PICTURE(mi
)->height
+
1719 /* this item would have to be the last item, or else
1720 * we need to add a "More..." entry pointing to a new menu */
1722 y
+ MST_BORDER_WIDTH(msp
->menu
) +
1723 MST_VERTICAL_MARGIN_BOTTOM(msp
->menu
) +
1724 ((MI_IS_SELECTABLE(mi
)) ? relief_thickness
: 0);
1725 if (menu_height
> MR_SCREEN_HEIGHT(msp
->menu
))
1727 /* Item does not fit on screen anymore. */
1730 MenuRoot
*menuContinuation
;
1731 int more_item_height
;
1733 more_item_height
= calc_more_item_height(msp
);
1734 /* Remove items form the menu until it fits (plus a
1737 MI_PREV_ITEM(mi
) != NULL
&&
1738 menu_height
> MR_SCREEN_HEIGHT(msp
->menu
))
1740 /* Remove current item. */
1742 mi
= MI_PREV_ITEM(mi
);
1745 y
+ MST_BORDER_WIDTH(msp
->menu
) +
1746 more_item_height
+ relief_thickness
+
1747 MST_VERTICAL_MARGIN_BOTTOM(msp
->menu
);
1750 MI_PREV_ITEM(mi
) == NULL
||
1751 menu_height
> MR_SCREEN_HEIGHT(msp
->menu
))
1753 fvwm_msg(ERR
, "size_menu_vertically",
1754 "Menu entry does not fit on screen");
1755 /* leave a coredump */
1760 t
= EscapeString(MR_NAME(msp
->menu
), "\"", '\\');
1761 tempname
= (char *)safemalloc(
1762 (10 + strlen(t
)) * sizeof(char));
1763 strcpy(tempname
, "Popup \"");
1764 strcat(tempname
, t
);
1765 strcat(tempname
, "$\"");
1767 /* NewMenuRoot inserts at the head of the list of menus
1768 but, we need it at the end. (Give it just the name,
1769 * which is 6 chars past the action since
1770 * strlen("Popup ")==6 ) */
1771 t
= (char *)safemalloc(strlen(MR_NAME(msp
->menu
)) + 2);
1772 strcpy(t
, MR_NAME(msp
->menu
));
1774 menuContinuation
= NewMenuRoot(t
);
1776 MR_CONTINUATION_MENU(msp
->menu
) = menuContinuation
;
1778 /* Now move this item and the remaining items into the
1780 MR_FIRST_ITEM(menuContinuation
) = MI_NEXT_ITEM(mi
);
1781 MR_LAST_ITEM(menuContinuation
) =
1782 MR_LAST_ITEM(msp
->menu
);
1783 MR_ITEMS(menuContinuation
) =
1784 MR_ITEMS(msp
->menu
) - cItems
;
1785 MI_PREV_ITEM(MI_NEXT_ITEM(mi
)) = NULL
;
1787 /* mi_prev is now the last item in the parent menu */
1788 MR_LAST_ITEM(msp
->menu
) = mi
;
1789 MR_ITEMS(msp
->menu
) = cItems
;
1790 MI_NEXT_ITEM(mi
) = NULL
;
1792 /* use the same style for the submenu */
1793 MR_STYLE(menuContinuation
) = MR_STYLE(msp
->menu
);
1794 MR_IS_LEFT_TRIANGLE(menuContinuation
) =
1795 MR_IS_LEFT_TRIANGLE(msp
->menu
);
1796 /* migo: propagate missing_submenu_func */
1797 if (MR_MISSING_SUBMENU_FUNC(msp
->menu
))
1799 MR_MISSING_SUBMENU_FUNC(menuContinuation
) =
1800 safestrdup(MR_MISSING_SUBMENU_FUNC(
1803 /* don't propagate sidepic, sidecolor, popup and
1804 * popdown actions */
1805 /* And add the entry pointing to the new menu */
1807 msp
->menu
, gettext("More&..."), tempname
,
1808 False
/* no pixmap scan */, False
, True
);
1810 has_continuation_menu
= True
;
1813 /* The menu may be empty here! */
1814 if (MR_LAST_ITEM(msp
->menu
) != NULL
&&
1815 MI_IS_SELECTABLE(MR_LAST_ITEM(msp
->menu
)))
1817 y
+= relief_thickness
;
1819 MR_HEIGHT(msp
->menu
) =
1820 y
+ MST_BORDER_WIDTH(msp
->menu
) +
1821 MST_VERTICAL_MARGIN_BOTTOM(msp
->menu
);
1823 return has_continuation_menu
;
1828 * Merge menu continuations back into the original menu.
1829 * Called by make_menu().
1832 static void merge_continuation_menus(MenuRoot
*mr
)
1834 /* merge menu continuations into one menu again - needed when changing
1835 * the font size of a long menu. */
1836 while (MR_CONTINUATION_MENU(mr
) != NULL
)
1838 MenuRoot
*cont
= MR_CONTINUATION_MENU(mr
);
1840 /* link first item of continuation to item before 'more...' */
1841 MI_NEXT_ITEM(MI_PREV_ITEM(MR_LAST_ITEM(mr
))) =
1842 MR_FIRST_ITEM(cont
);
1843 MI_PREV_ITEM(MR_FIRST_ITEM(cont
)) =
1844 MI_PREV_ITEM(MR_LAST_ITEM(mr
));
1845 menuitem_free(MR_LAST_ITEM(mr
));
1846 MR_LAST_ITEM(mr
) = MR_LAST_ITEM(cont
);
1847 MR_CONTINUATION_MENU(mr
) = MR_CONTINUATION_MENU(cont
);
1848 /* fake an empty menu so that DestroyMenu does not destroy the
1850 MR_FIRST_ITEM(cont
) = NULL
;
1851 DestroyMenu(cont
, False
, False
);
1859 * Creates the window for the menu.
1862 static void make_menu_window(MenuRoot
*mr
, Bool is_tear_off
)
1864 unsigned long valuemask
;
1865 XSetWindowAttributes attributes
;
1868 unsigned int evmask
;
1881 attributes
.background_pixel
= (MST_HAS_MENU_CSET(mr
)) ?
1882 Colorset
[MST_CSET_MENU(mr
)].bg
: MST_MENU_COLORS(mr
).back
;
1883 if (MR_WINDOW(mr
) != None
)
1885 /* just resize the existing window */
1886 XResizeWindow(dpy
, MR_WINDOW(mr
), w
, h
);
1887 /* and change the background color */
1888 valuemask
= CWBackPixel
| CWCursor
;
1889 XChangeWindowAttributes(
1890 dpy
, MR_WINDOW(mr
), valuemask
, &attributes
);
1894 /* create a new window */
1895 valuemask
= CWBackPixel
| CWEventMask
| CWCursor
| CWColormap
1896 | CWBorderPixel
| CWSaveUnder
;
1897 attributes
.border_pixel
= 0;
1898 attributes
.colormap
= Pcmap
;
1899 evmask
= XEVMASK_MENUW
;
1900 attributes
.event_mask
= 0;
1901 attributes
.cursor
= Scr
.FvwmCursors
[CRS_MENU
];
1902 attributes
.save_under
= True
;
1904 /* Create a display used to create the window. Can't use the
1905 * normal display because 'xkill' would kill the window
1906 * manager if used on a tear off menu. The display can't be
1907 * deleted right now because that would either destroy the new
1908 * window or leave it as an orphan if fvwm dies or is
1912 MR_CREATE_DPY(mr
) = XOpenDisplay(display_name
);
1913 if (MR_CREATE_DPY(mr
) == NULL
)
1915 /* Doh. Use the standard display instead. */
1916 MR_CREATE_DPY(mr
) = dpy
;
1921 MR_CREATE_DPY(mr
) = dpy
;
1923 MR_WINDOW(mr
) = XCreateWindow(
1924 MR_CREATE_DPY(mr
), Scr
.Root
, 0, 0, w
, h
,
1925 0, Pdepth
, InputOutput
, Pvisual
, valuemask
,
1927 if (MR_CREATE_DPY(mr
) != dpy
)
1929 /* We *must* synchronize the display here. Otherwise
1930 * the request will never be processed. */
1931 XSync(MR_CREATE_DPY(mr
), 1);
1933 if (MR_WINDOW(mr
) != None
)
1935 /* select events for the window from the standard
1937 XSelectInput(dpy
, MR_WINDOW(mr
), evmask
);
1939 XSaveContext(dpy
, MR_WINDOW(mr
), MenuContext
,(caddr_t
)mr
);
1948 * Generates the window for a menu
1951 static void make_menu(MenuRoot
*mr
, Bool is_tear_off
)
1953 MenuSizingParameters msp
;
1954 Bool has_continuation_menu
= False
;
1956 if (MR_MAPPED_COPIES(mr
) > 0)
1960 merge_continuation_menus(mr
);
1963 memset(&msp
, 0, sizeof(MenuSizingParameters
));
1965 calculate_item_sizes(&msp
);
1966 /* Call size_menu_horizontally first because it calculated
1967 * some values used by size_menu_vertically. */
1968 size_menu_horizontally(&msp
);
1969 has_continuation_menu
= size_menu_vertically(&msp
);
1970 /* repeat this step if the menu was split */
1971 } while (has_continuation_menu
);
1972 MR_USED_MINI_ICONS(mr
) = msp
.used_mini_icons
;
1974 MR_XANIMATION(mr
) = 0;
1975 memset(&(MR_DYNAMIC_FLAGS(mr
)), 0, sizeof(MR_DYNAMIC_FLAGS(mr
)));
1977 /* create a new window for the menu */
1978 make_menu_window(mr
, is_tear_off
);
1979 MR_IS_UPDATED(mr
) = 0;
1984 /* Make sure the menu is properly rebuilt when the style or the menu has
1986 static void update_menu(MenuRoot
*mr
, MenuParameters
*pmp
)
1990 Bool has_screen_size_changed
= False
;
1991 fscreen_scr_arg fscr
;
1993 if (MST_IS_UPDATED(mr
))
1995 /* The menu style has changed. */
1998 for (menu
= Menus
.all
; menu
; menu
= MR_NEXT_MENU(menu
))
2000 if (MR_STYLE(menu
) == MR_STYLE(mr
))
2002 /* Mark all other menus with the same style as
2004 MR_IS_UPDATED(menu
) = 1;
2007 MST_IS_UPDATED(mr
) = 0;
2009 fscr
.xypos
.x
= pmp
->screen_origin_x
;
2010 fscr
.xypos
.y
= pmp
->screen_origin_y
;
2011 FScreenGetScrRect(&fscr
, FSCREEN_XYPOS
, &JunkX
, &JunkY
, &sw
, &sh
);
2012 if (sw
!= MR_SCREEN_WIDTH(mr
) || sh
!= MR_SCREEN_HEIGHT(mr
))
2014 has_screen_size_changed
= True
;
2015 MR_SCREEN_WIDTH(mr
) = sw
;
2016 MR_SCREEN_HEIGHT(mr
) = sh
;
2018 if (MR_IS_UPDATED(mr
) || has_screen_size_changed
)
2020 /* The menu or the screen dimensions have changed. We have to
2022 make_menu(mr
, False
);
2031 * copy_menu_root - creates a new instance of an existing menu
2037 * mr - the MenuRoot structure of the existing menu
2040 static MenuRoot
*copy_menu_root(MenuRoot
*mr
)
2044 if (!mr
|| MR_COPIES(mr
) >= MAX_MENU_COPIES
)
2048 tmp
= (MenuRoot
*)safemalloc(sizeof(MenuRoot
));
2049 tmp
->d
= (MenuRootDynamic
*)safemalloc(sizeof(MenuRootDynamic
));
2050 memset(tmp
->d
, 0, sizeof(MenuRootDynamic
));
2054 MR_ORIGINAL_MENU(tmp
) = MR_ORIGINAL_MENU(mr
);
2055 MR_CONTINUATION_MENU(tmp
) = MR_CONTINUATION_MENU(mr
);
2056 MR_NEXT_MENU(tmp
) = MR_NEXT_MENU(mr
);
2057 MR_NEXT_MENU(mr
) = tmp
;
2058 MR_WINDOW(tmp
) = None
;
2059 memset(&(MR_DYNAMIC_FLAGS(tmp
)), 0, sizeof(MR_DYNAMIC_FLAGS(tmp
)));
2067 * clone_menu - duplicates an existing menu in newly allocated
2068 memory. The new menu is independent of the original.
2074 * mr - the MenuRoot structure of the existing menu
2077 static void clone_menu_root_static(
2078 MenuRoot
*dest_mr
, MenuRoot
*src_mr
)
2080 dest_mr
->s
= (MenuRootStatic
*)safemalloc(sizeof(MenuRootStatic
));
2081 /* copy everything */
2082 memcpy(dest_mr
->s
, src_mr
->s
, sizeof(MenuRootStatic
));
2083 /* special treatment for a few parts */
2084 if (MR_NAME(src_mr
) != NULL
)
2086 MR_NAME(dest_mr
) = safestrdup(MR_NAME(src_mr
));
2088 MR_COPIES(dest_mr
) = 1;
2089 MR_MAPPED_COPIES(dest_mr
) = 0;
2090 MR_POPUP_ACTION(dest_mr
) = NULL
;
2091 MR_POPDOWN_ACTION(dest_mr
) = NULL
;
2092 if (MR_MISSING_SUBMENU_FUNC(src_mr
))
2094 MR_MISSING_SUBMENU_FUNC(dest_mr
) =
2095 safestrdup(MR_MISSING_SUBMENU_FUNC(src_mr
));
2097 if (MR_HAS_SIDECOLOR(src_mr
))
2099 MR_SIDECOLOR(dest_mr
) =
2100 fvwmlib_clone_color(MR_SIDECOLOR(src_mr
));
2102 MR_SIDEPIC(dest_mr
) = PCloneFvwmPicture(MR_SIDEPIC(src_mr
));
2103 clone_menu_item_list(dest_mr
, src_mr
);
2108 static MenuRoot
*clone_menu(MenuRoot
*mr
)
2112 new_mr
= (MenuRoot
*)safemalloc(sizeof(MenuRoot
));
2113 new_mr
->d
= (MenuRootDynamic
*)safemalloc(sizeof(MenuRootDynamic
));
2114 memset(new_mr
->d
, 0, sizeof(MenuRootDynamic
));
2115 clone_menu_root_static(new_mr
, mr
);
2120 /* ---------------------------- position hints ----------------------------- */
2122 static int float_to_int_with_tolerance(float f
)
2128 low
= (int)(f
- ROUNDING_ERROR_TOLERANCE
);
2132 low
= (int)(f
+ ROUNDING_ERROR_TOLERANCE
);
2144 static void get_xy_from_position_hints(
2145 struct MenuPosHints
*ph
, int width
, int height
, int context_width
,
2146 Bool do_reverse_x
, int *ret_x
, int *ret_y
)
2153 if (ph
->is_menu_relative
)
2157 *ret_x
-= ph
->x_offset
;
2158 x_add
= width
* (-1.0 - ph
->x_factor
) +
2159 ph
->menu_width
* (1.0 - ph
->context_x_factor
);
2163 *ret_x
+= ph
->x_offset
;
2164 x_add
= width
* ph
->x_factor
+
2165 ph
->menu_width
* ph
->context_x_factor
;
2167 y_add
= height
* ph
->y_factor
;
2171 x_add
= width
* ph
->x_factor
;
2172 y_add
= height
* ph
->y_factor
;
2174 *ret_x
+= float_to_int_with_tolerance(x_add
);
2175 *ret_y
+= float_to_int_with_tolerance(y_add
);
2181 * Used by get_menu_options
2183 * The vars are named for the x-direction, but this is used for both x and y
2185 static char *get_one_menu_position_argument(
2186 char *action
, int x
, int w
, int *pFinalX
, int *x_offset
,
2187 float *width_factor
, float *context_width_factor
,
2188 Bool
*is_menu_relative
)
2190 char *token
, *orgtoken
, *naction
;
2195 float factor
= (float)w
/100;
2198 naction
= GetNextToken(action
, &token
);
2206 *width_factor
= 0.0;
2207 *context_width_factor
= 0.0;
2208 if (sscanf(token
,"o%d%n", &val
, &chars
) >= 1)
2212 x_add
+= fval
*factor
;
2213 *width_factor
-= fval
/ 100.0;
2214 *context_width_factor
+= fval
/ 100.0;
2216 else if (token
[0] == 'c')
2219 x_add
+= ((float)w
) / 2.0;
2220 *width_factor
-= 0.5;
2221 *context_width_factor
+= 0.5;
2225 if (sscanf(token
,"%d%n", &val
, &chars
) < 1)
2232 if (sscanf(token
,"%c", &c
) == 1)
2238 *width_factor
+= fval
/ 100.0;
2239 *is_menu_relative
= True
;
2247 x_add
+= fval
* factor
;
2248 *context_width_factor
+= fval
/ 100.0;
2254 x_add
+= fval
* factor
;
2255 *context_width_factor
+= fval
/ 100.0;
2258 *pFinalX
+= float_to_int_with_tolerance(x_add
);
2264 /* Returns the menu options for the menu that a given menu item pops up */
2265 static void get_popup_options(
2266 MenuParameters
*pmp
, MenuItem
*mi
, MenuOptions
*pops
)
2272 pops
->flags
.has_poshints
= 0;
2273 pops
->pos_hints
.has_screen_origin
= True
;
2274 pops
->pos_hints
.screen_origin_x
= pmp
->screen_origin_x
;
2275 pops
->pos_hints
.screen_origin_y
= pmp
->screen_origin_y
;
2276 /* just look past "Popup <name>" in the action */
2278 SkipNTokens(MI_ACTION(mi
), 2), MR_WINDOW(pmp
->menu
), NULL
,
2279 NULL
, pmp
->menu
, mi
, pops
);
2284 /* ---------------------------- menu painting functions --------------------- */
2286 static void clear_expose_menu_area(Window win
, XEvent
*e
)
2290 XClearWindow(dpy
, win
);
2295 dpy
, win
, e
->xexpose
.x
, e
->xexpose
.y
, e
->xexpose
.width
,
2296 e
->xexpose
.height
, False
);
2304 * Draws a picture on the left side of the menu
2305 * What about a SidePic Colorset ? (olicha 2002-08-21)
2308 static void paint_side_pic(MenuRoot
*mr
, XEvent
*pevent
)
2311 FvwmPicture
*sidePic
;
2315 int bw
= MST_BORDER_WIDTH(mr
);
2319 sidePic
= MR_SIDEPIC(mr
);
2321 else if (MST_SIDEPIC(mr
))
2323 sidePic
= MST_SIDEPIC(mr
);
2332 gc
= SHADOW_GC(MST_MENU_INACTIVE_GCS(mr
));
2336 gc
= FORE_GC(MST_MENU_INACTIVE_GCS(mr
));
2338 if (sidePic
->height
> MR_HEIGHT(mr
) - 2 * bw
)
2340 h
= MR_HEIGHT(mr
) - 2 * bw
;
2341 ys
= sidePic
->height
- h
;
2346 h
= sidePic
->height
;
2348 yt
= MR_HEIGHT(mr
) - bw
- sidePic
->height
;
2350 if (pevent
!= NULL
&& pevent
->type
== Expose
)
2353 pevent
->xexpose
.x
+ pevent
->xexpose
.width
<
2354 MR_SIDEPIC_X_OFFSET(mr
) ||
2355 pevent
->xexpose
.x
>=
2356 MR_SIDEPIC_X_OFFSET(mr
) + sidePic
->width
)
2358 /* out of x-range for side bar */
2362 pevent
->xexpose
.y
+ pevent
->xexpose
.height
< bw
||
2363 pevent
->xexpose
.y
>= bw
+ MR_HEIGHT(mr
))
2369 !(MR_HAS_SIDECOLOR(mr
) || MST_HAS_SIDE_COLOR(mr
)) &&
2370 pevent
->xexpose
.y
+ pevent
->xexpose
.height
< yt
)
2372 /* outside picture and no background */
2377 if (MR_HAS_SIDECOLOR(mr
))
2379 Globalgcv
.foreground
= MR_SIDECOLOR(mr
);
2381 else if (MST_HAS_SIDE_COLOR(mr
))
2383 Globalgcv
.foreground
= MST_SIDE_COLOR(mr
);
2385 if (MR_HAS_SIDECOLOR(mr
) || MST_HAS_SIDE_COLOR(mr
))
2387 Globalgcm
= GCForeground
;
2388 XChangeGC(dpy
, Scr
.ScratchGC1
, Globalgcm
, &Globalgcv
);
2390 dpy
, MR_WINDOW(mr
), Scr
.ScratchGC1
,
2391 MR_SIDEPIC_X_OFFSET(mr
), bw
, sidePic
->width
,
2392 MR_HEIGHT(mr
) - 2 * bw
);
2394 else if (sidePic
->alpha
!= None
)
2398 MR_SIDEPIC_X_OFFSET(mr
), yt
, sidePic
->width
, h
, False
);
2400 PGraphicsRenderPicture(
2401 dpy
, MR_WINDOW(mr
), sidePic
, 0, MR_WINDOW(mr
),
2402 gc
, Scr
.MonoGC
, Scr
.AlphaGC
,
2403 0, ys
, sidePic
->width
, h
,
2404 MR_SIDEPIC_X_OFFSET(mr
), yt
, sidePic
->width
, h
, False
);
2409 static Bool
paint_menu_gradient_background(
2410 MenuRoot
*mr
, XEvent
*pevent
)
2412 MenuStyle
*ms
= MR_STYLE(mr
);
2413 int bw
= MST_BORDER_WIDTH(mr
);
2418 unsigned long gcm
= GCLineWidth
;
2420 Bool do_clear
= False
;
2425 bounds
.width
= MR_WIDTH(mr
) - bw
;
2426 bounds
.height
= MR_HEIGHT(mr
) - bw
;
2427 /* H, V, D and B gradients are optimized and have
2428 * their own code here. (if no dither) */
2429 if (!ST_FACE(ms
).u
.grad
.do_dither
)
2431 switcher
= ST_FACE(ms
).gradient_type
;
2436 if (MR_IS_BACKGROUND_SET(mr
) == False
)
2441 pmap
= XCreatePixmap(
2442 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
),
2443 DEFAULT_MENU_GRADIENT_PIXMAP_THICKNESS
,
2445 pmapgc
= fvwmlib_XCreateGC(dpy
, pmap
, gcm
, &gcv
);
2447 (bounds
.width
/ ST_FACE(ms
).u
.grad
.npixels
)
2449 for (i
= 0; i
< ST_FACE(ms
).u
.grad
.npixels
; i
++)
2453 x
= i
* bounds
.width
/
2454 ST_FACE(ms
).u
.grad
.npixels
;
2457 ST_FACE(ms
).u
.grad
.xcs
[i
].pixel
);
2459 dpy
, pmap
, pmapgc
, x
, 0, dw
,
2460 DEFAULT_MENU_GRADIENT_PIXMAP_THICKNESS
);
2462 XSetWindowBackgroundPixmap(dpy
, MR_WINDOW(mr
), pmap
);
2463 XFreeGC(dpy
,pmapgc
);
2464 XFreePixmap(dpy
,pmap
);
2465 MR_IS_BACKGROUND_SET(mr
) = True
;
2470 if (MR_IS_BACKGROUND_SET(mr
) == False
)
2474 static int best_tile_width
= 0;
2477 if (best_tile_width
== 0)
2479 int tw
= DEFAULT_MENU_GRADIENT_PIXMAP_THICKNESS
;
2481 if (!XQueryBestTile(
2482 dpy
, Scr
.screen
, tw
, tw
,
2483 (unsigned int*)&best_tile_width
,
2484 (unsigned int*)&junk
))
2486 /* call failed, use default and risk a
2487 * screwed up tile */
2488 best_tile_width
= tw
;
2491 pmap
= XCreatePixmap(
2492 dpy
, MR_WINDOW(mr
), best_tile_width
,
2493 MR_HEIGHT(mr
), Pdepth
);
2494 pmapgc
= fvwmlib_XCreateGC(dpy
, pmap
, gcm
, &gcv
);
2495 dh
= (float) (bounds
.height
/
2496 ST_FACE(ms
).u
.grad
.npixels
) + 1;
2497 for (i
= 0; i
< ST_FACE(ms
).u
.grad
.npixels
; i
++)
2501 y
= i
* bounds
.height
/
2502 ST_FACE(ms
).u
.grad
.npixels
;
2505 ST_FACE(ms
).u
.grad
.xcs
[i
].pixel
);
2507 dpy
, pmap
, pmapgc
, 0, y
,
2508 best_tile_width
, dh
);
2510 XSetWindowBackgroundPixmap(dpy
, MR_WINDOW(mr
), pmap
);
2511 XFreeGC(dpy
,pmapgc
);
2512 XFreePixmap(dpy
,pmap
);
2513 MR_IS_BACKGROUND_SET(mr
) = True
;
2520 register int i
= 0, numLines
;
2523 FvwmPicture
*sidePic
= NULL
;
2527 sidePic
= MR_SIDEPIC(mr
);
2529 else if (MST_SIDEPIC(mr
))
2531 sidePic
= MST_SIDEPIC(mr
);
2535 r
.x
= pevent
->xexpose
.x
;
2536 r
.y
= pevent
->xexpose
.y
;
2537 r
.width
= pevent
->xexpose
.width
;
2538 r
.height
= pevent
->xexpose
.height
;
2544 r
.width
= MR_WIDTH(mr
) - 2 * bw
;
2545 r
.height
= MR_HEIGHT(mr
) - 2 * bw
;
2548 dpy
, Scr
.TransMaskGC
, 0, 0, &r
, 1, Unsorted
);
2549 numLines
= MR_WIDTH(mr
) + MR_HEIGHT(mr
) - 2 * bw
;
2550 for (i
= 0; i
< numLines
; i
++)
2552 if ((int)(i
* ST_FACE(ms
).u
.grad
.npixels
/ numLines
) >
2555 /* pick the next colour (skip if necc.) */
2556 cindex
= i
* ST_FACE(ms
).u
.grad
.npixels
/
2559 dpy
, Scr
.TransMaskGC
,
2560 ST_FACE(ms
).u
.grad
.xcs
[cindex
].pixel
);
2562 if (ST_FACE(ms
).gradient_type
== D_GRADIENT
)
2564 XDrawLine(dpy
, MR_WINDOW(mr
),
2565 Scr
.TransMaskGC
, 0, i
, i
, 0);
2567 else /* B_GRADIENT */
2569 XDrawLine(dpy
, MR_WINDOW(mr
), Scr
.TransMaskGC
,
2570 0, MR_HEIGHT(mr
) - 1 - i
, i
,
2575 XSetClipMask(dpy
, Scr
.TransMaskGC
, None
);
2578 if (MR_IS_BACKGROUND_SET(mr
) == False
)
2583 /* let library take care of all other gradients */
2584 pmap
= XCreatePixmap(
2585 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
),
2586 MR_HEIGHT(mr
), Pdepth
);
2587 pmapgc
= fvwmlib_XCreateGC(dpy
, pmap
, gcm
, &gcv
);
2589 /* find out the size the pixmap should be */
2590 CalculateGradientDimensions(
2591 dpy
, MR_WINDOW(mr
), ST_FACE(ms
).u
.grad
.npixels
,
2592 ST_FACE(ms
).gradient_type
,
2593 ST_FACE(ms
).u
.grad
.do_dither
, &g_width
,
2595 /* draw the gradient directly into the window */
2596 CreateGradientPixmap(
2597 dpy
, MR_WINDOW(mr
), pmapgc
,
2598 ST_FACE(ms
).gradient_type
, g_width
, g_height
,
2599 ST_FACE(ms
).u
.grad
.npixels
,
2600 ST_FACE(ms
).u
.grad
.xcs
,
2601 ST_FACE(ms
).u
.grad
.do_dither
,
2602 &(MR_STORED_PIXELS(mr
).d_pixels
),
2603 &(MR_STORED_PIXELS(mr
).d_npixels
),
2605 MR_WIDTH(mr
) - bw
, MR_HEIGHT(mr
) - bw
, NULL
);
2606 XSetWindowBackgroundPixmap(dpy
, MR_WINDOW(mr
), pmap
);
2607 XFreeGC(dpy
, pmapgc
);
2608 XFreePixmap(dpy
, pmap
);
2609 MR_IS_BACKGROUND_SET(mr
) = True
;
2617 static Bool
paint_menu_pixmap_background(
2618 MenuRoot
*mr
, XEvent
*pevent
)
2620 MenuStyle
*ms
= MR_STYLE(mr
);
2621 int width
, height
, x
, y
;
2622 int bw
= MST_BORDER_WIDTH(mr
);
2625 p
= ST_FACE(ms
).u
.p
;
2626 width
= MR_WIDTH(mr
) - 2 * bw
;
2627 height
= MR_HEIGHT(mr
) - 2 * bw
;
2628 y
= (int)(height
- p
->height
) / 2;
2629 x
= (int)(width
- p
->width
) / 2;
2638 if (width
> p
->width
)
2642 if (height
> p
->height
)
2646 if (width
> MR_WIDTH(mr
) - x
- bw
)
2648 width
= MR_WIDTH(mr
) - x
- bw
;
2650 if (height
> MR_HEIGHT(mr
) - y
- bw
)
2652 height
= MR_HEIGHT(mr
) - y
- bw
;
2654 XSetClipMask(dpy
, Scr
.TransMaskGC
, p
->mask
);
2655 XSetClipOrigin(dpy
, Scr
.TransMaskGC
, x
, y
);
2657 dpy
, p
->picture
, MR_WINDOW(mr
), Scr
.TransMaskGC
,
2658 bw
, bw
, width
, height
, x
, y
);
2666 * get_menu_paint_item_parameters - prepares the parameters to be
2667 * passed to menuitem_paint().
2669 * mr - the menu instance that holds the menu item
2670 * mi - the menu item to redraw
2671 * fw - the FvwmWindow structure to check against allowed functions
2674 static void get_menu_paint_item_parameters(
2675 MenuPaintItemParameters
*mpip
, MenuRoot
*mr
, MenuItem
*mi
,
2676 FvwmWindow
*fw
, XEvent
*pevent
, Bool do_redraw_menu_border
)
2678 mpip
->ms
= MR_STYLE(mr
);
2679 mpip
->w
= MR_WINDOW(mr
);
2680 mpip
->selected_item
= MR_SELECTED_ITEM(mr
);
2681 mpip
->dim
= &MR_DIM(mr
);
2683 mpip
->used_mini_icons
= MR_USED_MINI_ICONS(mr
);
2685 mpip
->cb_reset_bg
= paint_menu_gradient_background
;
2686 mpip
->flags
.is_first_item
= (MR_FIRST_ITEM(mr
) == mi
);
2687 mpip
->flags
.is_left_triangle
= MR_IS_LEFT_TRIANGLE(mr
);
2696 * paint_menu - draws the entire menu
2699 static void paint_menu(
2700 MenuRoot
*mr
, XEvent
*pevent
, FvwmWindow
*fw
)
2703 MenuStyle
*ms
= MR_STYLE(mr
);
2704 int bw
= MST_BORDER_WIDTH(mr
);
2706 int relief_thickness
= ST_RELIEF_THICKNESS(MR_STYLE(mr
));
2709 if (fw
&& !check_if_fvwm_window_exists(fw
))
2713 if (MR_IS_PAINTED(mr
) && pevent
&&
2714 (pevent
->xexpose
.x
>= MR_WIDTH(mr
) - bw
||
2715 pevent
->xexpose
.x
+ pevent
->xexpose
.width
<= bw
||
2716 pevent
->xexpose
.y
>= MR_HEIGHT(mr
) - bw
||
2717 pevent
->xexpose
.y
+ pevent
->xexpose
.height
<= bw
))
2719 /* Only the border was obscured. Redraw it centrally instead of
2720 * redrawing several menu items. */
2722 dpy
, MR_WINDOW(mr
), 0, 0, MR_WIDTH(mr
) - 1,
2723 MR_HEIGHT(mr
) - 1, (Pdepth
< 2) ?
2724 SHADOW_GC(MST_MENU_INACTIVE_GCS(mr
)) :
2725 HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr
)),
2726 SHADOW_GC(MST_MENU_INACTIVE_GCS(mr
)), bw
);
2731 MR_IS_PAINTED(mr
) = 1;
2732 /* paint the menu background */
2733 if (ms
&& ST_HAS_MENU_CSET(ms
))
2735 if (MR_IS_BACKGROUND_SET(mr
) == False
)
2737 SetWindowBackground(
2738 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
),
2739 MR_HEIGHT(mr
), &Colorset
[ST_CSET_MENU(ms
)],
2740 Pdepth
, FORE_GC(ST_MENU_INACTIVE_GCS(ms
)),
2742 MR_IS_BACKGROUND_SET(mr
) = True
;
2748 Bool do_clear
= False
;
2750 type
= ST_FACE(ms
).type
;
2754 XSetWindowBackground(
2755 dpy
, MR_WINDOW(mr
), MST_FACE(mr
).u
.back
);
2759 do_clear
= paint_menu_gradient_background(mr
, pevent
);
2762 do_clear
= paint_menu_pixmap_background(mr
, pevent
);
2764 case TiledPixmapMenu
:
2765 XSetWindowBackgroundPixmap(
2766 dpy
, MR_WINDOW(mr
), ST_FACE(ms
).u
.p
->picture
);
2769 } /* switch(type) */
2770 if (do_clear
== True
)
2772 clear_expose_menu_area(MR_WINDOW(mr
), pevent
);
2775 /* draw the relief */
2776 RelieveRectangle(dpy
, MR_WINDOW(mr
), 0, 0, MR_WIDTH(mr
) - 1,
2777 MR_HEIGHT(mr
) - 1, (Pdepth
< 2) ?
2778 SHADOW_GC(MST_MENU_INACTIVE_GCS(mr
)) :
2779 HILIGHT_GC(MST_MENU_INACTIVE_GCS(mr
)),
2780 SHADOW_GC(MST_MENU_INACTIVE_GCS(mr
)), bw
);
2781 /* paint the menu items */
2782 for (mi
= MR_FIRST_ITEM(mr
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
2786 /* be smart about handling the expose, redraw only the entries
2787 * that we need to */
2794 register int b_offset
;
2796 b_offset
= MI_Y_OFFSET(mi
) + MI_HEIGHT(mi
);
2797 if (MR_SELECTED_ITEM(mr
) == mi
)
2799 b_offset
+= relief_thickness
;
2801 if (pevent
->xexpose
.y
< b_offset
&&
2802 (pevent
->xexpose
.y
+ pevent
->xexpose
.height
) >
2810 MenuPaintItemParameters mpip
;
2812 get_menu_paint_item_parameters(
2813 &mpip
, mr
, NULL
, fw
, pevent
, True
);
2814 mpip
.flags
.is_first_item
= (MR_FIRST_ITEM(mr
) == mi
);
2815 menuitem_paint(mi
, &mpip
);
2818 paint_side_pic(mr
, pevent
);
2824 /* Set the selected-ness state of the menuitem passed in */
2825 static void select_menu_item(
2826 MenuRoot
*mr
, MenuItem
*mi
, Bool select
, FvwmWindow
*fw
)
2828 if (select
== True
&& MR_SELECTED_ITEM(mr
) != NULL
&&
2829 MR_SELECTED_ITEM(mr
) != mi
)
2831 select_menu_item(mr
, MR_SELECTED_ITEM(mr
), False
, fw
);
2833 else if (select
== False
&& MR_SELECTED_ITEM(mr
) == NULL
)
2837 else if (select
== True
&& MR_SELECTED_ITEM(mr
) == mi
)
2841 if (!MI_IS_SELECTABLE(mi
))
2846 if (select
== False
)
2848 MI_WAS_DESELECTED(mi
) = True
;
2851 if (!MST_HAS_MENU_CSET(mr
))
2853 switch (MST_FACE(mr
).type
)
2863 if (!MR_IS_PAINTED(mr
))
2865 flush_expose(MR_WINDOW(mr
));
2866 paint_menu(mr
, NULL
, fw
);
2868 iy
= MI_Y_OFFSET(mi
);
2869 ih
= MI_HEIGHT(mi
) +
2870 (MI_IS_SELECTABLE(mi
) ?
2871 MST_RELIEF_THICKNESS(mr
) : 0);
2877 mw
= MR_WIDTH(mr
) - 2 * MST_BORDER_WIDTH(mr
);
2878 if (iy
+ ih
> MR_HEIGHT(mr
))
2879 ih
= MR_HEIGHT(mr
) - iy
;
2881 MR_STORED_ITEM(mr
).stored
=
2883 dpy
, Scr
.NoFocusWin
, mw
, ih
,
2885 XSetGraphicsExposures(
2887 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
2890 while (FCheckTypedEvent(dpy
, NoExpose
, &e
))
2892 /* nothing to do here */
2896 MR_STORED_ITEM(mr
).stored
,
2897 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
2898 MST_BORDER_WIDTH(mr
), iy
, mw
, ih
, 0,
2901 if (FCheckTypedEvent(dpy
, NoExpose
, &e
))
2903 MR_STORED_ITEM(mr
).y
= iy
;
2904 MR_STORED_ITEM(mr
).width
= mw
;
2905 MR_STORED_ITEM(mr
).height
= ih
;
2911 MR_STORED_ITEM(mr
).stored
);
2912 MR_STORED_ITEM(mr
).stored
= None
;
2913 MR_STORED_ITEM(mr
).width
= 0;
2914 MR_STORED_ITEM(mr
).height
= 0;
2915 MR_STORED_ITEM(mr
).y
= 0;
2917 XSetGraphicsExposures(
2919 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
2922 else if (select
== False
&&
2923 MR_STORED_ITEM(mr
).width
!= 0)
2927 dpy
, MR_STORED_ITEM(mr
).stored
,
2929 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
2930 0, 0, MR_STORED_ITEM(mr
).width
,
2931 MR_STORED_ITEM(mr
).height
,
2932 MST_BORDER_WIDTH(mr
),
2933 MR_STORED_ITEM(mr
).y
);
2935 if (MR_STORED_ITEM(mr
).stored
!= None
)
2938 dpy
, MR_STORED_ITEM(mr
).stored
);
2940 MR_STORED_ITEM(mr
).stored
= None
;
2941 MR_STORED_ITEM(mr
).width
= 0;
2942 MR_STORED_ITEM(mr
).height
= 0;
2943 MR_STORED_ITEM(mr
).y
= 0;
2945 else if (select
== False
)
2948 FvwmPicture
*sidePic
= NULL
;
2951 e
.xexpose
.x
= MST_BORDER_WIDTH(mr
);
2952 e
.xexpose
.y
= MI_Y_OFFSET(mi
);
2953 e
.xexpose
.width
= MR_WIDTH(mr
) - 2 *
2954 MST_BORDER_WIDTH(mr
);
2955 e
.xexpose
.height
= MI_HEIGHT(mi
) +
2956 (MI_IS_SELECTABLE(mi
) ?
2957 MST_RELIEF_THICKNESS(mr
) : 0);
2960 sidePic
= MR_SIDEPIC(mr
);
2962 else if (MST_SIDEPIC(mr
))
2964 sidePic
= MST_SIDEPIC(mr
);
2968 e
.xexpose
.width
-= sidePic
->width
;
2969 if (MR_SIDEPIC_X_OFFSET(mr
) ==
2970 MST_BORDER_WIDTH(mr
))
2972 e
.xexpose
.x
+= sidePic
->width
;
2975 MR_SELECTED_ITEM(mr
) = (select
) ? mi
: NULL
;
2976 paint_menu(mr
, &e
, fw
);
2980 if (MR_STORED_ITEM(mr
).width
!= 0)
2982 if (MR_STORED_ITEM(mr
).stored
!= None
)
2986 MR_STORED_ITEM(mr
).stored
);
2988 MR_STORED_ITEM(mr
).stored
= None
;
2989 MR_STORED_ITEM(mr
).width
= 0;
2990 MR_STORED_ITEM(mr
).height
= 0;
2991 MR_STORED_ITEM(mr
).y
= 0;
2997 if (fw
&& !check_if_fvwm_window_exists(fw
))
3001 MR_SELECTED_ITEM(mr
) = (select
) ? mi
: NULL
;
3002 if (MR_IS_PAINTED(mr
))
3004 MenuPaintItemParameters mpip
;
3006 get_menu_paint_item_parameters(&mpip
, mr
, mi
, fw
, NULL
, False
);
3007 menuitem_paint(mi
, &mpip
);
3013 /* ---------------------------- popping menu up or down -------------------- */
3015 static int get_left_popup_x_position(MenuRoot
*mr
, MenuRoot
*submenu
, int x
)
3017 if (MST_USE_LEFT_SUBMENUS(mr
))
3019 return (x
- MST_POPUP_OFFSET_ADD(mr
) - MR_WIDTH(submenu
) +
3021 (100 - MST_POPUP_OFFSET_PERCENT(mr
)) / 100);
3025 return (x
- MR_WIDTH(submenu
) + MST_BORDER_WIDTH(mr
));
3029 static int get_right_popup_x_position(MenuRoot
*mr
, MenuRoot
*submenu
, int x
)
3031 if (MST_USE_LEFT_SUBMENUS(mr
))
3033 return (x
+ MR_WIDTH(mr
) - MST_BORDER_WIDTH(mr
));
3037 return (x
+ MR_WIDTH(mr
) * MST_POPUP_OFFSET_PERCENT(mr
) / 100 +
3038 MST_POPUP_OFFSET_ADD(mr
));
3042 static void get_prefered_popup_position(
3043 MenuRoot
*mr
, MenuRoot
*submenu
, int *px
, int *py
,
3044 Bool
*pprefer_left_submenus
)
3047 MenuItem
*mi
= NULL
;
3049 if (!menu_get_geometry(
3050 mr
, &JunkRoot
, &menu_x
, &menu_y
, &JunkWidth
, &JunkHeight
,
3051 &JunkBW
, &JunkDepth
))
3055 *pprefer_left_submenus
= False
;
3056 fvwm_msg(ERR
, "get_prefered_popup_position",
3057 "can't get geometry of menu %s", MR_NAME(mr
));
3060 /* set the direction flag */
3061 *pprefer_left_submenus
=
3062 (MR_HAS_POPPED_UP_LEFT(mr
) ||
3063 (MST_USE_LEFT_SUBMENUS(mr
) &&
3064 !MR_HAS_POPPED_UP_RIGHT(mr
)));
3065 /* get the x position */
3066 if (*pprefer_left_submenus
)
3068 *px
= get_left_popup_x_position(mr
, submenu
, menu_x
);
3072 *px
= get_right_popup_x_position(mr
, submenu
, menu_x
);
3074 /* get the y position */
3075 if (MR_SELECTED_ITEM(mr
))
3077 mi
= MR_SELECTED_ITEM(mr
);
3081 *py
= menu_y
+ MI_Y_OFFSET(mi
) -
3082 MST_BORDER_WIDTH(mr
) + MST_RELIEF_THICKNESS(mr
);
3090 static int do_menus_overlap(
3091 MenuRoot
*mr
, int x
, int y
, int width
, int height
, int h_tolerance
,
3092 int v_tolerance
, int s_tolerance
, Bool allow_popup_offset_tolerance
)
3094 int prior_x
, prior_y
, x_overlap
;
3095 int prior_width
, prior_height
;
3101 if (!menu_get_geometry(
3102 mr
, &JunkRoot
,&prior_x
,&prior_y
, &prior_width
,
3103 &prior_height
, &JunkBW
, &JunkDepth
))
3108 if (allow_popup_offset_tolerance
)
3110 if (MST_POPUP_OFFSET_ADD(mr
) < 0)
3112 s_tolerance
= -MST_POPUP_OFFSET_ADD(mr
);
3114 if (MST_USE_LEFT_SUBMENUS(mr
))
3116 prior_x
+= (100 - MST_POPUP_OFFSET_PERCENT(mr
)) / 100;
3121 (float)(MST_POPUP_OFFSET_PERCENT(mr
)) / 100.0;
3124 if (MST_USE_LEFT_SUBMENUS(mr
))
3126 int t
= s_tolerance
;
3127 s_tolerance
= h_tolerance
;
3130 if (y
< prior_y
+ prior_height
- v_tolerance
&&
3131 prior_y
< y
+ height
- v_tolerance
&&
3132 x
< prior_x
+ prior_width
- s_tolerance
&&
3133 prior_x
< x
+ width
- h_tolerance
)
3135 x_overlap
= x
- prior_x
;
3148 * pop_menu_up - pop up a pull down menu
3151 * x, y - location of upper left of menu
3152 * do_warp_to_item - warp pointer to the first item after title
3153 * pops - pointer to the menu options for new menu
3156 static int pop_menu_up(
3157 MenuRoot
**pmenu
, MenuParameters
*pmp
, MenuRoot
*parent_menu
,
3158 MenuItem
*parent_item
, const exec_context_t
**pexc
, int x
, int y
,
3159 Bool prefer_left_submenu
, Bool do_warp_to_item
, MenuOptions
*pops
,
3160 Bool
*ret_overlap
, Window popdown_window
)
3162 Bool do_warp_to_title
= False
;
3164 int x_clipped_overlap
;
3175 unsigned int event_mask
;
3181 (MR_MAPPED_COPIES(mr
) > 0 && MR_COPIES(mr
) >= MAX_MENU_COPIES
))
3187 * handle dynamic menu actions
3190 /* First of all, execute the popup action (if defined). */
3191 if (MR_POPUP_ACTION(mr
))
3194 saved_pos_hints pos_hints
;
3195 Bool is_busy_grabbed
= False
;
3196 int mapped_copies
= MR_MAPPED_COPIES(mr
);
3198 /* save variables that we still need but that may be
3200 menu_name
= safestrdup(MR_NAME(mr
));
3201 pos_hints
= last_saved_pos_hints
;
3202 if (Scr
.BusyCursor
& BUSY_DYNAMICMENU
)
3204 is_busy_grabbed
= GrabEm(CRS_WAIT
, GRAB_BUSYMENU
);
3206 /* Execute the action */
3207 __menu_execute_function(pmp
->pexc
, MR_POPUP_ACTION(mr
));
3208 if (is_busy_grabbed
)
3210 UngrabEm(GRAB_BUSYMENU
);
3212 /* restore the stuff we saved */
3213 last_saved_pos_hints
= pos_hints
;
3214 if (mapped_copies
== 0)
3216 /* Now let's see if the menu still exists. It may have
3217 * been destroyed and recreated, so we have to look for
3218 * a menu with the saved name. menus_find_menu() always
3219 * returns the original menu, not one of its copies, so
3220 * below logic would fail miserably if used with a menu
3221 * copy. On the other hand, menu copies can't be
3222 * deleted within a dynamic popup action, so just
3223 * ignore this case. */
3224 *pmenu
= menus_find_menu(menu_name
);
3234 update_menu(mr
, pmp
);
3236 if (mr
== NULL
|| MR_FIRST_ITEM(mr
) == NULL
|| MR_ITEMS(mr
) == 0)
3238 /* The menu deleted itself or all its items or it has been
3239 * empty from the start. */
3243 if (fw
&& !check_if_fvwm_window_exists(fw
))
3247 context
= (*pexc
)->w
.wcontext
;
3250 * Create a new menu instance (if necessary)
3253 if (MR_MAPPED_COPIES(mr
) > 0)
3255 /* create a new instance of the menu */
3256 *pmenu
= copy_menu_root(*pmenu
);
3261 make_menu_window(*pmenu
, False
);
3266 * Evaluate position hints
3269 /* calculate position from position hints if available */
3270 if (pops
->flags
.has_poshints
&&
3271 !last_saved_pos_hints
.flags
.do_ignore_pos_hints
)
3273 get_xy_from_position_hints(
3274 &(pops
->pos_hints
), MR_WIDTH(mr
), MR_HEIGHT(mr
),
3275 (parent_menu
) ? MR_WIDTH(parent_menu
) : 0,
3276 prefer_left_submenu
, &x
, &y
);
3280 * Initialise new menu
3283 MR_PARENT_MENU(mr
) = parent_menu
;
3284 MR_PARENT_ITEM(mr
) = parent_item
;
3285 MR_IS_PAINTED(mr
) = 0;
3287 MR_IS_RIGHT(mr
) = 0;
3290 MR_XANIMATION(mr
) = 0;
3291 InstallFvwmColormap();
3294 * Handle popups from button clicks on buttons in the title bar,
3295 * or the title bar itself. Position hints override this.
3298 if (!pops
->flags
.has_poshints
&& fw
&& parent_menu
== NULL
&&
3299 pmp
->flags
.is_first_root_menu
)
3308 has_context
= get_title_button_geometry(
3309 fw
, &button_g
, context
);
3312 fscreen_scr_arg fscr
;
3314 get_title_gravity_factors(fw
, &gx
, &gy
);
3315 cx
= button_g
.x
+ button_g
.width
/ 2;
3316 cy
= button_g
.y
+ button_g
.height
/ 2;
3320 (gx
== 1) * button_g
.width
-
3321 (gx
== -1) * MR_WIDTH(mr
);
3326 (gy
== 1) * button_g
.height
-
3327 (gy
== -1) * MR_HEIGHT(mr
);
3329 if (gx
== 0 && x
< button_g
.x
)
3333 else if (gx
== 0 && x
+ MR_WIDTH(mr
) >=
3334 button_g
.x
+ button_g
.width
)
3336 x
= button_g
.x
+ button_g
.width
- MR_WIDTH(mr
);
3338 if (gy
== 0 && y
< button_g
.y
)
3342 else if (gy
== 0 && y
+ MR_HEIGHT(mr
) >=
3343 button_g
.y
+ button_g
.height
)
3345 y
= button_g
.y
+ button_g
.height
-
3348 pops
->pos_hints
.has_screen_origin
= True
;
3351 if (FScreenGetScrRect(
3352 &fscr
, FSCREEN_XYPOS
, &JunkX
, &JunkY
,
3353 &JunkWidth
, &JunkHeight
))
3355 /* use current cx/cy */
3357 else if (FQueryPointer(
3358 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
,
3359 &cx
, &cy
, &JunkX
, &JunkY
, &JunkMask
))
3361 /* use pointer's position */
3368 pops
->pos_hints
.screen_origin_x
= cx
;
3369 pops
->pos_hints
.screen_origin_y
= cy
;
3371 } /* if (pops->flags.has_poshints) */
3378 fscreen_scr_arg fscr
;
3380 fscr
.xypos
.x
= pops
->pos_hints
.screen_origin_x
;
3381 fscr
.xypos
.y
= pops
->pos_hints
.screen_origin_y
;
3382 /* clip to screen */
3383 FScreenClipToScreen(
3384 &fscr
, FSCREEN_XYPOS
, &x
, &y
, MR_WIDTH(mr
),
3386 /* "this" screen is defined -- so get its coords for future
3389 &fscr
, FSCREEN_XYPOS
, &scr_x
, &scr_y
, &scr_w
, &scr_h
);
3394 bw
= MST_BORDER_WIDTH(mr
);
3395 bwp
= MST_BORDER_WIDTH(parent_menu
);
3396 x_overlap
= do_menus_overlap(
3397 parent_menu
, x
, y
, MR_WIDTH(mr
), MR_HEIGHT(mr
), bwp
,
3402 * Calculate position and animate menus
3405 if (parent_menu
== NULL
||
3407 parent_menu
, &JunkRoot
, &prev_x
, &prev_y
, &prev_width
,
3408 &prev_height
, &JunkBW
, &JunkDepth
))
3410 MR_HAS_POPPED_UP_LEFT(mr
) = 0;
3411 MR_HAS_POPPED_UP_RIGHT(mr
) = 0;
3417 Bool use_left_submenus
= MST_USE_LEFT_SUBMENUS(mr
);
3418 MenuRepaintTransparentParameters mrtp
;
3419 Bool transparent_bg
= False
;
3421 /* check if menus overlap */
3424 parent_menu
, x
, y
, MR_WIDTH(mr
), MR_HEIGHT(mr
),
3425 bwp
, bwp
, bwp
, True
);
3426 if (x_clipped_overlap
&&
3427 (!pops
->flags
.has_poshints
||
3428 pops
->pos_hints
.is_relative
== False
||
3431 /* menus do overlap, but do not reposition if overlap
3432 * was caused by relative positioning hints */
3433 left_x
= get_left_popup_x_position(
3434 parent_menu
, mr
, prev_x
);
3435 right_x
= get_right_popup_x_position(
3436 parent_menu
, mr
, prev_x
);
3437 if (use_left_submenus
)
3439 if (left_x
+ MR_WIDTH(mr
) < prev_x
+ bwp
)
3441 left_x
= prev_x
+ bwp
- MR_WIDTH(mr
);
3446 if (right_x
> prev_x
+ prev_width
- bwp
)
3448 right_x
= prev_x
+ prev_width
- bwp
;
3453 * Animate parent menu
3456 if (MST_IS_ANIMATED(mr
))
3458 /* animate previous out of the way */
3464 if (use_left_submenus
)
3466 if (prev_x
- left_x
< MR_WIDTH(mr
))
3469 x
+ (prev_x
- left_x
);
3474 x
+ MR_WIDTH(mr
) - bw
;
3476 a_left_x
= x
- MR_WIDTH(parent_menu
);
3480 if (right_x
- prev_x
< prev_width
)
3483 x
+ (prev_x
- right_x
);
3488 x
- prev_width
+ bw
;
3490 a_right_x
= x
+ MR_WIDTH(mr
);
3492 if (prefer_left_submenu
)
3494 /* popup menu is left of old menu, try
3495 * to move prior menu right */
3496 if (a_right_x
+ prev_width
<=
3501 else if (a_left_x
>= scr_x
)
3507 end_x
= scr_x
+ scr_w
-
3513 /* popup menu is right of old menu, try
3514 * to move prior menu left */
3515 if (a_left_x
>= scr_x
)
3519 else if (a_right_x
+ prev_width
<=
3529 if (end_x
== a_left_x
|| end_x
== 0)
3531 MR_HAS_POPPED_UP_LEFT(mr
) = 0;
3532 MR_HAS_POPPED_UP_RIGHT(mr
) = 1;
3536 MR_HAS_POPPED_UP_LEFT(mr
) = 1;
3537 MR_HAS_POPPED_UP_RIGHT(mr
) = 0;
3539 MR_XANIMATION(parent_menu
) += end_x
- prev_x
;
3540 if (ST_HAS_MENU_CSET(MR_STYLE(parent_menu
)) &&
3541 CSET_IS_TRANSPARENT(
3543 MR_STYLE(parent_menu
))))
3545 transparent_bg
= True
;
3546 get_menu_repaint_transparent_parameters(
3547 &mrtp
, parent_menu
, fw
);
3550 if (MR_IS_TEAR_OFF_MENU(parent_menu
))
3556 pmp
->tear_off_root_menu_window
3559 dpy
, w
, &JunkRoot
, &cx
,
3561 (unsigned int*)&JunkWidth
,
3562 (unsigned int*)&JunkHeight
,
3563 (unsigned int*)&JunkBW
,
3564 (unsigned int*)&JunkDepth
))
3566 end_x
+= (cx
- prev_x
);
3573 w
= MR_WINDOW(parent_menu
);
3575 AnimatedMoveOfWindow(
3576 w
, prev_x
, prev_y
, end_x
, prev_y
, True
,
3578 (transparent_bg
)? &mrtp
:NULL
);
3579 } /* if (MST_IS_ANIMATED(mr)) */
3582 * Try the other side of the parent menu
3585 else if (!pops
->flags
.is_fixed
)
3590 Bool use_left_as_last_resort
;
3592 use_left_as_last_resort
=
3593 (left_x
> scr_x
+ scr_w
- right_x
-
3595 may_use_left
= (left_x
>= scr_x
);
3596 may_use_right
= (right_x
+ MR_WIDTH(mr
) <=
3598 if (!may_use_left
&& !may_use_right
)
3600 /* If everything goes wrong, put the
3601 * submenu on the side with more free
3603 do_use_left
= use_left_as_last_resort
;
3605 else if (may_use_left
&&
3606 (prefer_left_submenu
||
3613 do_use_left
= False
;
3615 x
= (do_use_left
) ? left_x
: right_x
;
3616 MR_HAS_POPPED_UP_LEFT(mr
) = do_use_left
;
3617 MR_HAS_POPPED_UP_RIGHT(mr
) = !do_use_left
;
3619 /* Force the menu onto the screen, but leave at
3620 * least PARENT_MENU_FORCE_VISIBLE_WIDTH pixels
3621 * of the parent menu visible */
3622 if (x
+ MR_WIDTH(mr
) > scr_x
+ scr_w
)
3624 int d
= x
+ MR_WIDTH(mr
) -
3629 PARENT_MENU_FORCE_VISIBLE_WIDTH
)
3631 c
= PARENT_MENU_FORCE_VISIBLE_WIDTH
+
3636 c
= prev_x
+ prev_width
;
3639 if (x
- c
>= d
|| x
<= prev_x
)
3650 int c
= prev_width
-
3651 PARENT_MENU_FORCE_VISIBLE_WIDTH
;
3666 } /* else if (non-overlapping menu style) */
3667 } /* if (x_clipped_overlap && ...) */
3670 MR_HAS_POPPED_UP_LEFT(mr
) = prefer_left_submenu
;
3671 MR_HAS_POPPED_UP_RIGHT(mr
) = !prefer_left_submenu
;
3678 if (x
+ MR_WIDTH(mr
) > prev_x
+ prev_width
)
3680 MR_IS_RIGHT(mr
) = 1;
3686 if (y
+ MR_HEIGHT(mr
) > prev_y
+ prev_height
)
3690 if (!MR_IS_LEFT(mr
) && !MR_IS_RIGHT(mr
))
3693 MR_IS_RIGHT(mr
) = 1;
3695 } /* if (parent_menu) */
3698 * Make sure we have the correct events selected
3701 if (pmp
->tear_off_root_menu_window
== NULL
)
3703 /* normal menus and sub menus */
3704 event_mask
= XEVMASK_MENUW
;
3706 else if (parent_menu
== NULL
)
3708 /* tear off menu needs more events */
3709 event_mask
= XEVMASK_TEAR_OFF_MENUW
;
3713 /* sub menus of tear off menus need LeaveNotify */
3714 event_mask
= XEVMASK_TEAR_OFF_SUBMENUW
;
3721 XMoveWindow(dpy
, MR_WINDOW(mr
), x
, y
);
3722 XSelectInput(dpy
, MR_WINDOW(mr
), event_mask
);
3723 XMapRaised(dpy
, MR_WINDOW(mr
));
3725 XUnmapWindow(dpy
, popdown_window
);
3727 MR_MAPPED_COPIES(mr
)++;
3728 MST_USAGE_COUNT(mr
)++;
3733 parent_menu
, x
, y
, MR_WIDTH(mr
), MR_HEIGHT(mr
),
3734 0, 0, 0, False
) ? True
: False
;
3741 if (!do_warp_to_item
&& parent_menu
!= NULL
)
3745 mi
= find_entry(pmp
, NULL
, &mrMi
, None
, -1, -1);
3746 if (mi
&& mrMi
== mr
&& mi
!= MR_FIRST_ITEM(mrMi
))
3748 /* pointer is on an item of the popup */
3749 if (MST_DO_WARP_TO_TITLE(mr
))
3751 /* warp pointer if not on a root menu */
3752 do_warp_to_title
= True
;
3755 } /* if (!do_warp_to_item) */
3757 if (pops
->flags
.do_not_warp
)
3759 do_warp_to_title
= False
;
3761 else if (pops
->flags
.do_warp_title
)
3763 do_warp_to_title
= True
;
3765 if (pops
->flags
.has_poshints
&&
3766 !last_saved_pos_hints
.flags
.do_ignore_pos_hints
&&
3767 pops
->flags
.do_warp_title
)
3769 do_warp_to_title
= True
;
3771 if (do_warp_to_item
)
3774 MR_SELECTED_ITEM(mr
) = NULL
;
3775 warp_pointer_to_item(
3776 mr
, MR_FIRST_ITEM(mr
), True
/* skip Title */);
3777 select_menu_item(mr
, MR_SELECTED_ITEM(mr
), True
, fw
);
3779 else if (do_warp_to_title
)
3781 /* Warp pointer to middle of top line, since we don't
3782 * want the user to come up directly on an option */
3783 warp_pointer_to_title(mr
);
3792 * pop_menu_down - unhighlight the current menu selection and
3793 * take down the menus
3795 * mr - menu to pop down; this pointer is invalid after the function
3796 * returns. Don't use it anymore!
3797 * parent - the menu that has spawned mr (may be NULL). this is
3798 * used to see if mr was spawned by itself on some level.
3799 * this is a hack to allow specifying 'Popup foo' within
3800 * menu foo. You must use the MenuRoot that is currently
3801 * being processed here. DO NOT USE MR_PARENT_MENU(mr) here!
3804 static void pop_menu_down(MenuRoot
**pmr
, MenuParameters
*pmp
)
3809 memset(&(MR_DYNAMIC_FLAGS(*pmr
)), 0, sizeof(MR_DYNAMIC_FLAGS(*pmr
)));
3810 XUnmapWindow(dpy
, MR_WINDOW(*pmr
));
3811 MR_MAPPED_COPIES(*pmr
)--;
3812 MST_USAGE_COUNT(*pmr
)--;
3813 UninstallFvwmColormap();
3815 if ((mi
= MR_SELECTED_ITEM(*pmr
)) != NULL
)
3817 select_menu_item(*pmr
, mi
, False
, (*pmp
->pexc
)->w
.fw
);
3819 if (MR_STORED_PIXELS(*pmr
).d_pixels
!= NULL
)
3822 dpy
, Pcmap
, MR_STORED_PIXELS(*pmr
).d_pixels
,
3823 MR_STORED_PIXELS(*pmr
).d_npixels
, 0, False
);
3824 free(MR_STORED_PIXELS(*pmr
).d_pixels
);
3825 MR_STORED_PIXELS(*pmr
).d_pixels
= NULL
;
3827 if (MR_COPIES(*pmr
) > 1)
3829 /* delete this instance of the menu */
3830 DestroyMenu(*pmr
, False
, False
);
3832 else if (MR_POPDOWN_ACTION(*pmr
))
3834 /* Finally execute the popdown action (if defined). */
3835 saved_pos_hints pos_hints
;
3837 /* save variables that we still need but that may be
3839 pos_hints
= last_saved_pos_hints
;
3840 /* Execute the action */
3841 __menu_execute_function(pmp
->pexc
, MR_POPDOWN_ACTION(*pmr
));
3842 /* restore the stuff we saved */
3843 last_saved_pos_hints
= pos_hints
;
3852 * pop_menu_down_and_repaint_parent - Pops down a menu and repaints the
3853 * overlapped portions of the parent menu. This is done only if
3854 * *fSubmenuOverlaps is True. *fSubmenuOverlaps is set to False
3858 static void pop_menu_down_and_repaint_parent(
3859 MenuRoot
**pmr
, Bool
*fSubmenuOverlaps
, MenuParameters
*pmp
)
3861 MenuRoot
*parent
= MR_PARENT_MENU(*pmr
);
3873 if (*fSubmenuOverlaps
&& parent
)
3875 /* popping down the menu may destroy the menu via the dynamic
3876 * popdown action! Thus we must not access *pmr afterwards. */
3877 win
= MR_WINDOW(*pmr
);
3879 /* Create a fake event to pass into paint_menu */
3880 event
.type
= Expose
;
3881 if (!menu_get_geometry(
3882 *pmr
, &JunkRoot
, &mr_x
, &mr_y
, &mr_width
,
3883 &mr_height
, &JunkBW
, &JunkDepth
) ||
3885 parent
, &JunkRoot
, &parent_x
, &parent_y
,
3886 &parent_width
, &parent_height
, &JunkBW
,
3889 pop_menu_down(pmr
, pmp
);
3890 paint_menu(parent
, NULL
, (*pmp
->pexc
)->w
.fw
);
3894 pop_menu_down(pmr
, pmp
);
3895 event
.xexpose
.x
= mr_x
- parent_x
;
3896 event
.xexpose
.width
= mr_width
;
3897 if (event
.xexpose
.x
< 0)
3899 event
.xexpose
.width
+= event
.xexpose
.x
;
3900 event
.xexpose
.x
= 0;
3902 if (event
.xexpose
.x
+ event
.xexpose
.width
>
3905 event
.xexpose
.width
=
3906 parent_width
- event
.xexpose
.x
;
3908 event
.xexpose
.y
= mr_y
- parent_y
;
3909 event
.xexpose
.height
= mr_height
;
3910 if (event
.xexpose
.y
< 0)
3912 event
.xexpose
.height
+= event
.xexpose
.y
;
3913 event
.xexpose
.y
= 0;
3915 if (event
.xexpose
.y
+ event
.xexpose
.height
>
3918 event
.xexpose
.height
=
3919 parent_height
- event
.xexpose
.y
;
3921 flush_accumulate_expose(MR_WINDOW(parent
), &event
);
3922 paint_menu(parent
, &event
, (*pmp
->pexc
)->w
.fw
);
3927 /* popping down the menu may destroy the menu via the dynamic
3928 * popdown action! Thus we must not access *pmr afterwards. */
3929 pop_menu_down(pmr
, pmp
);
3931 *fSubmenuOverlaps
= False
;
3936 /* ---------------------------- menu main loop ------------------------------ */
3938 static void __mloop_init(
3939 MenuParameters
*pmp
, MenuReturn
*pmret
,
3940 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
,
3943 memset(in
, 0, sizeof(*in
));
3944 in
->mif
.do_force_reposition
= 1;
3945 memset(med
, 0, sizeof(*med
));
3946 msi
->t0
= fev_get_evtime();
3947 pmret
->rc
= MENU_NOP
;
3948 memset(pops
, 0, sizeof(*pops
));
3949 /* remember where the pointer was so we can tell if it has moved */
3951 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &(msi
->x_init
),
3952 &(msi
->y_init
), &JunkX
, &JunkY
, &JunkMask
) == False
)
3954 /* pointer is on a different screen */
3958 /* get the event mask right */
3959 msi
->event_mask
= (pmp
->tear_off_root_menu_window
== NULL
) ?
3960 XEVMASK_MENU
: XEVMASK_TEAR_OFF_MENU
;
3965 static void __mloop_get_event_timeout_loop(
3966 MenuParameters
*pmp
,
3967 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
)
3969 XEvent e
= *(*pmp
->pexc
)->x
.elast
;
3971 while (!FPending(dpy
) || !FCheckMaskEvent(dpy
, msi
->event_mask
, &e
))
3973 Bool is_popup_timed_out
=
3974 (MST_POPUP_DELAY(pmp
->menu
) > 0 &&
3975 med
->popup_delay_10ms
++ >=
3976 MST_POPUP_DELAY(pmp
->menu
) + 1);
3977 Bool is_popdown_timed_out
=
3978 (MST_POPDOWN_DELAY(pmp
->menu
) > 0 && in
->mrPopdown
&&
3979 med
->popdown_delay_10ms
++ >=
3980 MST_POPDOWN_DELAY(pmp
->menu
) + 1);
3981 Bool do_fake_motion
= False
;
3983 if (is_popup_timed_out
)
3985 med
->popup_delay_10ms
= MST_POPUP_DELAY(pmp
->menu
);
3987 if (is_popdown_timed_out
)
3989 med
->popdown_delay_10ms
= MST_POPDOWN_DELAY(pmp
->menu
);
3992 in
->mif
.do_force_popup
||
3993 (is_popup_timed_out
&&
3994 (is_popdown_timed_out
||
3995 in
->mif
.is_item_entered_by_key_press
||
3997 MST_DO_POPDOWN_IMMEDIATELY(pmp
->menu
))))
3999 in
->mif
.do_popup_now
= True
;
4000 in
->mif
.do_force_popup
= False
;
4001 do_fake_motion
= True
;
4002 in
->mif
.is_popped_up_by_timeout
= True
;
4003 is_popdown_timed_out
= True
;
4005 if ((in
->mrPopdown
|| in
->mrPopup
) &&
4006 (is_popdown_timed_out
||
4007 MST_DO_POPDOWN_IMMEDIATELY(pmp
->menu
)))
4009 MenuRoot
*m
= (in
->mrPopdown
) ?
4010 in
->mrPopdown
: in
->mrPopup
;
4012 if (!m
|| med
->mi
!= MR_PARENT_ITEM(m
))
4014 in
->mif
.do_popdown_now
= True
;
4015 do_fake_motion
= True
;
4016 if (MST_DO_POPUP_IMMEDIATELY(pmp
->menu
))
4018 in
->mif
.do_popup_now
= True
;
4019 in
->mif
.is_popped_up_by_timeout
= True
;
4022 !MST_DO_POPUP_IMMEDIATELY(pmp
->menu
) &&
4023 in
->mif
.is_pointer_in_active_item_area
)
4025 in
->mif
.do_popup_now
= True
;
4026 in
->mif
.is_popped_up_by_timeout
= True
;
4029 !MST_DO_POPUP_IMMEDIATELY(pmp
->menu
) &&
4030 !MST_DO_POPDOWN_IMMEDIATELY(pmp
->menu
)
4031 && MST_POPUP_DELAY(pmp
->menu
) <=
4032 MST_POPDOWN_DELAY(pmp
->menu
) &&
4033 med
->popup_delay_10ms
==
4034 med
->popdown_delay_10ms
)
4036 in
->mif
.do_popup_now
= True
;
4037 in
->mif
.is_popped_up_by_timeout
= True
;
4041 if (in
->mif
.do_popup_now
&& med
->mi
== in
->miRemovedSubmenu
&&
4042 !in
->mif
.is_key_press
)
4044 /* prevent popping up the menu again with
4045 * RemoveSubemenus */
4046 in
->mif
.do_popup_now
= False
;
4047 in
->mif
.do_force_popup
= False
;
4048 do_fake_motion
= in
->mif
.do_popdown_now
;
4049 in
->mif
.is_popped_up_by_timeout
= False
;
4054 /* fake a motion event, and set in->mif.do_popup_now */
4055 e
.type
= MotionNotify
;
4056 e
.xmotion
.time
= fev_get_evtime();
4058 in
->mif
.is_motion_faked
= True
;
4061 usleep(10000 /* 10 ms*/);
4067 static mloop_ret_code_t
__mloop_get_event(
4068 MenuParameters
*pmp
, MenuReturn
*pmret
,
4069 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
)
4071 XEvent e
= *(*pmp
->pexc
)->x
.elast
;
4073 in
->mif
.do_popup_and_warp
= False
;
4074 in
->mif
.do_popup_now
= False
;
4075 in
->mif
.do_popdown_now
= False
;
4076 in
->mif
.do_propagate_event_into_submenu
= False
;
4077 in
->mif
.is_key_press
= False
;
4078 in
->mif
.is_button_release
= 0;
4079 if (pmp
->event_propagate_to_submenu
)
4081 /* handle an event that was passed in from the parent menu */
4082 fev_fake_event(pmp
->event_propagate_to_submenu
);
4083 pmp
->event_propagate_to_submenu
= NULL
;
4085 else if (in
->mif
.do_recycle_event
)
4087 in
->mif
.is_popped_up_by_timeout
= False
;
4088 in
->mif
.do_recycle_event
= 0;
4089 if (pmp
->menu
!= pmret
->target_menu
)
4091 /* the event is for a previous menu, just close this
4093 pmret
->rc
= MENU_PROPAGATE_EVENT
;
4094 return MENU_MLOOP_RET_END
;
4096 if ((*pmp
->pexc
)->x
.elast
->type
== KeyPress
)
4098 /* since the pointer has been warped since the key was
4099 * pressed, fake a different key press position */
4101 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
,
4102 &e
.xkey
.x_root
, &e
.xkey
.y_root
,
4103 &JunkX
, &JunkY
, &e
.xkey
.state
) == False
)
4105 /* pointer is on a different screen */
4110 med
->mi
= MR_SELECTED_ITEM(pmp
->menu
);
4113 else if (pmp
->tear_off_root_menu_window
!= NULL
&&
4114 FCheckTypedWindowEvent(
4115 dpy
, FW_W_PARENT(pmp
->tear_off_root_menu_window
),
4118 /* Got a ClientMessage for the tear out menu */
4122 if (in
->mif
.do_force_reposition
)
4124 e
.type
= MotionNotify
;
4125 e
.xmotion
.time
= fev_get_evtime();
4126 in
->mif
.is_motion_faked
= True
;
4127 in
->mif
.do_force_reposition
= False
;
4128 in
->mif
.is_popped_up_by_timeout
= False
;
4131 else if (!FCheckMaskEvent(dpy
, ExposureMask
, &e
))
4133 Bool is_popdown_timer_active
= False
;
4134 Bool is_popup_timer_active
= False
;
4136 if (MST_POPDOWN_DELAY(pmp
->menu
) > 0 &&
4137 in
->mi_with_popup
!= NULL
&&
4138 in
->mi_with_popup
!= MR_SELECTED_ITEM(pmp
->menu
))
4140 is_popdown_timer_active
= True
;
4142 if (MST_POPUP_DELAY(pmp
->menu
) > 0 &&
4143 !in
->mif
.is_popped_up_by_timeout
&&
4144 in
->mi_wants_popup
!= NULL
)
4146 is_popup_timer_active
= True
;
4148 /* handle exposure events first */
4149 if (in
->mif
.do_force_popup
||
4150 in
->mif
.is_pointer_in_active_item_area
||
4151 is_popdown_timer_active
|| is_popup_timer_active
)
4153 __mloop_get_event_timeout_loop(
4158 /* block until there is an event */
4159 FMaskEvent(dpy
, msi
->event_mask
, &e
);
4160 in
->mif
.is_popped_up_by_timeout
= False
;
4165 in
->mif
.is_popped_up_by_timeout
= False
;
4169 in
->mif
.is_pointer_in_active_item_area
= False
;
4170 if (e
.type
== MotionNotify
)
4172 /* discard any extra motion events before a release */
4173 while (FCheckMaskEvent(
4174 dpy
, ButtonMotionMask
| ButtonReleaseMask
,
4175 &e
) && (e
.type
!= ButtonRelease
))
4181 return MENU_MLOOP_RET_NORMAL
;
4184 static mloop_ret_code_t
__mloop_handle_event(
4185 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
4186 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
)
4191 pmret
->rc
= MENU_NOP
;
4192 switch ((*pmp
->pexc
)->x
.elast
->type
)
4195 in
->mif
.is_button_release
= 1;
4196 med
->mi
= find_entry(
4197 pmp
, &med
->x_offset
, &med
->mrMi
,
4198 (*pmp
->pexc
)->x
.elast
->xbutton
.subwindow
,
4199 (*pmp
->pexc
)->x
.elast
->xbutton
.x_root
,
4200 (*pmp
->pexc
)->x
.elast
->xbutton
.y_root
);
4201 /* hold the menu up when the button is released
4202 * for the first time if released OFF of the menu */
4203 if (pmp
->flags
.is_sticky
&& !in
->mif
.is_motion_first
)
4205 in
->mif
.is_release_first
= True
;
4206 pmp
->flags
.is_sticky
= False
;
4207 return MENU_MLOOP_RET_LOOP
;
4209 if (med
->mrMi
!= NULL
)
4215 med
->mrMi
, pmp
, pmret
, (*pmp
->pexc
)->x
.elast
,
4216 &med
->mi
, pdkp
, &menu_x
, &menu_y
);
4217 if (pmret
->rc
== MENU_NEWITEM_MOVEMENU
)
4219 move_any_menu(med
->mrMi
, pmp
, menu_x
, menu_y
);
4220 pmret
->rc
= MENU_NEWITEM
;
4222 else if (pmret
->rc
== MENU_NEWITEM_FIND
)
4224 med
->mi
= find_entry(
4225 pmp
, NULL
, NULL
, None
, -1, -1);
4226 pmret
->rc
= MENU_NEWITEM
;
4231 pmret
->rc
= MENU_SELECTED
;
4236 return MENU_MLOOP_RET_LOOP
;
4239 case MENU_DOUBLE_CLICKED
:
4241 case MENU_KILL_TEAR_OFF_MENU
:
4243 return MENU_MLOOP_RET_END
;
4245 /* Allow for MoveLeft/MoveRight action to work with
4247 in
->mif
.do_popup_and_warp
= True
;
4249 /* unpost the menu if posted */
4250 pmret
->flags
.is_menu_posted
= 0;
4251 return MENU_MLOOP_RET_NORMAL
;
4253 in
->mif
.was_item_unposted
= 0;
4254 if (pmret
->flags
.is_menu_posted
&& med
->mrMi
!= NULL
)
4256 if (pmret
->flags
.do_unpost_submenu
)
4258 pmret
->flags
.do_unpost_submenu
= 0;
4259 pmret
->flags
.is_menu_posted
= 0;
4260 /* just ignore the event */
4261 return MENU_MLOOP_RET_LOOP
;
4263 else if (med
->mi
&& MI_IS_POPUP(med
->mi
)
4264 && med
->mrMi
== pmp
->menu
)
4266 /* post menu - done below */
4268 else if (MR_PARENT_ITEM(pmp
->menu
) &&
4269 med
->mi
== MR_PARENT_ITEM(pmp
->menu
))
4271 /* propagate back to parent menu
4272 * and unpost current menu */
4273 pmret
->flags
.do_unpost_submenu
= 1;
4274 pmret
->rc
= MENU_PROPAGATE_EVENT
;
4275 pmret
->target_menu
= med
->mrMi
;
4276 return MENU_MLOOP_RET_END
;
4278 else if (med
->mrMi
!= pmp
->menu
&&
4279 med
->mrMi
!= in
->mrPopup
)
4281 /* unpost and propagate back to
4283 pmret
->flags
.is_menu_posted
= 0;
4284 pmret
->rc
= MENU_PROPAGATE_EVENT
;
4285 pmret
->target_menu
= med
->mrMi
;
4286 return MENU_MLOOP_RET_END
;
4288 else if (in
->mrPopup
&& med
->mrMi
==
4291 /* unpost and propagate into
4293 in
->mif
.was_item_unposted
= 1;
4294 in
->mif
.do_propagate_event_into_submenu
4300 /* simply unpost the menu */
4301 pmret
->flags
.is_menu_posted
= 0;
4304 if (is_double_click(
4305 msi
->t0
, med
->mi
, pmp
, pmret
, pdkp
,
4306 in
->mif
.has_mouse_moved
))
4308 pmret
->rc
= MENU_DOUBLE_CLICKED
;
4309 return MENU_MLOOP_RET_END
;
4311 if (med
->mi
== NULL
)
4313 pmret
->rc
= MENU_ABORTED
;
4315 else if (MI_IS_POPUP(med
->mi
))
4317 switch (MST_DO_POPUP_AS(pmp
->menu
))
4320 if (in
->mif
.was_item_unposted
)
4322 pmret
->flags
.is_menu_posted
=
4324 pmret
->rc
= MENU_UNPOST
;
4325 pmret
->target_menu
= NULL
;
4326 in
->mif
.do_popup_now
= False
;
4330 pmret
->flags
.is_menu_posted
=
4332 pmret
->rc
= MENU_POST
;
4333 pmret
->target_menu
= NULL
;
4334 in
->mif
.do_popup_now
= True
;
4341 in
->mif
.do_popdown_now
4345 return MENU_MLOOP_RET_NORMAL
;
4347 pmret
->rc
= MENU_NOP
;
4348 return MENU_MLOOP_RET_NORMAL
;
4350 pmret
->rc
= MENU_ABORTED
;
4361 pdkp
->timestamp
= 0;
4363 return MENU_MLOOP_RET_END
;
4366 /* if the first event is a button press allow the release to
4367 * select something */
4368 pmp
->flags
.is_sticky
= False
;
4369 return MENU_MLOOP_RET_LOOP
;
4371 case VisibilityNotify
:
4372 return MENU_MLOOP_RET_LOOP
;
4375 if ((*pmp
->pexc
)->x
.elast
->xkey
.keycode
!=
4376 MST_SELECT_ON_RELEASE_KEY(pmp
->menu
))
4378 return MENU_MLOOP_RET_LOOP
;
4380 /* fall through to KeyPress */
4383 /* Handle a key press events to allow mouseless operation */
4384 in
->mif
.is_key_press
= True
;
4385 med
->x_offset
= menudim_middle_x_offset(&MR_DIM(pmp
->menu
));
4387 /* if there is a posted menu we may have to move back into a
4388 * previous menu or possibly ignore the mouse position */
4389 if (pmret
->flags
.is_menu_posted
)
4396 pmret
->flags
.is_menu_posted
= 0;
4398 pmp
, &l_x_offset
, &l_mrMi
, None
, -1, -1);
4401 if (pmp
->menu
!= l_mrMi
)
4403 /* unpost the menu and propagate the
4404 * event to the correct menu */
4405 pmret
->rc
= MENU_PROPAGATE_EVENT
;
4406 pmret
->target_menu
= l_mrMi
;
4407 return MENU_MLOOP_RET_END
;
4410 med
->mi
= MR_SELECTED_ITEM(pmp
->menu
);
4411 e
= *(*pmp
->pexc
)->x
.elast
;
4412 e
.xkey
.x_root
= med
->x_offset
;
4413 e
.xkey
.y_root
= menuitem_middle_y_offset(
4414 med
->mi
, MR_STYLE(pmp
->menu
));
4418 /* now handle the actual key press */
4424 pmp
->menu
, pmp
, pmret
, (*pmp
->pexc
)->x
.elast
,
4425 &med
->mi
, pdkp
, &menu_x
, &menu_y
);
4426 if (pmret
->rc
== MENU_NEWITEM_MOVEMENU
)
4428 move_any_menu(pmp
->menu
, pmp
, menu_x
, menu_y
);
4429 pmret
->rc
= MENU_NEWITEM
;
4431 else if (pmret
->rc
== MENU_NEWITEM_FIND
)
4433 med
->mi
= find_entry(
4434 pmp
, NULL
, NULL
, None
, -1, -1);
4435 pmret
->rc
= MENU_NEWITEM
;
4438 if (pmret
->rc
!= MENU_NOP
)
4440 /* using a key 'unposts' the posted menu */
4441 pmret
->flags
.is_menu_posted
= 0;
4446 if (med
->mi
&& MI_IS_POPUP(med
->mi
))
4448 switch (MST_DO_POPUP_AS(pmp
->menu
))
4451 pmret
->rc
= MENU_POPUP
;
4454 pmret
->rc
= MENU_NOP
;
4455 return MENU_MLOOP_RET_NORMAL
;
4457 pmret
->rc
= MENU_ABORTED
;
4458 return MENU_MLOOP_RET_END
;
4461 return MENU_MLOOP_RET_END
;
4465 return MENU_MLOOP_RET_END
;
4468 case MENU_DOUBLE_CLICKED
:
4470 case MENU_KILL_TEAR_OFF_MENU
:
4472 return MENU_MLOOP_RET_END
;
4475 if (med
->mrMi
== NULL
)
4477 /* Set the MenuRoot of the current item in case
4478 * we have just warped to the menu from the
4479 * void or unposted a popup menu. */
4480 med
->mrMi
= pmp
->menu
;
4482 /*tmrMi = med->mrMi;*/
4487 /* now warp to the new menu item, if any */
4488 if (pmret
->rc
== MENU_NEWITEM
&& med
->mi
)
4490 warp_pointer_to_item(med
->mrMi
, med
->mi
, False
);
4492 if (pmret
->rc
== MENU_POPUP
&& med
->mi
&& MI_IS_POPUP(med
->mi
))
4494 in
->mif
.do_popup_and_warp
= True
;
4503 Window p_child
= None
;
4505 if (in
->mif
.has_mouse_moved
== False
)
4508 dpy
, Scr
.Root
, &JunkRoot
, &p_child
, &p_rx
,
4509 &p_ry
, &JunkX
, &JunkY
, &JunkMask
) == False
)
4511 /* pointer is on a different screen */
4515 if (p_rx
- msi
->x_init
> Scr
.MoveThreshold
||
4516 msi
->x_init
- p_rx
> Scr
.MoveThreshold
||
4517 p_ry
- msi
->y_init
> Scr
.MoveThreshold
||
4518 msi
->y_init
- p_ry
> Scr
.MoveThreshold
)
4520 /* remember that this isn't just a click any
4521 * more since the pointer moved */
4522 in
->mif
.has_mouse_moved
= True
;
4525 med
->mi
= find_entry(
4526 pmp
, &med
->x_offset
, &med
->mrMi
, p_child
, p_rx
, p_ry
);
4527 if (pmret
->flags
.is_menu_posted
&&
4528 med
->mrMi
!= pmret
->target_menu
)
4530 /* ignore mouse movement outside a posted menu */
4534 if (!in
->mif
.is_release_first
&& !in
->mif
.is_motion_faked
&&
4535 in
->mif
.has_mouse_moved
)
4537 in
->mif
.is_motion_first
= True
;
4539 in
->mif
.is_motion_faked
= False
;
4544 /* grab our expose events, let the rest go through */
4546 rc
= menu_expose((*pmp
->pexc
)->x
.elast
, (*pmp
->pexc
)->w
.fw
);
4547 /* we want to dispatch this too so that icons and maybe tear
4548 * off get redrawn after being obscured by menus. */
4551 dispatch_event((*pmp
->pexc
)->x
.elast
);
4553 return MENU_MLOOP_RET_LOOP
;
4556 if ((*pmp
->pexc
)->x
.elast
->xclient
.format
== 32 &&
4557 (*pmp
->pexc
)->x
.elast
->xclient
.data
.l
[0] ==
4558 _XA_WM_DELETE_WINDOW
&&
4559 pmp
->tear_off_root_menu_window
!= NULL
&&
4560 (*pmp
->pexc
)->x
.elast
->xclient
.window
== FW_W_PARENT(
4561 pmp
->tear_off_root_menu_window
))
4563 /* handle deletion of tear out menus */
4564 pmret
->rc
= MENU_KILL_TEAR_OFF_MENU
;
4565 return MENU_MLOOP_RET_END
;
4570 /* ignore EnterNotify events */
4574 if (pmp
->tear_off_root_menu_window
!= NULL
&&
4575 find_entry(pmp
, NULL
, &tmrMi
, None
, -1, -1) == NULL
&&
4578 /* handle deletion of tear out menus */
4579 pmret
->rc
= MENU_ABORTED
;
4580 return MENU_MLOOP_RET_END
;
4583 return MENU_MLOOP_RET_LOOP
;
4586 /* should never happen, but does not hurt */
4587 if (pmp
->tear_off_root_menu_window
!= NULL
&&
4588 (*pmp
->pexc
)->x
.elast
->xunmap
.window
==
4589 FW_W(pmp
->tear_off_root_menu_window
))
4591 /* handle deletion of tear out menus */
4592 pmret
->rc
= MENU_KILL_TEAR_OFF_MENU
;
4593 /* extra safety: pass event back to main event loop to
4594 * make sure the window is destroyed */
4595 FPutBackEvent(dpy
, (*pmp
->pexc
)->x
.elast
);
4596 return MENU_MLOOP_RET_END
;
4601 /* We must not dispatch events here. There is no guarantee
4602 * that dispatch_event doesn't destroy a window stored in the
4603 * menu structures. Anyway, no events should ever get here
4604 * except to tear off menus and these must be handled
4607 dispatch_event((*pmp
->pexc
)->x
.elast
);
4612 return MENU_MLOOP_RET_NORMAL
;
4615 static void __mloop_select_item(
4616 MenuParameters
*pmp
, mloop_evh_input_t
*in
, mloop_evh_data_t
*med
,
4617 Bool does_submenu_overlap
, Bool
*pdoes_popdown_submenu_overlap
)
4619 in
->mif
.is_item_entered_by_key_press
= in
->mif
.is_key_press
;
4620 med
->popup_delay_10ms
= 0;
4621 /* we're on the same menu, but a different item, so we need to unselect
4623 if (MR_SELECTED_ITEM(pmp
->menu
))
4625 /* something else was already selected on this menu. We have
4626 * to pop down the menu before unselecting the item in case we
4627 * are using gradient menus. The recalled image would paint
4628 * over the submenu. */
4629 if (in
->mrPopup
&& in
->mrPopup
!= in
->mrPopdown
)
4631 in
->mrPopdown
= in
->mrPopup
;
4632 med
->popdown_delay_10ms
= 0;
4633 *pdoes_popdown_submenu_overlap
= does_submenu_overlap
;
4637 pmp
->menu
, MR_SELECTED_ITEM(pmp
->menu
), False
,
4638 (*pmp
->pexc
)->w
.fw
);
4640 /* highlight the new item; sets MR_SELECTED_ITEM(pmp->menu) too */
4641 select_menu_item(pmp
->menu
, med
->mi
, True
, (*pmp
->pexc
)->w
.fw
);
4646 static void __mloop_wants_popup(
4647 MenuParameters
*pmp
, mloop_evh_input_t
*in
, mloop_evh_data_t
*med
,
4648 MenuRoot
*mrMiPopup
)
4650 Bool do_it_now
= False
;
4652 in
->mi_wants_popup
= med
->mi
;
4653 if (in
->mif
.do_popup_now
)
4657 else if (MST_DO_POPUP_IMMEDIATELY(pmp
->menu
) &&
4658 med
->mi
!= in
->miRemovedSubmenu
)
4660 if (in
->mif
.is_key_press
||
4661 MST_DO_POPDOWN_IMMEDIATELY(pmp
->menu
) || !in
->mrPopdown
)
4666 else if (pointer_in_active_item_area(med
->x_offset
, med
->mrMi
))
4668 if (in
->mif
.is_key_press
|| med
->mi
== in
->miRemovedSubmenu
||
4669 MST_DO_POPDOWN_IMMEDIATELY(pmp
->menu
) || !in
->mrPopdown
)
4675 in
->mif
.is_pointer_in_active_item_area
= 1;
4680 in
->miRemovedSubmenu
= NULL
;
4681 /* must create a new menu or popup */
4682 if (in
->mrPopup
== NULL
|| in
->mrPopup
!= mrMiPopup
)
4684 if (in
->mif
.do_popup_now
)
4686 in
->mif
.do_popup
= True
;
4690 /* pop up in next pass through loop */
4691 in
->mif
.do_force_popup
= True
;
4694 else if (in
->mif
.do_popup_and_warp
)
4696 warp_pointer_to_item(
4697 in
->mrPopup
, MR_FIRST_ITEM(in
->mrPopup
), True
);
4704 static mloop_ret_code_t
__mloop_make_popup(
4705 MenuParameters
*pmp
, MenuReturn
*pmret
,
4706 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
,
4707 MenuOptions
*pops
, Bool
*pdoes_submenu_overlap
)
4709 /* create a popup menu */
4710 if (!is_submenu_mapped(pmp
->menu
, med
->mi
))
4712 /* We want to pop prepop menus so it doesn't *have* to be
4713 unpopped; do_menu pops down any menus it pops up, but we
4714 want to be able to popdown w/o actually removing the menu */
4717 Bool prefer_left_submenus
;
4719 /* Make sure we are using the latest style and menu layout. */
4720 update_menu(in
->mrPopup
, pmp
);
4722 get_prefered_popup_position(
4723 pmp
->menu
, in
->mrPopup
, &x
, &y
, &prefer_left_submenus
);
4724 /* Note that we don't care if popping up the menu works. If it
4725 * doesn't we'll catch it below. */
4727 &in
->mrPopup
, pmp
, pmp
->menu
, med
->mi
, pmp
->pexc
, x
, y
,
4728 prefer_left_submenus
, in
->mif
.do_popup_and_warp
, pops
,
4729 pdoes_submenu_overlap
,
4730 (in
->mrPopdown
) ? MR_WINDOW(in
->mrPopdown
) : None
);
4731 in
->mi_with_popup
= med
->mi
;
4732 in
->mi_wants_popup
= NULL
;
4733 if (in
->mrPopup
== NULL
)
4735 /* the menu deleted itself when execution the dynamic
4737 pmret
->rc
= MENU_ERROR
;
4738 return MENU_MLOOP_RET_END
;
4740 MR_SUBMENU_ITEM(pmp
->menu
) = med
->mi
;
4743 return MENU_MLOOP_RET_NORMAL
;
4746 static mloop_ret_code_t
__mloop_get_mi_actions(
4747 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
4748 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
,
4749 MenuRoot
*mrMiPopup
, Bool
*pdoes_submenu_overlap
,
4750 Bool
*pdoes_popdown_submenu_overlap
)
4752 in
->mif
.do_popdown
= False
;
4753 in
->mif
.do_popup
= False
;
4754 in
->mif
.do_menu
= False
;
4755 in
->mif
.is_submenu_mapped
= False
;
4756 if (!in
->mrPopup
&& in
->mrPopdown
&&
4757 med
->mi
== MR_PARENT_ITEM(in
->mrPopdown
))
4759 /* We're again on the item that we left before.
4760 * Deschedule popping it down. */
4761 in
->mrPopup
= in
->mrPopdown
;
4762 in
->mrPopdown
= NULL
;
4764 if (med
->mrMi
== in
->mrPopup
)
4766 /* must make current popup menu a real menu */
4767 in
->mif
.do_menu
= True
;
4768 in
->mif
.is_submenu_mapped
= True
;
4770 else if (pmret
->rc
== MENU_POST
)
4772 /* must create a real menu and warp into it */
4773 if (in
->mrPopup
== NULL
|| in
->mrPopup
!= mrMiPopup
)
4775 in
->mif
.do_popup
= True
;
4777 in
->mif
.do_menu
= True
;
4778 in
->mif
.is_submenu_mapped
= True
;
4780 else if (in
->mif
.do_popup_and_warp
)
4782 /* must create a real menu and warp into it */
4783 if (in
->mrPopup
== NULL
|| in
->mrPopup
!= mrMiPopup
)
4785 in
->mif
.do_popup
= True
;
4789 XRaiseWindow(dpy
, MR_WINDOW(in
->mrPopup
));
4790 warp_pointer_to_item(
4791 in
->mrPopup
, MR_FIRST_ITEM(in
->mrPopup
), True
);
4792 in
->mif
.do_menu
= True
;
4793 in
->mif
.is_submenu_mapped
= True
;
4796 else if (med
->mi
&& MI_IS_POPUP(med
->mi
))
4798 if ((*pmp
->pexc
)->x
.elast
->type
== ButtonPress
&&
4800 msi
->t0
, med
->mi
, pmp
, pmret
, pdkp
,
4801 in
->mif
.has_mouse_moved
))
4803 pmret
->rc
= MENU_DOUBLE_CLICKED
;
4804 return MENU_MLOOP_RET_END
;
4806 else if (!in
->mrPopup
|| in
->mrPopup
!= mrMiPopup
||
4807 in
->mif
.do_popup_and_warp
)
4809 __mloop_wants_popup(pmp
, in
, med
, mrMiPopup
);
4812 if (in
->mif
.do_popdown_now
)
4814 in
->mif
.do_popdown
= True
;
4815 in
->mif
.do_popdown_now
= False
;
4817 else if (in
->mif
.do_popup
)
4819 if (in
->mrPopup
&& in
->mrPopup
!= mrMiPopup
)
4821 in
->mif
.do_popdown
= True
;
4822 in
->mrPopdown
= in
->mrPopup
;
4823 med
->popdown_delay_10ms
= 0;
4824 *pdoes_popdown_submenu_overlap
=
4825 *pdoes_submenu_overlap
;
4827 else if (in
->mrPopdown
&& in
->mrPopdown
!= mrMiPopup
)
4829 in
->mif
.do_popdown
= True
;
4831 else if (in
->mrPopdown
&& in
->mrPopdown
== mrMiPopup
)
4833 in
->mrPopup
= in
->mrPopdown
;
4834 *pdoes_submenu_overlap
=
4835 *pdoes_popdown_submenu_overlap
;
4836 in
->mrPopdown
= NULL
;
4837 in
->mif
.do_popup
= False
;
4838 in
->mif
.do_popdown
= False
;
4842 return MENU_MLOOP_RET_NORMAL
;
4845 static void __mloop_do_popdown(
4846 MenuParameters
*pmp
, mloop_evh_input_t
*in
,
4847 Bool
*pdoes_popdown_submenu_overlap
)
4851 pop_menu_down_and_repaint_parent(
4852 &in
->mrPopdown
, pdoes_popdown_submenu_overlap
, pmp
);
4853 in
->mi_with_popup
= NULL
;
4854 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
4855 if (in
->mrPopup
== in
->mrPopdown
)
4859 in
->mrPopdown
= NULL
;
4861 in
->mif
.do_popdown
= False
;
4866 static mloop_ret_code_t
__mloop_do_popup(
4867 MenuParameters
*pmp
, MenuReturn
*pmret
,
4868 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
,
4869 MenuOptions
*pops
, MenuRoot
*mrMiPopup
, Bool
*pdoes_submenu_overlap
,
4870 Bool
*pdoes_popdown_submenu_overlap
)
4872 if (!MR_IS_PAINTED(pmp
->menu
))
4874 /* draw the parent menu if it is not already drawn */
4875 flush_expose(MR_WINDOW(pmp
->menu
));
4876 paint_menu(pmp
->menu
, NULL
, (*pmp
->pexc
)->w
.fw
);
4878 /* get pos hints for item's action */
4879 get_popup_options(pmp
, med
->mi
, pops
);
4880 if (med
->mrMi
== pmp
->menu
&& mrMiPopup
== NULL
&&
4881 MI_IS_POPUP(med
->mi
) && MR_MISSING_SUBMENU_FUNC(pmp
->menu
))
4883 /* We're on a submenu item, but the submenu does not exist.
4884 * The user defined missing_submenu_action may create it. */
4885 Bool is_complex_function
;
4886 Bool is_busy_grabbed
= False
;
4889 char *missing_action
= MR_MISSING_SUBMENU_FUNC(pmp
->menu
);
4891 menu_name
= PeekToken(
4892 SkipNTokens(MI_ACTION(med
->mi
), 1), NULL
);
4897 is_complex_function
=
4898 functions_is_complex_function(missing_action
);
4899 if (is_complex_function
)
4904 strlen("Function") + 3 +
4905 strlen(missing_action
) * 2 + 3 +
4906 strlen(menu_name
) * 2 + 1);
4907 strcpy(action
, "Function ");
4908 action_ptr
= action
+ strlen(action
);
4909 action_ptr
= QuoteString(action_ptr
, missing_action
);
4910 *action_ptr
++ = ' ';
4911 action_ptr
= QuoteString(action_ptr
, menu_name
);
4915 action
= MR_MISSING_SUBMENU_FUNC(pmp
->menu
);
4917 if (Scr
.BusyCursor
& BUSY_DYNAMICMENU
)
4919 is_busy_grabbed
= GrabEm(CRS_WAIT
, GRAB_BUSYMENU
);
4921 /* Execute the action */
4922 __menu_execute_function(pmp
->pexc
, action
);
4923 if (is_complex_function
)
4927 if (is_busy_grabbed
)
4929 UngrabEm(GRAB_BUSYMENU
);
4931 /* Let's see if the menu exists now. */
4932 mrMiPopup
= mr_popup_for_mi(pmp
->menu
, med
->mi
);
4933 } /* run MISSING_SUBMENU_FUNCTION */
4934 in
->mrPopup
= mrMiPopup
;
4937 in
->mif
.do_menu
= False
;
4938 pmret
->flags
.is_menu_posted
= 0;
4942 if (__mloop_make_popup(
4943 pmp
, pmret
, in
, med
, pops
, pdoes_submenu_overlap
)
4944 == MENU_MLOOP_RET_END
)
4946 return MENU_MLOOP_RET_END
;
4948 } /* else (in->mrPopup) */
4949 if (in
->mif
.do_popdown
)
4953 if (in
->mrPopdown
!= in
->mrPopup
)
4955 if (in
->mi_with_popup
==
4956 MR_PARENT_ITEM(in
->mrPopdown
))
4958 in
->mi_with_popup
= NULL
;
4960 pop_menu_down_and_repaint_parent(
4962 pdoes_popdown_submenu_overlap
, pmp
);
4964 in
->mrPopdown
= NULL
;
4966 in
->mif
.do_popdown
= False
;
4970 if (MR_PARENT_MENU(in
->mrPopup
) == pmp
->menu
)
4972 med
->mi
= find_entry(
4973 pmp
, NULL
, &med
->mrMi
, None
, -1, -1);
4974 if (med
->mi
&& med
->mrMi
== in
->mrPopup
)
4976 in
->mif
.do_menu
= True
;
4977 in
->mif
.is_submenu_mapped
= True
;
4982 /* This menu must be already mapped somewhere else, so
4983 * ignore it completely. This can only happen if we
4984 * have reached the maximum allowed number of menu
4986 in
->mif
.do_menu
= False
;
4987 in
->mif
.do_popdown
= False
;
4992 return MENU_MLOOP_RET_NORMAL
;
4995 static mloop_ret_code_t
__mloop_do_menu(
4996 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
4997 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, MenuOptions
*pops
,
4998 Bool
*pdoes_submenu_overlap
)
5003 memset(&mp
, 0, sizeof(mp
));
5004 mp
.menu
= in
->mrPopup
;
5005 mp
.pexc
= pmp
->pexc
;
5006 mp
.parent_menu
= pmp
->menu
;
5007 mp
.parent_item
= med
->mi
;
5008 mp
.tear_off_root_menu_window
= pmp
->tear_off_root_menu_window
;
5009 MR_IS_TEAR_OFF_MENU(in
->mrPopup
) = 0;
5010 mp
.flags
.has_default_action
= False
;
5011 mp
.flags
.is_already_mapped
= in
->mif
.is_submenu_mapped
;
5012 mp
.flags
.is_sticky
= False
;
5013 mp
.flags
.is_submenu
= True
;
5014 mp
.flags
.is_triggered_by_keypress
= !!in
->mif
.do_popup_and_warp
;
5016 mp
.ret_paction
= pmp
->ret_paction
;
5017 mp
.screen_origin_x
= pmp
->screen_origin_x
;
5018 mp
.screen_origin_y
= pmp
->screen_origin_y
;
5019 if (in
->mif
.do_propagate_event_into_submenu
)
5021 e
= *(*pmp
->pexc
)->x
.elast
;
5022 mp
.event_propagate_to_submenu
= &e
;
5026 mp
.event_propagate_to_submenu
= NULL
;
5029 /* recursively do the new menu we've moved into */
5030 do_menu(&mp
, pmret
);
5032 in
->mif
.do_propagate_event_into_submenu
= False
;
5033 if (pmret
->rc
== MENU_PROPAGATE_EVENT
)
5035 in
->mif
.do_recycle_event
= 1;
5036 return MENU_MLOOP_RET_LOOP
;
5038 if (IS_MENU_RETURN(pmret
->rc
))
5040 pdkp
->timestamp
= 0;
5041 return MENU_MLOOP_RET_END
;
5043 if (MST_DO_UNMAP_SUBMENU_ON_POPDOWN(pmp
->menu
) &&
5044 pmret
->flags
.is_key_press
)
5046 in
->miRemovedSubmenu
= MR_PARENT_ITEM(in
->mrPopup
);
5047 pop_menu_down_and_repaint_parent(
5048 &in
->mrPopup
, pdoes_submenu_overlap
, pmp
);
5049 in
->mi_with_popup
= NULL
;
5050 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
5051 if (in
->mrPopup
== in
->mrPopdown
)
5053 in
->mrPopdown
= NULL
;
5057 if (pmret
->rc
== MENU_POPDOWN
)
5059 med
->popup_delay_10ms
= 0;
5060 in
->mif
.do_force_reposition
= True
;
5063 return MENU_MLOOP_RET_NORMAL
;
5066 static mloop_ret_code_t
__mloop_handle_action_with_mi(
5067 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
5068 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
,
5069 MenuOptions
*pops
, Bool
*pdoes_submenu_overlap
,
5070 Bool
*pdoes_popdown_submenu_overlap
)
5074 MenuRoot
*mrMiPopup
= NULL
;
5076 pmret
->flags
.do_unpost_submenu
= 0;
5077 /* we're on a menu item */
5078 in
->mif
.is_off_menu_allowed
= False
;
5079 if (med
->mrMi
== pmp
->menu
&& med
->mi
!= in
->miRemovedSubmenu
)
5081 in
->miRemovedSubmenu
= NULL
;
5083 if (med
->mrMi
!= pmp
->menu
&& med
->mrMi
!= in
->mrPopup
&&
5084 med
->mrMi
!= in
->mrPopdown
)
5086 /* we're on an item from a prior menu */
5087 if (med
->mrMi
!= MR_PARENT_MENU(pmp
->menu
))
5089 /* the event is for a previous menu, just close
5091 pmret
->rc
= MENU_PROPAGATE_EVENT
;
5092 pmret
->target_menu
= med
->mrMi
;
5096 pmret
->rc
= MENU_POPDOWN
;
5098 pdkp
->timestamp
= 0;
5099 return MENU_MLOOP_RET_END
;
5101 if (med
->mi
!= MR_SELECTED_ITEM(pmp
->menu
) && med
->mrMi
== pmp
->menu
)
5103 /* new item of the same menu */
5104 __mloop_select_item(
5105 pmp
, in
, med
, *pdoes_submenu_overlap
,
5106 pdoes_popdown_submenu_overlap
);
5108 else if (med
->mi
!= MR_SELECTED_ITEM(pmp
->menu
) && med
->mrMi
&&
5109 med
->mrMi
== in
->mrPopdown
)
5111 /* we're on the popup menu of a different menu item of this
5113 med
->mi
= MR_PARENT_ITEM(in
->mrPopdown
);
5114 in
->mrPopup
= in
->mrPopdown
;
5115 in
->mrPopdown
= NULL
;
5116 *pdoes_submenu_overlap
= *pdoes_popdown_submenu_overlap
;
5117 select_menu_item(pmp
->menu
, med
->mi
, True
, (*pmp
->pexc
)->w
.fw
);
5119 mrMiPopup
= mr_popup_for_mi(pmp
->menu
, med
->mi
);
5120 /* check what has to be done with the item */
5121 if (__mloop_get_mi_actions(
5122 pmp
, pmret
, pdkp
, in
, med
, msi
, mrMiPopup
,
5123 pdoes_submenu_overlap
, pdoes_popdown_submenu_overlap
) ==
5126 return MENU_MLOOP_RET_END
;
5128 /* do what needs to be done */
5129 if (in
->mif
.do_popdown
&& !in
->mif
.do_popup
)
5131 /* popdown previous popup */
5132 __mloop_do_popdown(pmp
, in
, pdoes_popdown_submenu_overlap
);
5134 if (in
->mif
.do_popup
)
5136 if (__mloop_do_popup(
5137 pmp
, pmret
, in
, med
, pops
, mrMiPopup
,
5138 pdoes_submenu_overlap
,
5139 pdoes_popdown_submenu_overlap
) ==
5142 return MENU_MLOOP_RET_END
;
5145 /* remember the 'posted' menu */
5146 if (pmret
->flags
.is_menu_posted
&& in
->mrPopup
!= NULL
&&
5147 pmret
->target_menu
== NULL
)
5149 pmret
->target_menu
= in
->mrPopup
;
5151 else if (pmret
->flags
.is_menu_posted
&& in
->mrPopup
== NULL
)
5153 pmret
->flags
.is_menu_posted
= 0;
5155 if (in
->mif
.do_menu
)
5157 mloop_ret_code_t rc
;
5159 rc
= __mloop_do_menu(
5160 pmp
, pmret
, pdkp
, in
, med
, pops
,
5161 pdoes_submenu_overlap
);
5162 if (rc
!= MENU_MLOOP_RET_NORMAL
)
5167 /* Now check whether we can animate the current popup menu back to the
5168 * original place to unobscure the current menu; this happens only
5169 * when using animation */
5170 if (in
->mrPopup
&& MR_XANIMATION(in
->mrPopup
) &&
5171 (tmi
= find_entry(pmp
, NULL
, &tmrMi
, None
, -1, -1)) &&
5172 (tmi
== MR_SELECTED_ITEM(pmp
->menu
) || tmrMi
!= pmp
->menu
))
5174 animated_move_back(in
->mrPopup
, False
, (*pmp
->pexc
)->w
.fw
);
5176 /* now check whether we should animate the current real menu
5177 * over to the right to unobscure the prior menu; only a very
5178 * limited case where this might be helpful and not too disruptive */
5179 /* but this cause terrible back-and-forth under certain circonstance,
5180 * I think we should disable this ... 2002-09-17 olicha */
5181 if (in
->mrPopup
== NULL
&& pmp
->parent_menu
!= NULL
&&
5182 MR_XANIMATION(pmp
->menu
) != 0 &&
5183 pointer_in_passive_item_area(med
->x_offset
, med
->mrMi
))
5185 /* we have to see if we need menu to be moved */
5186 animated_move_back(pmp
->menu
, True
, (*pmp
->pexc
)->w
.fw
);
5189 if (in
->mrPopdown
!= in
->mrPopup
)
5191 pop_menu_down_and_repaint_parent(
5193 pdoes_popdown_submenu_overlap
, pmp
);
5194 in
->mi_with_popup
= NULL
;
5196 in
->mrPopdown
= NULL
;
5198 in
->mif
.do_popdown
= False
;
5201 return MENU_MLOOP_RET_NORMAL
;
5204 static mloop_ret_code_t
__mloop_handle_action_without_mi(
5205 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
5206 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
,
5207 MenuOptions
*pops
, Bool
*pdoes_submenu_overlap
,
5208 Bool
*pdoes_popdown_submenu_overlap
)
5210 pmret
->flags
.do_unpost_submenu
= 0;
5211 /* moved off menu, deselect selected item... */
5212 if (!MR_SELECTED_ITEM(pmp
->menu
) ||
5213 in
->mif
.is_off_menu_allowed
== True
|| pmret
->flags
.is_menu_posted
)
5216 return MENU_MLOOP_RET_NORMAL
;
5223 if (FQueryPointer(dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
,
5224 &x
, &y
, &JunkX
, &JunkY
, &JunkMask
) == False
)
5226 /* pointer is on a different screen */
5230 if (menu_get_geometry(
5231 pmp
->menu
, &JunkRoot
, &mx
, &my
, &mw
, &mh
, &JunkBW
,
5233 ((!MR_IS_LEFT(in
->mrPopup
) && x
< mx
) ||
5234 (!MR_IS_RIGHT(in
->mrPopup
) && x
> mx
+ mw
) ||
5235 (!MR_IS_UP(in
->mrPopup
) && y
< my
) ||
5236 (!MR_IS_DOWN(in
->mrPopup
) && y
> my
+ mh
)))
5239 pmp
->menu
, MR_SELECTED_ITEM(pmp
->menu
), False
,
5240 (*pmp
->pexc
)->w
.fw
);
5241 pop_menu_down_and_repaint_parent(
5242 &in
->mrPopup
, pdoes_submenu_overlap
, pmp
);
5243 in
->mi_with_popup
= NULL
;
5244 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
5245 if (in
->mrPopup
== in
->mrPopdown
)
5247 in
->mrPopdown
= NULL
;
5251 else if (x
< mx
|| x
>= mx
+ mw
|| y
< my
|| y
>= my
+ mh
)
5253 /* pointer is outside the menu but do not pop down */
5254 in
->mif
.is_off_menu_allowed
= True
;
5258 /* Pointer is still in the menu. Postpone the decision
5259 * if we have to pop down. */
5261 } /* if (in->mrPopup) */
5265 pmp
->menu
, MR_SELECTED_ITEM(pmp
->menu
), False
,
5266 (*pmp
->pexc
)->w
.fw
);
5269 return MENU_MLOOP_RET_NORMAL
;
5272 static void __mloop_exit_warp_back(MenuParameters
*pmp
)
5277 if (pmp
->parent_menu
&& MR_SELECTED_ITEM(pmp
->parent_menu
))
5279 warp_pointer_to_item(
5280 pmp
->parent_menu
, MR_SELECTED_ITEM(pmp
->parent_menu
),
5282 tmi
= find_entry(pmp
, NULL
, &tmrMi
, None
, -1, -1);
5283 if (pmp
->parent_menu
!= tmrMi
&& MR_XANIMATION(pmp
->menu
) == 0)
5285 /* Warping didn't take us to the correct menu, i.e. the
5286 * spot we want to warp to is obscured. So raise our
5288 XRaiseWindow(dpy
, MR_WINDOW(pmp
->parent_menu
));
5295 static void __mloop_exit_select_in_place(
5296 MenuParameters
*pmp
, mloop_evh_data_t
*med
, MenuOptions
*pops
)
5299 XWindowAttributes win_attribs
;
5301 last_saved_pos_hints
.flags
.is_last_menu_pos_hints_valid
= True
;
5302 last_saved_pos_hints
.pos_hints
.x_offset
= 0;
5303 last_saved_pos_hints
.pos_hints
.x_factor
= 0;
5304 last_saved_pos_hints
.pos_hints
.context_x_factor
= 0;
5305 last_saved_pos_hints
.pos_hints
.y_factor
= 0;
5306 last_saved_pos_hints
.pos_hints
.is_relative
= False
;
5307 last_saved_pos_hints
.pos_hints
.is_menu_relative
= False
;
5308 submenu
= mr_popup_for_mi(pmp
->menu
, med
->mi
);
5309 if (submenu
&& MR_WINDOW(submenu
) != None
&&
5310 XGetWindowAttributes(dpy
, MR_WINDOW(submenu
), &win_attribs
) &&
5311 win_attribs
.map_state
== IsViewable
&&
5313 submenu
, &JunkRoot
, &last_saved_pos_hints
.pos_hints
.x
,
5314 &last_saved_pos_hints
.pos_hints
.y
, &JunkWidth
, &JunkHeight
,
5315 &JunkBW
, &JunkDepth
))
5317 /* The submenu is mapped, just take its position and put it in
5318 * the position hints. */
5320 else if (pops
->flags
.has_poshints
)
5322 last_saved_pos_hints
.pos_hints
= pops
->pos_hints
;
5328 get_prefered_popup_position(
5329 pmp
->menu
, submenu
, &last_saved_pos_hints
. pos_hints
.x
,
5330 &last_saved_pos_hints
. pos_hints
.y
, &dummy
);
5332 if (pops
->flags
.do_warp_on_select
)
5334 last_saved_pos_hints
.flags
.do_warp_title
= 1;
5340 static void __mloop_exit_selected(
5341 MenuParameters
*pmp
, MenuReturn
*pmret
, mloop_evh_data_t
*med
,
5344 /* save action to execute so that the menu may be destroyed now */
5345 if (pmp
->ret_paction
)
5347 if (pmret
->rc
== MENU_EXEC_CMD
)
5349 *pmp
->ret_paction
= safestrdup(*pmp
->ret_paction
);
5353 if (*pmp
->ret_paction
)
5355 free(*pmp
->ret_paction
);
5357 *pmp
->ret_paction
= (med
->mi
) ?
5358 safestrdup(MI_ACTION(med
->mi
)) : NULL
;
5362 pmp
->ret_paction
&& *pmp
->ret_paction
&&
5363 med
->mi
&& MI_IS_POPUP(med
->mi
))
5365 get_popup_options(pmp
, med
->mi
, pops
);
5366 if (pops
->flags
.do_select_in_place
)
5368 __mloop_exit_select_in_place(pmp
, med
, pops
);
5372 last_saved_pos_hints
.flags
.do_ignore_pos_hints
= True
;
5373 } /* pops->flags.do_select_in_place */
5374 last_saved_pos_hints
.pos_hints
.screen_origin_x
=
5375 pmp
->screen_origin_x
;
5376 last_saved_pos_hints
.pos_hints
.screen_origin_y
=
5377 pmp
->screen_origin_y
;
5383 static void __mloop_exit(
5384 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
,
5385 mloop_evh_input_t
*in
, mloop_evh_data_t
*med
, mloop_static_info_t
*msi
,
5389 Bool do_deselect
= False
;
5393 pop_menu_down_and_repaint_parent(&in
->mrPopdown
, &no
, pmp
);
5394 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
5397 pmret
->rc
== MENU_SELECTED
&& is_double_click(
5398 msi
->t0
, med
->mi
, pmp
, pmret
, pdkp
,
5399 in
->mif
.has_mouse_moved
))
5401 pmret
->rc
= MENU_DOUBLE_CLICKED
;
5404 pmret
->rc
== MENU_SELECTED
&& med
->mi
&&
5405 MI_FUNC_TYPE(med
->mi
) == F_TEARMENUOFF
)
5407 pmret
->rc
= (MR_IS_TEAR_OFF_MENU(pmp
->menu
)) ?
5408 MENU_KILL_TEAR_OFF_MENU
: MENU_TEAR_OFF
;
5413 case MENU_PROPAGATE_EVENT
:
5414 case MENU_DOUBLE_CLICKED
:
5416 /* Allow popdown to warp back pointer to main menu with mouse
5417 button control. (MoveLeft/MoveRight on a mouse binding) */
5418 if (((pmret
->rc
== MENU_POPDOWN
&& in
->mif
.is_button_release
)
5419 || in
->mif
.is_key_press
) &&
5420 pmret
->rc
!= MENU_DOUBLE_CLICKED
)
5422 if (!pmp
->flags
.is_submenu
)
5424 /* abort a root menu rather than pop it down */
5425 pmret
->rc
= MENU_ABORTED
;
5427 __mloop_exit_warp_back(pmp
);
5432 case MENU_KILL_TEAR_OFF_MENU
:
5437 __mloop_exit_selected(pmp
, pmret
, med
, pops
);
5438 pmret
->rc
= MENU_DONE
;
5443 if (do_deselect
&& MR_SELECTED_ITEM(pmp
->menu
))
5446 pmp
->menu
, MR_SELECTED_ITEM(pmp
->menu
), False
,
5447 (*pmp
->pexc
)->w
.fw
);
5449 if (pmret
->rc
== MENU_SUBMENU_TORN_OFF
)
5452 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
5456 pop_menu_down_and_repaint_parent(&in
->mrPopup
, &no
, pmp
);
5457 MR_SUBMENU_ITEM(pmp
->menu
) = NULL
;
5459 pmret
->flags
.is_key_press
= in
->mif
.is_key_press
;
5464 static void __menu_loop(
5465 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
)
5467 mloop_evh_input_t mei
;
5468 mloop_ret_code_t mloop_ret
;
5469 mloop_evh_data_t med
;
5470 mloop_static_info_t msi
;
5473 Bool does_submenu_overlap
= False
;
5474 Bool does_popdown_submenu_overlap
= False
;
5476 __mloop_init(pmp
, pmret
, &mei
, &med
, &msi
, &mops
);
5477 for (is_finished
= False
; !is_finished
; )
5479 mloop_ret
= __mloop_get_event(pmp
, pmret
, &mei
, &med
, &msi
);
5482 case MENU_MLOOP_RET_END
:
5484 case MENU_MLOOP_RET_LOOP
:
5489 mloop_ret
= __mloop_handle_event(
5490 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
);
5493 case MENU_MLOOP_RET_END
:
5496 case MENU_MLOOP_RET_LOOP
:
5501 /* Now handle new menu items, whether it is from a keypress or
5502 * a pointer motion event. */
5505 mloop_ret
= __mloop_handle_action_with_mi(
5506 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
, &mops
,
5507 &does_submenu_overlap
,
5508 &does_popdown_submenu_overlap
);
5512 mloop_ret
= __mloop_handle_action_without_mi(
5513 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
, &mops
,
5514 &does_submenu_overlap
,
5515 &does_popdown_submenu_overlap
);
5517 if (mloop_ret
== MENU_MLOOP_RET_END
)
5523 __mloop_exit(pmp
, pmret
, pdkp
, &mei
, &med
, &msi
, &mops
);
5529 * Functions dealing with tear off menus
5532 static char *menu_strip_tear_off_title(MenuRoot
*mr
)
5539 for (mi
= MR_FIRST_ITEM(mr
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
5541 if (MI_IS_TITLE(mi
))
5545 else if (!MI_IS_SEPARATOR(mi
) && !MI_IS_TEAR_OFF_BAR(mi
))
5547 /* a normal item, no title found */
5550 /* skip separators and tear off bars */
5552 if (mi
== NULL
|| !MI_HAS_TEXT(mi
) || MI_NEXT_ITEM(mi
) == NULL
)
5556 /* extract the window title from the labels */
5557 for (i
= 0, len
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
5559 if (MI_LABEL(mi
)[i
] != 0)
5561 len
+= strlen(MI_LABEL(mi
)[i
]) + 1;
5568 name
= safemalloc(len
+ 1);
5570 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++)
5572 if (MI_LABEL(mi
)[i
] != 0)
5574 strcat(name
, MI_LABEL(mi
)[i
]);
5578 /* strip the last space */
5580 /* unlink and destroy the item */
5581 unlink_item_from_menu(mr
, mi
);
5587 static void menu_tear_off(MenuRoot
*mr_to_copy
)
5592 XSizeHints menusizehints
;
5593 XClassHint menuclasshints
;
5594 XTextProperty menunametext
;
5595 XWMHints menuwmhints
;
5596 char *list
[] ={ NULL
, NULL
};
5602 unsigned int add_mask
= 0;
5603 initial_window_options_t win_opts
;
5605 exec_context_changes_t ecc
;
5608 cond_rc_t
*cond_rc
= NULL
;
5609 const exec_context_t
*exc
= NULL
;
5611 /* keep the menu open */
5612 if (MR_WINDOW(mr_to_copy
) != None
)
5614 discard_window_events(
5615 MR_WINDOW(mr_to_copy
), SubstructureNotifyMask
);
5617 mr
= clone_menu(mr_to_copy
);
5618 /* also dump the menu style */
5619 buffer
= (char *)safemalloc(23);
5620 sprintf(buffer
,"%lu",(unsigned long)mr
);
5622 ms
= menustyle_parse_style(F_PASS_ARGS
);
5625 /* this must never happen */
5627 ERR
, "menu_tear_off",
5628 "impossible to create %s menu style", buffer
);
5630 DestroyMenu(mr
, False
, False
);
5634 menustyle_copy(MR_STYLE(mr_to_copy
),ms
);
5636 MST_USAGE_COUNT(mr
) = 0;
5637 name
= menu_strip_tear_off_title(mr
);
5638 /* create the menu window and size the menu */
5639 make_menu(mr
, True
);
5641 if (menu_get_geometry(
5642 mr_to_copy
, &JunkRoot
, &x
, &y
, &JunkWidth
, &JunkHeight
,
5643 &JunkBW
, &JunkDepth
))
5645 add_mask
= PPosition
;
5646 XMoveWindow(dpy
, MR_WINDOW(mr
), x
, y
);
5653 menuwmhints
.flags
= InputHint
;
5654 menuwmhints
.input
= False
;
5656 menusizehints
.flags
= PBaseSize
| PMinSize
| PMaxSize
| add_mask
;
5657 menusizehints
.base_width
= 0;
5658 menusizehints
.base_height
= 0;
5659 menusizehints
.min_width
= MR_WIDTH(mr
);
5660 menusizehints
.min_height
= MR_HEIGHT(mr
);
5661 menusizehints
.max_width
= MR_WIDTH(mr
);
5662 menusizehints
.max_height
= MR_HEIGHT(mr
);
5663 /* class, resource and names */
5664 menuclasshints
.res_name
=
5665 (name
!= NULL
) ? name
: safestrdup(MR_NAME(mr
));
5666 menuclasshints
.res_class
= safestrdup("fvwm_menu");
5667 for (t
= menuclasshints
.res_name
; t
!= NULL
&& *t
!= 0; t
++)
5669 /* replace tabs in the title with spaces */
5675 list
[0] = menuclasshints
.res_name
;
5676 menunametext
.value
= NULL
;
5677 XStringListToTextProperty(list
, 1, &menunametext
);
5678 /* set all properties and hints */
5680 dpy
, MR_WINDOW(mr
), &menunametext
, &menunametext
, NULL
, 0,
5681 &menusizehints
, NULL
, &menuclasshints
);
5682 XSetWMHints(dpy
, MR_WINDOW(mr
), &menuwmhints
);
5683 protocols
[0] = _XA_WM_DELETE_WINDOW
;
5684 XSetWMProtocols(dpy
, MR_WINDOW(mr
), &(protocols
[0]), 1);
5687 if (menunametext
.value
!= NULL
)
5689 XFree(menunametext
.value
);
5691 free(menuclasshints
.res_class
);
5692 free(menuclasshints
.res_name
);
5694 /* manage the window */
5695 memset(&win_opts
, 0, sizeof(win_opts
));
5696 win_opts
.flags
.is_menu
= True
;
5697 ev
.type
= MapRequest
;
5698 ev
.xmaprequest
.send_event
= True
;
5699 ev
.xmaprequest
.display
= dpy
;
5700 ev
.xmaprequest
.parent
= Scr
.Root
;
5701 ev
.xmaprequest
.window
= MR_WINDOW(mr
);
5702 fev_fake_event(&ev
);
5703 ecc
.type
= EXCT_NULL
;
5704 ecc
.x
.etrigger
= &ev
;
5705 ecc
.w
.w
= MR_WINDOW(mr
);
5706 ecc
.w
.wcontext
= C_ROOT
;
5707 ea
.exc
= exc_create_context(
5708 &ecc
, ECC_TYPE
| ECC_ETRIGGER
| ECC_W
| ECC_WCONTEXT
);
5709 HandleMapRequestKeepRaised(&ea
, None
, NULL
, &win_opts
);
5710 exc_destroy_context(ea
.exc
);
5715 static void do_menu_close_tear_off_menu(MenuRoot
*mr
, MenuParameters mp
)
5717 pop_menu_down(&mr
, &mp
);
5718 menustyle_free(MR_STYLE(mr
));
5719 DestroyMenu(mr
, False
, False
);
5722 /* ---------------------------- interface functions ------------------------- */
5724 void menus_init(void)
5726 memset((&Menus
), 0, sizeof(MenuInfo
));
5731 /* menus_find_menu expects a token as the input. Make sure you have used
5732 * GetNextToken before passing a menu name to remove quotes (if necessary) */
5733 MenuRoot
*menus_find_menu(char *name
)
5741 for (mr
= Menus
.all
; mr
!= NULL
; mr
= MR_NEXT_MENU(mr
))
5743 if (!MR_IS_DESTROYED(mr
) && mr
== MR_ORIGINAL_MENU(mr
) &&
5744 MR_NAME(mr
) != NULL
&& StrEquals(name
, MR_NAME(mr
)))
5753 void menus_remove_style_from_menus(MenuStyle
*ms
)
5757 for (mr
= Menus
.all
; mr
; mr
= MR_NEXT_MENU(mr
))
5759 if (MR_STYLE(mr
) == ms
)
5761 MR_STYLE(mr
) = menustyle_get_default_style();
5762 MR_IS_UPDATED(mr
) = 1;
5770 * Functions dealing with tear off menus
5773 void menu_enter_tear_off_menu(const exec_context_t
*exc
)
5776 char *ret_action
= NULL
;
5780 const exec_context_t
*exc2
;
5781 exec_context_changes_t ecc
;
5784 dpy
, FW_W(exc
->w
.fw
), MenuContext
,
5785 (caddr_t
*)&mr
) == XCNOENT
)
5791 ecc
.w
.wcontext
= C_ROOT
;
5792 exc2
= exc_clone_context(exc
, &ecc
, ECC_FW
| ECC_W
| ECC_WCONTEXT
);
5793 memset(&mops
, 0, sizeof(mops
));
5794 memset(&mret
, 0, sizeof(MenuReturn
));
5795 memset(&mp
, 0, sizeof(mp
));
5798 mp
.tear_off_root_menu_window
= exc
->w
.fw
;
5799 MR_IS_TEAR_OFF_MENU(mr
) = 1;
5800 mp
.flags
.has_default_action
= 0;
5801 mp
.flags
.is_already_mapped
= True
;
5802 mp
.flags
.is_sticky
= False
;
5803 mp
.flags
.is_submenu
= False
;
5805 mp
.ret_paction
= &ret_action
;
5806 do_menu(&mp
, &mret
);
5807 exc_destroy_context(exc2
);
5812 void menu_close_tear_off_menu(FvwmWindow
*fw
)
5816 const exec_context_t
*exc
;
5817 exec_context_changes_t ecc
;
5820 dpy
, FW_W(fw
), MenuContext
, (caddr_t
*)&mr
) == XCNOENT
)
5824 memset(&mp
, 0, sizeof(mp
));
5828 ecc
.w
.wcontext
= C_ROOT
;
5829 exc
= exc_create_context(&ecc
, ECC_FW
| ECC_W
| ECC_WCONTEXT
);
5831 do_menu_close_tear_off_menu(mr
, mp
);
5832 exc_destroy_context(exc
);
5837 Bool
menu_redraw_transparent_tear_off_menu(FvwmWindow
*fw
, Bool pr_only
)
5840 MenuStyle
*ms
= NULL
;
5843 if (!(IS_TEAR_OFF_MENU(fw
) &&
5844 XFindContext(dpy
, FW_W(fw
), MenuContext
,(caddr_t
*)&mr
) !=
5846 (ms
= MR_STYLE(mr
)) &&
5847 ST_HAS_MENU_CSET(ms
)))
5852 cs
= ST_CSET_MENU(ms
);
5854 if (!CSET_IS_TRANSPARENT(cs
) ||
5855 (pr_only
&& !CSET_IS_TRANSPARENT_PR(cs
)))
5860 return UpdateBackgroundTransparency(
5861 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
),
5863 &Colorset
[ST_CSET_MENU(ms
)],
5865 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
5871 * Initiates a menu pop-up
5873 * fStick = True = sticky menu, stays up on initial button release.
5874 * fStick = False = transient menu, drops on initial release.
5876 * eventp = 0: menu opened by mouse, do not warp
5877 * eventp > 1: root menu opened by keypress with 'Menu', warp pointer and
5878 * allow 'double-keypress'.
5879 * eventp = 1: menu opened by keypress, warp but forbid 'double-keypress'
5880 * this should always be used except in the call in 'staysup_func'
5883 * Returns one of MENU_NOP, MENU_ERROR, MENU_ABORTED, MENU_DONE
5884 * do_menu() may destroy the *pmp->pexec member and replace it with a new
5885 * copy. Be sure not to rely on the original structure being kept intact
5886 * when calling do_menu().
5888 void do_menu(MenuParameters
*pmp
, MenuReturn
*pmret
)
5892 Bool fWasAlreadyPopped
= False
;
5894 Bool is_pointer_grabbed
= False
;
5895 Bool is_pointer_ungrabbed
= False
;
5896 Bool do_menu_interaction
;
5897 Bool do_check_pop_down
;
5899 Time t0
= fev_get_evtime();
5901 double_keypress dkp
;
5902 /* don't save these ones, we want them to work even within recursive
5903 * menus popped up by dynamic actions. */
5904 static int indirect_depth
= 0;
5907 static Bool has_mouse_moved
= False
;
5911 pmret
->rc
= MENU_NOP
;
5912 if (pmp
->flags
.is_sticky
&& !pmp
->flags
.is_submenu
)
5914 FCheckTypedEvent(dpy
, ButtonPressMask
, &tmpevent
);
5916 if (pmp
->menu
== NULL
)
5918 pmret
->rc
= MENU_ERROR
;
5921 key_press
= pmp
->flags
.is_triggered_by_keypress
;
5923 /* Try to pick a root-relative optimal x,y to
5924 * put the mouse right on the title w/o warping */
5925 if (FQueryPointer(dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
,
5926 &x
, &y
, &JunkX
, &JunkY
, &JunkMask
) == False
)
5928 /* pointer is on a different screen */
5932 /* Save these - we want to warp back here if this is a top level
5933 * menu brought up by a keystroke */
5934 if (!pmp
->flags
.is_submenu
)
5936 pmp
->flags
.is_invoked_by_key_press
= key_press
;
5937 pmp
->flags
.is_first_root_menu
= !indirect_depth
;
5941 pmp
->flags
.is_first_root_menu
= 0;
5943 if (!pmp
->flags
.is_submenu
&& indirect_depth
== 0)
5955 pmp
->screen_origin_x
= pmp
->pops
->pos_hints
.screen_origin_x
;
5956 pmp
->screen_origin_y
= pmp
->pops
->pos_hints
.screen_origin_y
;
5958 if (pmp
->pops
->flags
.do_warp_title
)
5962 else if (pmp
->pops
->flags
.do_not_warp
)
5974 /* Figure out where we should popup, if possible */
5975 if (!pmp
->flags
.is_already_mapped
)
5977 Bool prefer_left_submenus
= False
;
5978 /* Make sure we are using the latest style and menu layout. */
5979 update_menu(pmp
->menu
, pmp
);
5981 if (pmp
->flags
.is_submenu
)
5983 /* this is a submenu popup */
5984 get_prefered_popup_position(
5985 pmp
->parent_menu
, pmp
->menu
, &x
, &y
,
5986 &prefer_left_submenus
);
5990 fscreen_scr_arg fscr
;
5992 /* we're a top level menu */
5993 has_mouse_moved
= False
;
5994 if (!GrabEm(CRS_MENU
, GRAB_MENU
))
5996 /* GrabEm specifies the cursor to use */
5998 pmret
->rc
= MENU_ABORTED
;
6001 is_pointer_grabbed
= True
;
6002 /* Make the menu appear under the pointer rather than
6007 &fscr
, FSCREEN_XYPOS
, &scr_x
, &scr_y
, &scr_w
,
6009 x
-= menudim_middle_x_offset(&MR_DIM(pmp
->menu
));
6010 y
-= menuitem_middle_y_offset(
6011 MR_FIRST_ITEM(pmp
->menu
), MR_STYLE(pmp
->menu
));
6022 /* pop_menu_up may move the x,y to make it fit on screen more
6023 * nicely. It might also move parent_menu out of the way. */
6025 &(pmp
->menu
), pmp
, pmp
->parent_menu
, NULL
,
6026 pmp
->pexc
, x
, y
, prefer_left_submenus
, do_warp
,
6027 pmp
->pops
, NULL
, None
))
6030 UngrabEm(GRAB_MENU
);
6031 pmret
->rc
= MENU_ERROR
;
6037 fWasAlreadyPopped
= True
;
6038 if (pmp
->tear_off_root_menu_window
!= NULL
)
6040 if (!GrabEm(CRS_MENU
, GRAB_MENU
))
6042 /* GrabEm specifies the cursor to use */
6044 pmret
->rc
= MENU_ABORTED
;
6047 is_pointer_grabbed
= True
;
6051 warp_pointer_to_item(
6052 pmp
->menu
, MR_FIRST_ITEM(pmp
->menu
),
6053 True
/* skip Title */);
6057 /* Remember the key that popped up the root menu. */
6058 if (pmp
->flags
.is_submenu
)
6064 if (pmp
->flags
.is_triggered_by_keypress
)
6066 /* we have a real key event */
6067 dkp
.keystate
= (*pmp
->pexc
)->x
.etrigger
->xkey
.state
;
6068 dkp
.keycode
= (*pmp
->pexc
)->x
.etrigger
->xkey
.keycode
;
6071 (key_press
&& pmp
->flags
.has_default_action
) ? t0
: 0;
6073 if (!pmp
->flags
.is_submenu
&& indirect_depth
== 0)
6075 /* we need to grab the keyboard so we are sure no key presses
6077 MyXGrabKeyboard(dpy
);
6079 /* This may loop for tear off menus */
6080 for (do_menu_interaction
= True
; do_menu_interaction
== True
; )
6082 if (is_pointer_ungrabbed
&& !GrabEm(CRS_MENU
, GRAB_MENU
))
6084 /* re-grab the pointer in this cycle */
6086 pmret
->rc
= MENU_ABORTED
;
6089 do_menu_interaction
= False
;
6090 if (pmp
->pops
->flags
.do_tear_off_immediately
== 1)
6092 pmret
->rc
= MENU_TEAR_OFF
;
6096 if (!pmp
->flags
.is_submenu
)
6099 dpy
, Scr
.NoFocusWin
, XEVMASK_MENUNFW
);
6102 __menu_loop(pmp
, pmret
, &dkp
);
6103 if (!pmp
->flags
.is_submenu
)
6106 dpy
, Scr
.NoFocusWin
, XEVMASK_NOFOCUSW
);
6110 do_check_pop_down
= False
;
6114 menu_tear_off(pmp
->menu
);
6115 pop_menu_down(&pmp
->menu
, pmp
);
6117 case MENU_KILL_TEAR_OFF_MENU
:
6118 if (MR_IS_TEAR_OFF_MENU(pmp
->menu
))
6121 do_menu_close_tear_off_menu(pmp
->menu
, *pmp
);
6122 pmret
->rc
= MENU_ABORTED
;
6126 /* pass return code up to the torn off menu */
6129 case MENU_DOUBLE_CLICKED
:
6131 if (MR_IS_TEAR_OFF_MENU(pmp
->menu
))
6133 do_menu_interaction
= True
;
6137 do_check_pop_down
= True
;
6140 case MENU_SUBMENU_TORN_OFF
:
6141 pmret
->rc
= MENU_ABORTED
;
6142 do_check_pop_down
= True
;
6145 do_check_pop_down
= True
;
6148 if (do_check_pop_down
== True
)
6150 /* popping down may destroy the menu via the dynamic
6151 * popdown action! */
6152 if (!MR_IS_TEAR_OFF_MENU(pmp
->menu
) &&
6153 fWasAlreadyPopped
== False
)
6155 pop_menu_down(&pmp
->menu
, pmp
);
6160 if (!pmp
->flags
.is_submenu
&& x_start
>= 0 && y_start
>= 0 &&
6161 pmret
->flags
.is_key_press
&& pmret
->rc
!= MENU_TEAR_OFF
)
6163 /* warp pointer back to where invoked if this was
6164 * brought up with a keypress and we're returning from
6165 * a top level menu, and a button release event didn't
6168 dpy
, 0, Scr
.Root
, 0, 0, Scr
.MyDisplayWidth
,
6169 Scr
.MyDisplayHeight
, x_start
, y_start
);
6170 if ((*pmp
->pexc
)->x
.elast
->type
== KeyPress
)
6172 XEvent e
= *(*pmp
->pexc
)->x
.elast
;
6174 e
.xkey
.x_root
= x_start
;
6175 e
.xkey
.y_root
= y_start
;
6179 if (pmret
->rc
== MENU_TEAR_OFF
)
6181 pmret
->rc
= MENU_SUBMENU_TORN_OFF
;
6185 if (is_pointer_grabbed
)
6187 UngrabEm(GRAB_MENU
);
6188 WaitForButtonsUp(True
);
6189 is_pointer_ungrabbed
= True
;
6191 if (!pmp
->flags
.is_submenu
)
6193 if (pmret
->rc
== MENU_DONE
)
6195 if (pmp
->ret_paction
&& *pmp
->ret_paction
)
6198 /* Execute the action */
6199 __menu_execute_function(
6200 pmp
->pexc
, *pmp
->ret_paction
);
6202 free(*pmp
->ret_paction
);
6203 *pmp
->ret_paction
= NULL
;
6205 last_saved_pos_hints
.flags
.
6206 do_ignore_pos_hints
= False
;
6207 last_saved_pos_hints
.flags
.
6208 is_last_menu_pos_hints_valid
= False
;
6210 if (indirect_depth
== 0)
6212 last_saved_pos_hints
.flags
.
6213 do_ignore_pos_hints
= False
;
6214 last_saved_pos_hints
.flags
.
6215 is_last_menu_pos_hints_valid
= False
;
6219 if (!pmp
->flags
.is_submenu
&& indirect_depth
== 0)
6221 /* release the keyboard when the last menu closes */
6222 MyXUngrabKeyboard(dpy
);
6228 Bool
menu_expose(XEvent
*event
, FvwmWindow
*fw
)
6230 MenuRoot
*mr
= NULL
;
6233 dpy
, event
->xany
.window
, MenuContext
, (caddr_t
*)&mr
) !=
6236 flush_accumulate_expose(event
->xany
.window
, event
);
6237 paint_menu(mr
, event
, fw
);
6250 * update_transparent_menu_bg - set the background of the menu to
6251 * match a forseen move of a menu. If the background is updated
6252 * for the target position before the move is done, and repainted
6253 * after the move, the move will look more seamless.
6255 * This method should be folleowd by a call to repaint_transparent_menu
6256 * with the same step_x, stey_y, end_x and any_y, with is_bg_set True
6258 void update_transparent_menu_bg(
6259 MenuRepaintTransparentParameters
*prtm
,
6260 int current_x
, int current_y
, int step_x
, int step_y
,
6261 int end_x
, int end_y
)
6269 if (step_x
== end_x
&& step_y
== end_y
)
6273 if (!last
&& CSET_IS_TRANSPARENT_PR_TINT(ST_CSET_MENU(ms
)))
6278 SetWindowBackgroundWithOffset(
6279 dpy
, MR_WINDOW(mr
), step_x
- current_x
, step_y
- current_y
,
6280 MR_WIDTH(mr
), MR_HEIGHT(mr
),
6281 &Colorset
[ST_CSET_MENU(ms
)], Pdepth
,
6282 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)), False
);
6289 * repaint_transparent_menu - repaint the menu background if it is
6290 * tranparent during an animated move. Called in move_resize.c
6291 * (AnimatedMoveAnyWindow). Performance improvement Welcome!
6292 * ideas: - write the foreground into a pixmap and a mask the first time
6293 * this function is called. Then use these pixmaps to draw
6295 * - Use a Buffer if !IS_TRANSPARENT_PR_PURE and if we do not have one
6298 void repaint_transparent_menu(
6299 MenuRepaintTransparentParameters
*prtm
,
6300 Bool first
, int x
, int y
, int end_x
, int end_y
, Bool is_bg_set
)
6312 if (x
== end_x
&& y
== end_y
)
6316 if (!last
&& CSET_IS_TRANSPARENT_PR_TINT(ST_CSET_MENU(ms
)))
6323 SetWindowBackground(
6324 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
), MR_HEIGHT(mr
),
6325 &Colorset
[ST_CSET_MENU(ms
)], Pdepth
,
6326 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)), False
);
6328 /* redraw the background of non active item */
6329 for (mi
= MR_FIRST_ITEM(mr
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
6331 if (mi
== MR_SELECTED_ITEM(mr
) && MST_DO_HILIGHT_BACK(mr
))
6335 left
= MR_HILIGHT_X_OFFSET(mr
) - MR_ITEM_X_OFFSET(mr
);
6340 MR_ITEM_X_OFFSET(mr
), MI_Y_OFFSET(mi
),
6341 left
, MI_HEIGHT(mi
) +
6342 MST_RELIEF_THICKNESS(mr
), 0);
6349 s_h
+= MI_HEIGHT(mi
);
6353 e_h
+= MI_HEIGHT(mi
);
6357 dpy
, MR_WINDOW(mr
), MR_ITEM_X_OFFSET(mr
), MST_BORDER_WIDTH(mr
),
6358 MR_ITEM_WIDTH(mr
), s_h
, 0);
6362 dpy
, MR_WINDOW(mr
), MR_ITEM_X_OFFSET(mr
),
6363 s_h
+ h
+ MST_RELIEF_THICKNESS(mr
) +
6364 MST_BORDER_WIDTH(mr
), MR_ITEM_WIDTH(mr
), e_h
, 0);
6367 /* now redraw the items */
6368 for (mi
= MR_FIRST_ITEM(mr
); mi
!= NULL
; mi
= MI_NEXT_ITEM(mi
))
6370 MenuPaintItemParameters mpip
;
6372 if (mi
== MR_SELECTED_ITEM(mr
) && MST_DO_HILIGHT_BACK(mr
) &&
6373 !CSET_IS_TRANSPARENT_PR_TINT(ST_CSET_MENU(ms
)))
6377 get_menu_paint_item_parameters(
6378 &mpip
, mr
, NULL
, prtm
->fw
, NULL
, True
);
6379 mpip
.flags
.is_first_item
= (MR_FIRST_ITEM(mr
) == mi
);
6380 menuitem_paint(mi
, &mpip
);
6383 /* if we have a side pic and no side colors we shound repaint the side
6385 if ((MR_SIDEPIC(mr
) || MST_SIDEPIC(mr
)) &&
6386 !MR_HAS_SIDECOLOR(mr
) && !MST_HAS_SIDE_COLOR(mr
))
6388 FvwmPicture
*sidePic
;
6391 sidePic
= MR_SIDEPIC(mr
);
6393 else if (MST_SIDEPIC(mr
))
6395 sidePic
= MST_SIDEPIC(mr
);
6402 dpy
, MR_WINDOW(mr
), MR_SIDEPIC_X_OFFSET(mr
),
6403 MST_BORDER_WIDTH(mr
), sidePic
->width
,
6404 MR_HEIGHT(mr
) - 2 * MST_BORDER_WIDTH(mr
), 0);
6405 paint_side_pic(mr
, NULL
);
6409 Bool
DestroyMenu(MenuRoot
*mr
, Bool do_recreate
, Bool is_command_request
)
6412 MenuRoot
*tmp
, *prev
;
6413 Bool in_list
= True
;
6420 /* seek menu in master list */
6423 while (tmp
&& tmp
!= mr
)
6426 tmp
= MR_NEXT_MENU(tmp
);
6434 if (MR_MAPPED_COPIES(mr
) > 0 &&
6435 (is_command_request
|| MR_COPIES(mr
) == 1))
6437 /* can't destroy a menu while in use */
6438 fvwm_msg(ERR
, "DestroyMenu", "Menu %s is in use", MR_NAME(mr
));
6442 if (in_list
&& MR_COPIES(mr
) > 1 && MR_ORIGINAL_MENU(mr
) == mr
)
6447 /* find a new 'original' menu */
6448 for (m
= Menus
.all
, new_orig
= NULL
; m
; m
= MR_NEXT_MENU(m
))
6450 if (m
!= mr
&& MR_ORIGINAL_MENU(m
) == mr
)
6452 if (new_orig
== NULL
)
6456 MR_ORIGINAL_MENU(m
) = new_orig
;
6459 MR_ORIGINAL_MENU(mr
) = new_orig
;
6460 /* now dump old original menu */
6464 if (MR_STORED_ITEM(mr
).stored
)
6466 XFreePixmap(dpy
, MR_STORED_ITEM(mr
).stored
);
6468 if (MR_COPIES(mr
) > 0)
6470 do_recreate
= False
;
6474 /* free all items */
6475 mi
= MR_FIRST_ITEM(mr
);
6478 tmp2
= MI_NEXT_ITEM(mi
);
6484 /* just dump the menu items but keep the menu itself */
6486 MR_FIRST_ITEM(mr
) = NULL
;
6487 MR_LAST_ITEM(mr
) = NULL
;
6488 MR_SELECTED_ITEM(mr
) = NULL
;
6489 MR_CONTINUATION_MENU(mr
) = NULL
;
6490 MR_PARENT_MENU(mr
) = NULL
;
6492 memset(&(MR_STORED_ITEM(mr
)), 0 ,
6493 sizeof(MR_STORED_ITEM(mr
)));
6494 MR_IS_UPDATED(mr
) = 1;
6499 /* unlink menu from list */
6504 Menus
.all
= MR_NEXT_MENU(mr
);
6508 MR_NEXT_MENU(prev
) = MR_NEXT_MENU(mr
);
6511 /* destroy the window and the display */
6512 if (MR_WINDOW(mr
) != None
)
6514 XDeleteContext(dpy
, MR_WINDOW(mr
), MenuContext
);
6516 XDestroyWindow(MR_CREATE_DPY(mr
), MR_WINDOW(mr
));
6517 MR_WINDOW(mr
) = None
;
6518 XFlush(MR_CREATE_DPY(mr
));
6520 if (MR_CREATE_DPY(mr
) != NULL
&& MR_CREATE_DPY(mr
) != dpy
)
6522 XCloseDisplay(MR_CREATE_DPY(mr
));
6523 MR_CREATE_DPY(mr
) = NULL
;
6526 if (MR_COPIES(mr
) == 0)
6528 if (MR_POPUP_ACTION(mr
))
6530 free(MR_POPUP_ACTION(mr
));
6532 if (MR_POPDOWN_ACTION(mr
))
6534 free(MR_POPDOWN_ACTION(mr
));
6536 if (MR_MISSING_SUBMENU_FUNC(mr
))
6538 free(MR_MISSING_SUBMENU_FUNC(mr
));
6543 PDestroyFvwmPicture(dpy
, MR_SIDEPIC(mr
));
6545 memset(mr
->s
, 0, sizeof(*(mr
->s
)));
6548 memset(mr
->d
, 0, sizeof(*(mr
->d
)));
6550 memset(mr
, 0, sizeof(*mr
));
6556 /* FollowMenuContinuations
6557 * Given an menu root, return the menu root to add to by
6558 * following continuation links until there are no more
6560 MenuRoot
*FollowMenuContinuations(MenuRoot
*mr
, MenuRoot
**pmrPrior
)
6563 while ((mr
!= NULL
) &&
6564 (MR_CONTINUATION_MENU(mr
) != NULL
))
6567 mr
= MR_CONTINUATION_MENU(mr
);
6576 * AddToMenu - add an item to a root menu
6582 * menu - pointer to the root menu to add the item
6583 * item - the text to appear in the menu
6584 * action - the string to possibly execute
6586 * ckh - need to add boolean to say whether or not to expand for pixmaps,
6587 * so built in window list can handle windows w/ * and % in title.
6591 MenuRoot
*mr
, char *item
, char *action
, Bool fPixmapsOk
, Bool fNoPlus
,
6592 Bool is_continuation_item
)
6598 char *option
= NULL
;
6601 Bool do_replace_title
;
6603 if (MR_MAPPED_COPIES(mr
) > 0)
6605 /* whoa, we can't handle *everything* */
6608 if ((item
== NULL
|| *item
== 0) && (action
== NULL
|| *action
== 0) &&
6613 /* empty items screw up our menu when painted, so we replace them with
6619 * Handle dynamic actions
6622 if (StrEquals(item
, "DynamicPopupAction"))
6624 if (MR_POPUP_ACTION(mr
))
6626 free(MR_POPUP_ACTION(mr
));
6628 if (!action
|| *action
== 0)
6630 MR_POPUP_ACTION(mr
) = NULL
;
6634 MR_POPUP_ACTION(mr
) = stripcpy(action
);
6638 else if (StrEquals(item
, "DynamicPopdownAction"))
6640 if (MR_POPDOWN_ACTION(mr
))
6642 free(MR_POPDOWN_ACTION(mr
));
6644 if (!action
|| *action
== 0)
6646 MR_POPDOWN_ACTION(mr
) = NULL
;
6650 MR_POPDOWN_ACTION(mr
) = stripcpy(action
);
6654 else if (StrEquals(item
, "MissingSubmenuFunction"))
6656 if (MR_MISSING_SUBMENU_FUNC(mr
))
6658 free(MR_MISSING_SUBMENU_FUNC(mr
));
6660 if (!action
|| *action
== 0)
6662 MR_MISSING_SUBMENU_FUNC(mr
) = NULL
;
6666 MR_MISSING_SUBMENU_FUNC(mr
) = stripcpy(action
);
6675 if (action
== NULL
|| *action
== 0)
6679 GetNextToken(GetNextToken(action
, &token
), &option
);
6680 tmp
= menuitem_create();
6681 if (MR_FIRST_ITEM(mr
) != NULL
&& StrEquals(token
, "title") &&
6682 option
!= NULL
&& StrEquals(option
, "top"))
6684 do_replace_title
= True
;
6688 do_replace_title
= False
;
6690 append_item_to_menu(mr
, tmp
, do_replace_title
);
6700 MI_ACTION(tmp
) = stripcpy(action
);
6708 for (i
= 0; i
< MAX_MENU_ITEM_LABELS
; i
++, start
= end
)
6710 /* Read label up to next tab. */
6713 if (i
< MAX_MENU_ITEM_LABELS
- 1)
6715 while (*end
&& *end
!= '\t')
6717 /* seek next tab or end of string */
6723 /* remove all tabs in last label */
6733 /* Copy the label. */
6734 MI_LABEL(tmp
)[i
] = safemalloc(end
- start
+ 1);
6735 strncpy(MI_LABEL(tmp
)[i
], start
, end
- start
);
6736 (MI_LABEL(tmp
)[i
])[end
- start
] = 0;
6745 MI_LABEL(tmp
)[i
] = NULL
;
6748 /* Parse the label. */
6749 if (MI_LABEL(tmp
)[i
] != NULL
)
6753 string_def_t item_pixmaps
[] = {
6754 {'*', __scan_for_pixmap
},
6755 {'%', __scan_for_pixmap
},
6757 string_context_t ctx
;
6759 SCTX_SET_MI(ctx
,tmp
);
6761 MI_LABEL(tmp
)[i
], item_pixmaps
, &ctx
);
6763 if (!MI_HAS_HOTKEY(tmp
))
6765 /* pete@tecc.co.uk */
6766 scanForHotkeys(tmp
, i
);
6768 if (!MI_HAS_HOTKEY(tmp
) &&
6769 *MI_LABEL(tmp
)[i
] != 0)
6771 MI_HOTKEY_COFFSET(tmp
) = 0;
6772 MI_HOTKEY_COLUMN(tmp
) = i
;
6773 MI_HAS_HOTKEY(tmp
) = 1;
6774 MI_IS_HOTKEY_AUTOMATIC(tmp
) = 1;
6777 if (*(MI_LABEL(tmp
)[i
]))
6779 MI_HAS_TEXT(tmp
) = True
;
6783 free(MI_LABEL(tmp
)[i
]);
6784 MI_LABEL(tmp
)[i
] = NULL
;
6787 MI_LABEL_STRLEN(tmp
)[i
] =
6788 (MI_LABEL(tmp
)[i
]) ? strlen(MI_LABEL(tmp
)[i
]) : 0;
6792 * Set the type flags
6795 if (is_continuation_item
)
6797 MI_IS_CONTINUATION(tmp
) = True
;
6799 find_func_t(MI_ACTION(tmp
), &(MI_FUNC_TYPE(tmp
)), NULL
);
6800 switch (MI_FUNC_TYPE(tmp
))
6803 MI_IS_POPUP(tmp
) = True
;
6806 MI_IS_MENU(tmp
) = True
;
6809 MI_IS_TITLE(tmp
) = True
;
6814 is_empty
= (!MI_HAS_TEXT(tmp
) && !MI_HAS_PICTURE(tmp
));
6817 if (MI_FUNC_TYPE(tmp
) == F_TEARMENUOFF
)
6819 MI_IS_TEAR_OFF_BAR(tmp
) = 1;
6820 MI_IS_SEPARATOR(tmp
) = 0;
6824 MI_IS_TEAR_OFF_BAR(tmp
) = 0;
6825 MI_IS_SEPARATOR(tmp
) = 1;
6828 if (MI_IS_SEPARATOR(tmp
))
6830 /* An empty title is handled like a separator. */
6831 MI_IS_TITLE(tmp
) = False
;
6833 MI_IS_SELECTABLE(tmp
) =
6834 ((MI_HAS_TEXT(tmp
) || MI_HAS_PICTURE(tmp
) ||
6835 MI_IS_TEAR_OFF_BAR(tmp
)) && !MI_IS_TITLE(tmp
));
6842 MR_IS_UPDATED(mr
) = 1;
6850 * NewMenuRoot - create a new menu root
6856 * name - the name of the menu root
6859 MenuRoot
*NewMenuRoot(char *name
)
6862 string_def_t root_strings
[] = {
6863 {'@', __scan_for_pixmap
},
6864 {'^', __scan_for_color
},
6866 string_context_t ctx
;
6868 mr
= (MenuRoot
*)safemalloc(sizeof(MenuRoot
));
6869 mr
->s
= (MenuRootStatic
*)safemalloc(sizeof(MenuRootStatic
));
6870 mr
->d
= (MenuRootDynamic
*)safemalloc(sizeof(MenuRootDynamic
));
6872 memset(mr
->s
, 0, sizeof(MenuRootStatic
));
6873 memset(mr
->d
, 0, sizeof(MenuRootDynamic
));
6874 MR_NEXT_MENU(mr
) = Menus
.all
;
6875 MR_NAME(mr
) = safestrdup(name
);
6876 MR_WINDOW(mr
) = None
;
6877 SCTX_SET_MR(ctx
,mr
);
6878 MR_HAS_SIDECOLOR(mr
) = False
;
6880 MR_NAME(mr
), root_strings
, &ctx
);
6881 MR_STYLE(mr
) = menustyle_get_default_style();
6882 MR_ORIGINAL_MENU(mr
) = mr
;
6884 MR_IS_UPDATED(mr
) = 1;
6890 void SetMenuCursor(Cursor cursor
)
6895 for (mr
= Menus
.all
; mr
; mr
= MR_NEXT_MENU(mr
))
6899 XDefineCursor(dpy
, MR_WINDOW(mr
), cursor
);
6906 void UpdateAllMenuStyles(void)
6910 for (ms
= menustyle_get_default_style(); ms
; ms
= ST_NEXT_STYLE(ms
))
6912 menustyle_update(ms
);
6918 void UpdateMenuColorset(int cset
)
6923 for (ms
= menustyle_get_default_style(); ms
; ms
= ST_NEXT_STYLE(ms
))
6925 if ((ST_HAS_MENU_CSET(ms
) && ST_CSET_MENU(ms
) == cset
) ||
6926 (ST_HAS_ACTIVE_CSET(ms
) && ST_CSET_ACTIVE(ms
) == cset
) ||
6927 (ST_HAS_GREYED_CSET(ms
) && ST_CSET_GREYED(ms
) == cset
) ||
6928 (ST_HAS_TITLE_CSET(ms
) && ST_CSET_TITLE(ms
) == cset
))
6930 menustyle_update(ms
);
6934 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
6936 MenuRoot
*mr
= NULL
;
6937 MenuStyle
*ms
= NULL
;
6939 if (IS_TEAR_OFF_MENU(t
) &&
6940 XFindContext(dpy
, FW_W(t
), MenuContext
, (caddr_t
*)&mr
) !=
6942 (ms
= MR_STYLE(mr
)))
6944 if (ST_HAS_MENU_CSET(ms
) &&
6945 ST_CSET_MENU(ms
) == cset
)
6947 SetWindowBackground(
6948 dpy
, MR_WINDOW(mr
), MR_WIDTH(mr
),
6950 &Colorset
[ST_CSET_MENU(ms
)],
6952 FORE_GC(MST_MENU_INACTIVE_GCS(mr
)),
6955 else if ((ST_HAS_ACTIVE_CSET(ms
) &&
6956 ST_CSET_ACTIVE(ms
) == cset
) ||
6957 (ST_HAS_GREYED_CSET(ms
) &&
6958 ST_CSET_GREYED(ms
) == cset
))
6960 paint_menu(mr
, NULL
, NULL
);
6968 /* This is called by the window list function */
6969 void change_mr_menu_style(MenuRoot
*mr
, char *stylename
)
6971 MenuStyle
*ms
= NULL
;
6973 ms
= menustyle_find(stylename
);
6978 if (MR_MAPPED_COPIES(mr
) != 0)
6980 fvwm_msg(ERR
,"ChangeMenuStyle", "menu %s is in use",
6986 MR_IS_UPDATED(mr
) = 1;
6992 void add_another_menu_item(char *action
)
6998 mr
= FollowMenuContinuations(Scr
.last_added_item
.item
, &mrPrior
);
7003 if (MR_MAPPED_COPIES(mr
) != 0)
7005 fvwm_msg(ERR
,"add_another_menu_item", "menu is in use");
7008 rest
= GetNextToken(action
,&item
);
7009 AddToMenu(mr
, item
, rest
, True
/* pixmap scan */, False
, False
);
7019 * get_menu_options is used for Menu, Popup and WindowList
7020 * It parses strings matching
7022 * [ [context-rectangle] x y ] [special-options] [other arguments]
7024 * and returns a pointer to the first part of the input string that doesn't
7025 * match this syntax.
7027 * See documentation for a detailed description.
7029 char *get_menu_options(
7030 char *action
, Window w
, FvwmWindow
*fw
, XEvent
*e
, MenuRoot
*mr
,
7031 MenuItem
*mi
, MenuOptions
*pops
)
7034 char *naction
= action
;
7045 Window context_window
= None
;
7046 Bool fHasContext
, fUseItemOffset
, fRectangleContext
, fXineramaRoot
;
7047 Bool fValidPosHints
=
7048 last_saved_pos_hints
.flags
.is_last_menu_pos_hints_valid
;
7049 Bool is_action_empty
= False
;
7050 Bool once_more
= True
;
7051 Bool is_icon_context
;
7054 /* If this is set we may want to reverse the position hints, so don't
7055 * sum up the totals right now. This is useful for the SubmenusLeft
7058 fXineramaRoot
= False
;
7059 last_saved_pos_hints
.flags
.is_last_menu_pos_hints_valid
= False
;
7063 "get_menu_options","no MenuOptions pointer passed");
7068 memset(&(pops
->flags
), 0, sizeof(pops
->flags
));
7069 pops
->flags
.has_poshints
= 0;
7070 if (!action
|| *action
== 0)
7072 is_action_empty
= True
;
7074 while (action
!= NULL
&& *action
!= 0 && once_more
)
7076 /* ^ just to be able to jump to end of loop without 'goto' */
7078 pops
->pos_hints
.is_relative
= False
;
7079 pops
->pos_hints
.menu_width
= 0;
7080 pops
->pos_hints
.is_menu_relative
= False
;
7081 /* parse context argument (if present) */
7082 naction
= GetNextToken(taction
, &tok
);
7085 /* no context string */
7086 fHasContext
= False
;
7087 is_action_empty
= True
;
7090 /* set to False for absolute hints! */
7091 pops
->pos_hints
.is_relative
= True
;
7092 fUseItemOffset
= False
;
7094 fRectangleContext
= False
;
7095 is_icon_context
= False
;
7096 if (StrEquals(tok
, "context"))
7100 context_window
= MR_WINDOW(mr
);
7104 if (IS_ICONIFIED(fw
))
7106 is_icon_context
= True
;
7107 get_icon_geometry(fw
, &icon_g
);
7108 context_window
= None
;
7121 else if (StrEquals(tok
,"menu"))
7125 context_window
= MR_WINDOW(mr
);
7126 pops
->pos_hints
.is_menu_relative
= True
;
7127 pops
->pos_hints
.menu_width
= MR_WIDTH(mr
);
7130 else if (StrEquals(tok
,"item"))
7134 context_window
= MR_WINDOW(mr
);
7135 fUseItemOffset
= True
;
7136 pops
->pos_hints
.is_menu_relative
= True
;
7137 pops
->pos_hints
.menu_width
= MR_WIDTH(mr
);
7140 else if (StrEquals(tok
,"icon"))
7142 if (fw
&& IS_ICONIFIED(fw
))
7144 is_icon_context
= True
;
7145 get_icon_geometry(fw
, &icon_g
);
7146 context_window
= None
;
7149 else if (StrEquals(tok
,"window"))
7151 if (fw
&& !IS_ICONIFIED(fw
))
7153 context_window
= FW_W_FRAME(fw
);
7156 else if (StrEquals(tok
,"interior"))
7158 if (fw
&& !IS_ICONIFIED(fw
))
7160 context_window
= FW_W(fw
);
7163 else if (StrEquals(tok
,"title"))
7167 if (IS_ICONIFIED(fw
))
7170 FW_W_ICON_TITLE(fw
);
7174 context_window
= FW_W_TITLE(fw
);
7178 else if (strncasecmp(tok
,"button",6) == 0)
7180 if (sscanf(&(tok
[6]),"%d",&button
) != 1 ||
7181 tok
[6] == '+' || tok
[6] == '-' || button
< 0 ||
7184 fHasContext
= False
;
7186 else if (fw
&& !IS_ICONIFIED(fw
))
7188 button
= BUTTON_INDEX(button
);
7189 context_window
= FW_W_BUTTON(fw
, button
);
7192 else if (StrEquals(tok
,"root"))
7194 context_window
= Scr
.Root
;
7195 pops
->pos_hints
.is_relative
= False
;
7197 else if (StrEquals(tok
,"xineramaroot"))
7199 context_window
= Scr
.Root
;
7200 pops
->pos_hints
.is_relative
= False
;
7201 fXineramaRoot
= True
;
7203 else if (StrEquals(tok
,"mouse"))
7205 context_window
= None
;
7207 else if (StrEquals(tok
,"rectangle"))
7216 /* parse the rectangle */
7218 naction
= GetNextToken(naction
, &tok
);
7221 fvwm_msg(ERR
, "get_menu_options",
7222 "missing rectangle geometry");
7223 if (!pops
->pos_hints
.has_screen_origin
)
7225 /* xinerama: emergency fallback */
7226 pops
->pos_hints
.has_screen_origin
= 1;
7227 pops
->pos_hints
.screen_origin_x
= 0;
7228 pops
->pos_hints
.screen_origin_y
= 0;
7232 flags
= FScreenParseGeometryWithScreen(
7234 (unsigned int*)&width
,
7235 (unsigned int*)&height
, &screen
);
7236 if ((flags
& (XValue
| YValue
)) != (XValue
| YValue
))
7239 fvwm_msg(ERR
, "get_menu_options",
7240 "invalid rectangle geometry");
7241 if (!pops
->pos_hints
.has_screen_origin
)
7243 /* xinerama: emergency fallback */
7244 pops
->pos_hints
.has_screen_origin
= 1;
7245 pops
->pos_hints
.screen_origin_x
= 0;
7246 pops
->pos_hints
.screen_origin_y
= 0;
7250 pops
->pos_hints
.has_screen_origin
= 1;
7251 FScreenGetScrRect(NULL
, screen
, &sx
, &sy
, &sw
, &sh
);
7252 pops
->pos_hints
.screen_origin_x
= sx
;
7253 pops
->pos_hints
.screen_origin_y
= sy
;
7254 if (!(flags
& WidthValue
))
7258 if (!(flags
& HeightValue
))
7264 if (flags
& XNegative
)
7266 x
= sx
+ sw
- x
- width
;
7268 if (flags
& YNegative
)
7270 y
= sy
+ sh
- y
- height
;
7272 pops
->pos_hints
.is_relative
= False
;
7273 fRectangleContext
= True
;
7275 else if (StrEquals(tok
,"this"))
7281 dpy
, w
, MenuContext
,
7282 (caddr_t
*)&dummy_mr
) != XCNOENT
)
7286 /* the parent menu */
7287 pops
->pos_hints
.is_menu_relative
=
7289 pops
->pos_hints
.menu_width
=
7296 /* no context string */
7297 fHasContext
= False
;
7313 if (fRectangleContext
)
7315 if (!pops
->pos_hints
.has_screen_origin
)
7317 /* xinerama: use global screen as reference */
7318 pops
->pos_hints
.has_screen_origin
= 1;
7319 pops
->pos_hints
.screen_origin_x
= -1;
7320 pops
->pos_hints
.screen_origin_y
= -1;
7322 /* nothing else to do */
7324 else if (!is_icon_context
&&
7325 (!fHasContext
|| !context_window
||
7327 dpy
, context_window
, &JunkRoot
, &JunkX
,
7328 &JunkY
, (unsigned int*)&width
,
7329 (unsigned int*)&height
,
7330 (unsigned int*)&JunkBW
,
7331 (unsigned int*)&JunkDepth
) ||
7332 !XTranslateCoordinates(
7333 dpy
, context_window
, Scr
.Root
, 0, 0, &x
, &y
,
7336 /* no window or could not get geometry */
7338 dpy
,Scr
.Root
, &JunkRoot
, &JunkChild
, &x
,
7339 &y
, &JunkX
, &JunkY
, &JunkMask
) == False
)
7341 /* pointer is on a different screen - that's
7347 if (!pops
->pos_hints
.has_screen_origin
)
7349 /* xinerama: use screen with pinter as
7351 pops
->pos_hints
.has_screen_origin
= 1;
7352 pops
->pos_hints
.screen_origin_x
= x
;
7353 pops
->pos_hints
.screen_origin_y
= y
;
7358 if (is_icon_context
)
7362 width
= icon_g
.width
;
7363 height
= icon_g
.height
;
7365 /* we have a context window */
7368 y
+= MI_Y_OFFSET(mi
);
7369 height
= MI_HEIGHT(mi
);
7371 if (!pops
->pos_hints
.has_screen_origin
)
7373 pops
->pos_hints
.has_screen_origin
= 1;
7376 /* use whole screen */
7377 pops
->pos_hints
.screen_origin_x
= -1;
7378 pops
->pos_hints
.screen_origin_y
= -1;
7380 else if (context_window
== Scr
.Root
)
7382 /* xinerama: use screen that contains
7383 * the window center as reference */
7384 if (!fev_get_evpos_or_query(
7385 dpy
, context_window
, e
,
7392 screen_origin_x
= 0;
7394 screen_origin_y
= 0;
7398 fscreen_scr_arg fscr
;
7400 fscr
.xypos
.x
= pops
->pos_hints
.
7402 fscr
.xypos
.y
= pops
->pos_hints
.
7405 &fscr
, FSCREEN_XYPOS
,
7412 /* xinerama: use screen that contains
7413 * the window center as reference */
7414 pops
->pos_hints
.screen_origin_x
=
7416 pops
->pos_hints
.screen_origin_y
=
7422 /* parse position arguments */
7423 taction
= get_one_menu_position_argument(
7424 naction
, x
, width
, &(pops
->pos_hints
.x
),
7425 &(pops
->pos_hints
.x_offset
),
7426 &(pops
->pos_hints
.x_factor
),
7427 &(pops
->pos_hints
.context_x_factor
),
7428 &pops
->pos_hints
.is_menu_relative
);
7429 if (pops
->pos_hints
.is_menu_relative
)
7431 pops
->pos_hints
.x
= x
;
7432 if (pops
->pos_hints
.menu_width
== 0 && mr
)
7434 pops
->pos_hints
.menu_width
= MR_WIDTH(mr
);
7437 naction
= get_one_menu_position_argument(
7438 taction
, y
, height
, &(pops
->pos_hints
.y
), &dummy_int
,
7439 &(pops
->pos_hints
.y_factor
), &dummy_float
,
7441 if (naction
== taction
)
7443 /* argument is missing or invalid */
7446 fvwm_msg(ERR
, "get_menu_options",
7447 "invalid position arguments");
7454 pops
->flags
.has_poshints
= 1;
7455 if (fValidPosHints
== True
&&
7456 pops
->pos_hints
.is_relative
== True
)
7458 pops
->pos_hints
= last_saved_pos_hints
.pos_hints
;
7460 /* we want to do this only once */
7463 if (is_action_empty
)
7465 if (!pops
->pos_hints
.has_screen_origin
)
7467 pops
->pos_hints
.has_screen_origin
= 1;
7468 if (!fev_get_evpos_or_query(
7470 &pops
->pos_hints
.screen_origin_x
,
7471 &pops
->pos_hints
.screen_origin_y
))
7473 pops
->pos_hints
.screen_origin_x
= 0;
7474 pops
->pos_hints
.screen_origin_y
= 0;
7479 if (!pops
->flags
.has_poshints
&& fValidPosHints
)
7481 pops
->flags
.has_poshints
= 1;
7482 pops
->pos_hints
= last_saved_pos_hints
.pos_hints
;
7483 pops
->pos_hints
.is_relative
= False
;
7487 /* to keep Purify silent */
7488 pops
->flags
.do_select_in_place
= 0;
7489 /* parse additional options */
7490 while (naction
&& *naction
)
7492 naction
= GetNextToken(action
, &tok
);
7497 if (StrEquals(tok
, "WarpTitle"))
7499 pops
->flags
.do_warp_title
= 1;
7500 pops
->flags
.do_not_warp
= 0;
7502 else if (StrEquals(tok
, "NoWarp"))
7504 pops
->flags
.do_warp_title
= 0;
7505 pops
->flags
.do_not_warp
= 1;
7507 else if (StrEquals(tok
, "Fixed"))
7509 pops
->flags
.is_fixed
= 1;
7511 else if (StrEquals(tok
, "SelectInPlace"))
7513 pops
->flags
.do_select_in_place
= 1;
7515 else if (StrEquals(tok
, "SelectWarp"))
7517 pops
->flags
.do_warp_on_select
= 1;
7519 else if (StrEquals(tok
, "TearOffImmediately"))
7521 pops
->flags
.do_tear_off_immediately
= 1;
7531 if (!pops
->flags
.do_select_in_place
)
7533 pops
->flags
.do_warp_on_select
= 0;
7539 /* ---------------------------- new menu loop code ------------------------- */
7546 MTR_FAKE_ENTER_ITEM
= 0x2,
7547 MTR_PROPAGATE_XEVENT_UP
= 0x4,
7548 MTR_PROPAGATE_XEVENT_DOWN
= 0x8,
7549 MTR_POPUP_TIMEOUT
= 0x10,
7550 MTR_POPDOWN_TIMEOUT
= 0x20
7551 } mloop_trigger_type_t
;
7555 mloop_trigger_type_t type
;
7568 MenuRoot
*current_menu
;
7572 unsigned do_fake_enter_item
: 1;
7573 unsigned do_propagete_event_up
: 1;
7574 unsigned do_propagete_event_down
: 1;
7576 } mloop_get_trigger_input_t
;
7579 mloop_trigger_type_t
__mloop_get_trigger(
7580 mloop_trigger_t
*ret_trigger
, mloop_timeouts_t
*io_timeouts
,
7581 const mloop_get_trigger_input_t
* const in
,
7583 if (in_out
->in_flags
->do_propagate_event_down
)
7585 return MTR_PROPAGATE_XEVENT_DOWN
;
7587 else if (in_out
->in_flags
->do_propagate_event_up
)
7591 return MTR_PROPAGATE_XEVENT_UP
;
7596 /*!!!return propagate up*/
7599 /*!!!read event or wait for timeout*/
7600 while (0/*!!!not finished*/)
7603 if (0/*!!!wait for tiomeout*/)
7605 /*!!!check for event*/
7609 /*!!!block for event*/
7613 /*!!!rc = MTR_XEVENT*/
7615 if (0/*!!!popup timed out;break*/)
7617 /*!!!rc = MTR_POPUP;break*/
7619 if (0/*!!!popdown timed out;break*/)
7621 /*!!!rc = MTR_POPDOWN;break*/
7625 if (0/*!!!rc == MTR_XEVENT && evtype == MotionNotify*/)
7627 /*!!!eat up further MotionNotify events*/
7634 void __menu_loop_new(
7635 MenuParameters
*pmp
, MenuReturn
*pmret
, double_keypress
*pdkp
)
7637 mloop_evh_input_t mei
;
7639 mloop_ret_code_t mloop_ret
;
7641 mloop_evh_data_t med
;
7642 mloop_static_info_t msi
;
7646 /*!!!init menu loop*/
7647 __mloop_init(pmp
, pmret
, &mei
, &med
, &msi
, &mops
);
7648 for (is_finished
= False
; !is_finished
; )
7650 mloop_trigger_type_t mtr
;
7652 mtr
= __mloop_get_trigger(
7653 pmp
, pmret
, &mei
, &med
, &msi
);
7659 case MTR_FAKE_ENTER_ITEM
:
7660 /*!!!fake enter item*/
7662 case MTR_PROPAGATE_XEVENT_UP
:
7663 /*!!!handle propagation*/
7665 case MTR_PROPAGATE_XEVENT_DOWN
:
7666 /*!!!handle propagation*/
7668 case MTR_POPUP_TIMEOUT
:
7671 case MTR_POPDOWN_TIMEOUT
:
7672 /*!!!handle popdown*/
7681 mloop_ret
= __mloop_handle_event(
7682 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
);
7685 case MENU_MLOOP_RET_LOOP
:
7687 case MENU_MLOOP_RET_END
:
7693 /* Now handle new menu items, whether it is from a
7694 * keypress or a pointer motion event. */
7697 mloop_ret
= __mloop_handle_action_with_mi(
7698 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
,
7703 mloop_ret
= __mloop_handle_action_without_mi(
7704 pmp
, pmret
, pdkp
, &mei
, &med
, &msi
,
7707 if (mloop_ret
== MENU_MLOOP_RET_END
)
7714 __mloop_exit(pmp
, pmret
, pdkp
, &mei
, &med
, &msi
, &mops
);