BUTTON_TYPE_RADIO
};
+/* This structure is based on the one in ../lwlib/lwlib.h, modified
+ for Windows. */
typedef struct _widget_value
{
/* name of widget */
char* name;
/* value (meaning depend on widget type) */
char* value;
- /* keyboard equivalent. no implications for XtTranslations */
+ /* keyboard equivalent. no implications for XtTranslations */
char* key;
- /* Help string or null if none. */
- char *help;
+ /* Help string or nil if none.
+ GC finds this string through the frame's menu_bar_vector
+ or through menu_items. */
+ Lisp_Object help;
/* true if enabled */
Boolean enabled;
/* true if selected */
#endif
} widget_value;
-/* LocalAlloc/Free is a reasonably good allocator. */
-#define malloc_widget_value() (void*)LocalAlloc (LMEM_ZEROINIT, sizeof (widget_value))
-#define free_widget_value(wv) LocalFree (wv)
+/* Local memory management */
+#define local_heap (GetProcessHeap ())
+#define local_alloc(n) (HeapAlloc (local_heap, HEAP_ZERO_MEMORY, (n)))
+#define local_free(p) (HeapFree (local_heap, 0, ((LPVOID) (p))))
+
+#define malloc_widget_value() ((widget_value *) local_alloc (sizeof (widget_value)))
+#define free_widget_value(wv) (local_free ((wv)))
/******************************************************************/
#define FALSE 0
#endif /* no TRUE */
+static HMENU current_popup_menu;
+
+void syms_of_w32menu ();
+void globals_of_w32menu ();
+
+typedef BOOL (WINAPI * GetMenuItemInfoA_Proc) (
+ IN HMENU,
+ IN UINT,
+ IN BOOL,
+ IN OUT LPMENUITEMINFOA
+ );
+typedef BOOL (WINAPI * SetMenuItemInfoA_Proc) (
+ IN HMENU,
+ IN UINT,
+ IN BOOL,
+ IN LPCMENUITEMINFOA
+ );
+
+GetMenuItemInfoA_Proc get_menu_item_info=NULL;
+SetMenuItemInfoA_Proc set_menu_item_info=NULL;
+
Lisp_Object Vmenu_updating_frame;
Lisp_Object Qdebug_on_next_call;
static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object));
+#ifdef HAVE_DIALOGS
static Lisp_Object w32_dialog_show ();
+#endif
static Lisp_Object w32_menu_show ();
static void keymap_panes ();
static void single_menu_item ();
static void list_of_panes ();
static void list_of_items ();
+void w32_free_menu_strings (HWND);
\f
/* This holds a Lisp vector that holds the results of decoding
the keymaps or alist-of-alists that specify a menu.
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
- XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
+ ASET (menu_items, menu_items_used++, Qnil);
menu_items_submenu_depth++;
}
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
- XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
+ ASET (menu_items, menu_items_used++, Qlambda);
menu_items_submenu_depth--;
}
if (menu_items_used + 1 > menu_items_allocated)
grow_menu_items ();
- XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
+ ASET (menu_items, menu_items_used++, Qquote);
}
/* Start a new menu pane in menu_items.
if (menu_items_submenu_depth == 0)
menu_items_n_panes++;
- XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
- XVECTOR (menu_items)->contents[menu_items_used++] = name;
- XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
+ ASET (menu_items, menu_items_used++, Qt);
+ ASET (menu_items, menu_items_used++, name);
+ ASET (menu_items, menu_items_used++, prefix_vec);
}
/* Push one menu item into the current pane. NAME is the string to
if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
grow_menu_items ();
- XVECTOR (menu_items)->contents[menu_items_used++] = name;
- XVECTOR (menu_items)->contents[menu_items_used++] = enable;
- XVECTOR (menu_items)->contents[menu_items_used++] = key;
- XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
- XVECTOR (menu_items)->contents[menu_items_used++] = def;
- XVECTOR (menu_items)->contents[menu_items_used++] = type;
- XVECTOR (menu_items)->contents[menu_items_used++] = selected;
- XVECTOR (menu_items)->contents[menu_items_used++] = help;
+ ASET (menu_items, menu_items_used++, name);
+ ASET (menu_items, menu_items_used++, enable);
+ ASET (menu_items, menu_items_used++, key);
+ ASET (menu_items, menu_items_used++, equiv);
+ ASET (menu_items, menu_items_used++, def);
+ ASET (menu_items, menu_items_used++, type);
+ ASET (menu_items, menu_items_used++, selected);
+ ASET (menu_items, menu_items_used++, help);
}
\f
/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
else if (VECTORP (item))
{
/* Loop over the char values represented in the vector. */
- int len = XVECTOR (item)->size;
+ int len = ASIZE (item);
int c;
for (c = 0; c < len; c++)
{
Lisp_Object character;
XSETFASTINT (character, c);
- single_menu_item (character, XVECTOR (item)->contents[c],
+ single_menu_item (character, AREF (item, c),
&pending_maps, notreal, maxdepth);
}
}
\f
/* This is a subroutine of single_keymap_panes that handles one
keymap entry.
- KEY is a key in a keymap and ITEM is its binding.
+ KEY is a key in a keymap and ITEM is its binding.
PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
separate panes.
If NOTREAL is nonzero, only check for equivalent key bindings, don't
Lisp_Object map, item_string, enabled;
struct gcpro gcpro1, gcpro2;
int res;
-
+
/* Parse the menu item and leave the result in item_properties. */
GCPRO2 (key, item);
res = parse_menu_item (item, notreal, 0);
if (!res)
return; /* Not a menu item. */
- map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
-
+ map = AREF (item_properties, ITEM_PROPERTY_MAP);
+
if (notreal)
{
/* We don't want to make a menu, just traverse the keymaps to
return;
}
- enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
- item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
+ enabled = AREF (item_properties, ITEM_PROPERTY_ENABLE);
+ item_string = AREF (item_properties, ITEM_PROPERTY_NAME);
- if (!NILP (map) && XSTRING (item_string)->data[0] == '@')
+ if (!NILP (map) && SREF (item_string, 0) == '@')
{
if (!NILP (enabled))
/* An enabled separate pane. Remember this to handle it later. */
}
push_menu_item (item_string, enabled, key,
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
+ AREF (item_properties, ITEM_PROPERTY_DEF),
+ AREF (item_properties, ITEM_PROPERTY_KEYEQ),
+ AREF (item_properties, ITEM_PROPERTY_TYPE),
+ AREF (item_properties, ITEM_PROPERTY_SELECTED),
+ AREF (item_properties, ITEM_PROPERTY_HELP));
/* Display a submenu using the toolkit. */
if (! (NILP (map) || NILP (enabled)))
/* Make that be the pane title of the first pane. */
if (!NILP (prompt) && menu_items_n_panes >= 0)
- XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
+ ASET (menu_items, MENU_ITEMS_PANE_NAME, prompt);
keymaps = 1;
}
/* Make the title be the pane title of the first pane. */
if (!NILP (title) && menu_items_n_panes >= 0)
- XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
+ ASET (menu_items, MENU_ITEMS_PANE_NAME, title);
keymaps = 1;
}
keymaps = 0;
}
-
+
if (NILP (position))
{
discard_menu_items ();
}
#ifdef HAVE_MENUS
+ /* If resources from a previous popup menu exist yet, does nothing
+ until the `menu_free_timer' has freed them (see w32fns.c).
+ */
+ if (current_popup_menu)
+ {
+ discard_menu_items ();
+ UNGCPRO;
+ return Qnil;
+ }
+
/* Display them in a menu. */
BLOCK_INPUT;
UNBLOCK_INPUT;
discard_menu_items ();
+#endif /* HAVE_MENUS */
UNGCPRO;
-#endif /* HAVE_MENUS */
if (error_name) error (error_name);
return selection;
/* Activate the menu bar of frame F.
This is called from keyboard.c when it gets the
- menu_bar_activate_event out of the Emacs event queue.
+ MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
To activate the menu bar, we signal to the input thread that it can
return from the WM_INITMENU message, allowing the normal Windows
But first we recompute the menu bar contents (the whole tree).
This way we can safely execute Lisp code. */
-
+
void
x_activate_menubar (f)
FRAME_PTR f;
i = 0;
while (i < f->menu_bar_items_used)
{
- if (EQ (XVECTOR (vector)->contents[i], Qnil))
+ if (EQ (AREF (vector, i), Qnil))
{
subprefix_stack[submenu_depth++] = prefix;
prefix = entry;
i++;
}
- else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
+ else if (EQ (AREF (vector, i), Qlambda))
{
prefix = subprefix_stack[--submenu_depth];
i++;
}
- else if (EQ (XVECTOR (vector)->contents[i], Qt))
+ else if (EQ (AREF (vector, i), Qt))
{
- prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
+ prefix = AREF (vector, i + MENU_ITEMS_PANE_PREFIX);
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
- entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
+ entry = AREF (vector, i + MENU_ITEMS_ITEM_VALUE);
/* The EMACS_INT cast avoids a warning. There's no problem
as long as pointers have enough bits to hold small integers. */
if ((int) (EMACS_INT) client_data == i)
buf.arg = entry;
kbd_buffer_store_event (&buf);
+ /* Free memory used by owner-drawn and help-echo strings. */
+ w32_free_menu_strings (FRAME_W32_WINDOW (f));
+ f->output_data.w32->menu_command_in_progress = 0;
+ f->output_data.w32->menubar_active = 0;
return;
}
i += MENU_ITEMS_ITEM_LENGTH;
}
}
+ /* Free memory used by owner-drawn and help-echo strings. */
+ w32_free_menu_strings (FRAME_W32_WINDOW (f));
+ f->output_data.w32->menu_command_in_progress = 0;
+ f->output_data.w32->menubar_active = 0;
}
/* Allocate a widget_value, blocking input. */
UNBLOCK_INPUT;
}
\f
-/* Return a tree of widget_value structures for a menu bar item
+/* Set up data i menu_items for a menu bar item
whose event type is ITEM_KEY (with string ITEM_NAME)
and whose contents come from the list of keymaps MAPS. */
-static widget_value *
-single_submenu (item_key, item_name, maps)
+static int
+parse_single_submenu (item_key, item_name, maps)
Lisp_Object item_key, item_name, maps;
{
- widget_value *wv, *prev_wv, *save_wv, *first_wv;
- int i;
- int submenu_depth = 0;
Lisp_Object length;
int len;
Lisp_Object *mapvec;
- widget_value **submenu_stack;
- int previous_items = menu_items_used;
+ int i;
int top_level_items = 0;
length = Flength (maps);
maps = Fcdr (maps);
}
- menu_items_n_panes = 0;
-
/* Loop over the given keymaps, making a pane for each map.
But don't make a pane that is empty--ignore that map instead. */
for (i = 0; i < len; i++)
Qnil, Qnil, Qnil, Qnil);
}
else
- single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
+ {
+ Lisp_Object prompt;
+ prompt = Fkeymap_prompt (mapvec[i]);
+ single_keymap_panes (mapvec[i],
+ !NILP (prompt) ? prompt : item_name,
+ item_key, 0, 10);
+ }
}
- /* Create a tree of widget_value objects
- representing the panes and their items. */
+ return top_level_items;
+}
+
+
+/* Create a tree of widget_value objects
+ representing the panes and items
+ in menu_items starting at index START, up to index END. */
+
+static widget_value *
+digest_single_submenu (start, end, top_level_items)
+ int start, end, top_level_items;
+{
+ widget_value *wv, *prev_wv, *save_wv, *first_wv;
+ int i;
+ int submenu_depth = 0;
+ widget_value **submenu_stack;
submenu_stack
= (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
first_wv = wv;
save_wv = 0;
prev_wv = 0;
-
- /* Loop over all panes and items made during this call
- and construct a tree of widget_value objects.
- Ignore the panes and items made by previous calls to
- single_submenu, even though those are also in menu_items. */
- i = previous_items;
- while (i < menu_items_used)
+
+ /* Loop over all panes and items made by the preceding call
+ to parse_single_submenu and construct a tree of widget_value objects.
+ Ignore the panes and items used by previous calls to
+ digest_single_submenu, even though those are also in menu_items. */
+ i = start;
+ while (i < end)
{
- if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+ if (EQ (AREF (menu_items, i), Qnil))
{
submenu_stack[submenu_depth++] = save_wv;
save_wv = prev_wv;
prev_wv = 0;
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+ else if (EQ (AREF (menu_items, i), Qlambda))
{
prev_wv = save_wv;
save_wv = submenu_stack[--submenu_depth];
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+ else if (EQ (AREF (menu_items, i), Qt)
&& submenu_depth != 0)
i += MENU_ITEMS_PANE_LENGTH;
/* Ignore a nil in the item list.
It's meaningful only for dialog boxes. */
- else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ else if (EQ (AREF (menu_items, i), Qquote))
i += 1;
- else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ else if (EQ (AREF (menu_items, i), Qt))
{
/* Create a new pane. */
Lisp_Object pane_name, prefix;
char *pane_string;
- pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
- prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
#ifndef HAVE_MULTILINGUAL_MENU
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
pane_name = ENCODE_SYSTEM (pane_name);
- AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
+ ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
}
#endif
pane_string = (NILP (pane_name)
- ? "" : (char *) XSTRING (pane_name)->data);
+ ? "" : (char *) SDATA (pane_name));
/* If there is just one top-level pane, put all its items directly
under the top-level menu. */
if (menu_items_n_panes == 1)
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
}
save_wv = wv;
prev_wv = 0;
if (STRING_MULTIBYTE (item_name))
{
item_name = ENCODE_SYSTEM (item_name);
- AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
+ ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
}
if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
{
descrip = ENCODE_SYSTEM (descrip);
- AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
+ ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
}
#endif /* not HAVE_MULTILINGUAL_MENU */
wv = xmalloc_widget_value ();
- if (prev_wv)
+ if (prev_wv)
prev_wv->next = wv;
else
save_wv->contents = wv;
- wv->name = (char *) XSTRING (item_name)->data;
+ wv->name = (char *) SDATA (item_name);
if (!NILP (descrip))
- wv->key = (char *) XSTRING (descrip)->data;
+ wv->key = (char *) SDATA (descrip);
wv->value = 0;
/* The EMACS_INT cast avoids a warning. There's no problem
as long as pointers have enough bits to hold small integers. */
abort ();
wv->selected = !NILP (selected);
- if (STRINGP (help))
- wv->help = (char *) XSTRING (help)->data;
- else
- wv->help = NULL;
+ if (!STRINGP (help))
+ help = Qnil;
+
+ wv->help = help;
prev_wv = wv;
HMENU menubar_widget = f->output_data.w32->menubar_widget;
Lisp_Object items;
widget_value *wv, *first_wv, *prev_wv = 0;
- int i;
+ int i, last_i;
+ int *submenu_start, *submenu_end;
+ int *submenu_top_level_items, *submenu_n_panes;
/* We must not change the menubar when actually in use. */
if (f->output_data.w32->menubar_active)
else if (pending_menu_activation && !deep_p)
deep_p = 1;
- wv = xmalloc_widget_value ();
- wv->name = "menubar";
- wv->value = 0;
- wv->enabled = 1;
- wv->button_type = BUTTON_TYPE_NONE;
- first_wv = wv;
-
if (deep_p)
{
/* Make a widget-value tree representing the entire menu trees. */
struct buffer *prev = current_buffer;
Lisp_Object buffer;
- int specpdl_count = specpdl_ptr - specpdl;
+ int specpdl_count = SPECPDL_INDEX ();
int previous_menu_items_used = f->menu_bar_items_used;
Lisp_Object *previous_items
= (Lisp_Object *) alloca (previous_menu_items_used
items = FRAME_MENU_BAR_ITEMS (f);
- inhibit_garbage_collection ();
-
/* Save the frame's previous menu bar contents data. */
if (previous_menu_items_used)
bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
previous_menu_items_used * sizeof (Lisp_Object));
- /* Fill in the current menu bar contents. */
+ /* Fill in menu_items with the current menu bar contents.
+ This can evaluate Lisp code. */
menu_items = f->menu_bar_vector;
menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
+ submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+ submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
+ submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
+ submenu_top_level_items
+ = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
init_menu_items ();
- for (i = 0; i < XVECTOR (items)->size; i += 4)
+ for (i = 0; i < ASIZE (items); i += 4)
{
Lisp_Object key, string, maps;
- key = XVECTOR (items)->contents[i];
- string = XVECTOR (items)->contents[i + 1];
- maps = XVECTOR (items)->contents[i + 2];
+ last_i = i;
+
+ key = AREF (items, i);
+ string = AREF (items, i + 1);
+ maps = AREF (items, i + 2);
if (NILP (string))
break;
- wv = single_submenu (key, string, maps);
- if (prev_wv)
+ submenu_start[i] = menu_items_used;
+
+ menu_items_n_panes = 0;
+ submenu_top_level_items[i]
+ = parse_single_submenu (key, string, maps);
+ submenu_n_panes[i] = menu_items_n_panes;
+
+ submenu_end[i] = menu_items_used;
+ }
+
+ finish_menu_items ();
+
+ /* Convert menu_items into widget_value trees
+ to display the menu. This cannot evaluate Lisp code. */
+
+ wv = xmalloc_widget_value ();
+ wv->name = "menubar";
+ wv->value = 0;
+ wv->enabled = 1;
+ wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
+ first_wv = wv;
+
+ for (i = 0; i < last_i; i += 4)
+ {
+ menu_items_n_panes = submenu_n_panes[i];
+ wv = digest_single_submenu (submenu_start[i], submenu_end[i],
+ submenu_top_level_items[i]);
+ if (prev_wv)
prev_wv->next = wv;
else
first_wv->contents = wv;
prev_wv = wv;
}
- finish_menu_items ();
-
set_buffer_internal_1 (prev);
unbind_to (specpdl_count, Qnil);
for (i = 0; i < previous_menu_items_used; i++)
if (menu_items_used == i
- || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
+ || (!EQ (previous_items[i], AREF (menu_items, i))))
break;
if (i == menu_items_used && i == previous_menu_items_used && i != 0)
{
}
/* Now GC cannot happen during the lifetime of the widget_value,
- so it's safe to store data from a Lisp_String. */
+ so it's safe to store data from a Lisp_String, as long as
+ local copies are made when the actual menu is created.
+ Windows takes care of this for normal string items, but
+ not for owner-drawn items or additional item-info. */
wv = first_wv->contents;
- for (i = 0; i < XVECTOR (items)->size; i += 4)
+ for (i = 0; i < ASIZE (items); i += 4)
{
Lisp_Object string;
- string = XVECTOR (items)->contents[i + 1];
+ string = AREF (items, i + 1);
if (NILP (string))
break;
- wv->name = (char *) XSTRING (string)->data;
+ wv->name = (char *) SDATA (string);
wv = wv->next;
}
/* Make a widget-value tree containing
just the top level menu bar strings. */
+ wv = xmalloc_widget_value ();
+ wv->name = "menubar";
+ wv->value = 0;
+ wv->enabled = 1;
+ wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
+ first_wv = wv;
+
items = FRAME_MENU_BAR_ITEMS (f);
- for (i = 0; i < XVECTOR (items)->size; i += 4)
+ for (i = 0; i < ASIZE (items); i += 4)
{
Lisp_Object string;
- string = XVECTOR (items)->contents[i + 1];
+ string = AREF (items, i + 1);
if (NILP (string))
break;
wv = xmalloc_widget_value ();
- wv->name = (char *) XSTRING (string)->data;
+ wv->name = (char *) SDATA (string);
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
/* This prevents lwlib from assuming this
menu item is really supposed to be empty. */
/* The EMACS_INT cast avoids a warning.
This value just has to be different from small integers. */
wv->call_data = (void *) (EMACS_INT) (-1);
- if (prev_wv)
+ if (prev_wv)
prev_wv->next = wv;
else
first_wv->contents = wv;
f->output_data.w32->menubar_widget = menubar_widget;
SetMenu (FRAME_W32_WINDOW (f), f->output_data.w32->menubar_widget);
- /* Causes flicker when menu bar is updated
+ /* Causes flicker when menu bar is updated
DrawMenuBar (FRAME_W32_WINDOW (f)); */
/* Force the window size to be recomputed so that the frame's text
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
first_wv = wv;
first_pane = 1;
-
+
/* Loop over all panes and items, filling in the tree. */
i = 0;
while (i < menu_items_used)
{
- if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+ if (EQ (AREF (menu_items, i), Qnil))
{
submenu_stack[submenu_depth++] = save_wv;
save_wv = prev_wv;
first_pane = 1;
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+ else if (EQ (AREF (menu_items, i), Qlambda))
{
prev_wv = save_wv;
save_wv = submenu_stack[--submenu_depth];
first_pane = 0;
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
+ else if (EQ (AREF (menu_items, i), Qt)
&& submenu_depth != 0)
i += MENU_ITEMS_PANE_LENGTH;
/* Ignore a nil in the item list.
It's meaningful only for dialog boxes. */
- else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ else if (EQ (AREF (menu_items, i), Qquote))
i += 1;
- else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ else if (EQ (AREF (menu_items, i), Qt))
{
/* Create a new pane. */
Lisp_Object pane_name, prefix;
if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
{
pane_name = ENCODE_SYSTEM (pane_name);
- AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
+ ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
}
#endif
pane_string = (NILP (pane_name)
- ? "" : (char *) XSTRING (pane_name)->data);
+ ? "" : (char *) SDATA (pane_name));
/* If there is just one top-level pane, put all its items directly
under the top-level menu. */
if (menu_items_n_panes == 1)
wv->value = 0;
wv->enabled = 1;
wv->button_type = BUTTON_TYPE_NONE;
+ wv->help = Qnil;
save_wv = wv;
prev_wv = 0;
}
if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
{
item_name = ENCODE_SYSTEM (item_name);
- AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
+ ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
}
if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
{
descrip = ENCODE_SYSTEM (descrip);
- AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
+ ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
}
#endif /* not HAVE_MULTILINGUAL_MENU */
wv = xmalloc_widget_value ();
- if (prev_wv)
+ if (prev_wv)
prev_wv->next = wv;
- else
+ else
save_wv->contents = wv;
- wv->name = (char *) XSTRING (item_name)->data;
+ wv->name = (char *) SDATA (item_name);
if (!NILP (descrip))
- wv->key = (char *) XSTRING (descrip)->data;
+ wv->key = (char *) SDATA (descrip);
wv->value = 0;
/* Use the contents index as call_data, since we are
restricted to 16-bits. */
abort ();
wv->selected = !NILP (selected);
- if (STRINGP (help))
- wv->help = XSTRING (help)->data;
+ if (!STRINGP (help))
+ help = Qnil;
+
+ wv->help = help;
prev_wv = wv;
so that it looks better. Having two separators looks odd. */
wv_sep->name = "--";
wv_sep->next = first_wv->contents;
+ wv_sep->help = Qnil;
#ifndef HAVE_MULTILINGUAL_MENU
if (STRING_MULTIBYTE (title))
title = ENCODE_SYSTEM (title);
#endif
- wv_title->name = (char *) XSTRING (title)->data;
+ wv_title->name = (char *) SDATA (title);
wv_title->enabled = TRUE;
wv_title->title = TRUE;
wv_title->button_type = BUTTON_TYPE_NONE;
+ wv_title->help = Qnil;
wv_title->next = wv_sep;
first_wv->contents = wv_title;
}
/* Actually create the menu. */
- menu = CreatePopupMenu ();
+ current_popup_menu = menu = CreatePopupMenu ();
fill_in_menu (menu, first_wv->contents);
/* Adjust coordinates to be root-window-relative. */
menu_item_selection = 0;
/* Display the menu. */
- menu_item_selection = SendMessage (FRAME_W32_WINDOW (f),
+ menu_item_selection = SendMessage (FRAME_W32_WINDOW (f),
WM_EMACS_TRACKPOPUPMENU,
(WPARAM)menu, (LPARAM)&pos);
i = 0;
while (i < menu_items_used)
{
- if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
+ if (EQ (AREF (menu_items, i), Qnil))
{
subprefix_stack[submenu_depth++] = prefix;
prefix = entry;
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
+ else if (EQ (AREF (menu_items, i), Qlambda))
{
prefix = subprefix_stack[--submenu_depth];
i++;
}
- else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ else if (EQ (AREF (menu_items, i), Qt))
{
- prefix
- = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
i += MENU_ITEMS_PANE_LENGTH;
}
/* Ignore a nil in the item list.
It's meaningful only for dialog boxes. */
- else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+ else if (EQ (AREF (menu_items, i), Qquote))
i += 1;
else
{
- entry
- = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
if (menu_item_selection == i)
{
if (keymaps != 0)
}
\f
+#ifdef HAVE_DIALOGS
static char * button_names [] = {
"button1", "button2", "button3", "button4", "button5",
"button6", "button7", "button8", "button9", "button10" };
{
Lisp_Object pane_name, prefix;
char *pane_string;
- pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
- prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
+ pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, MENU_ITEMS_PANE_PREFIX);
pane_string = (NILP (pane_name)
- ? "" : (char *) XSTRING (pane_name)->data);
+ ? "" : (char *) SDATA (pane_name));
prev_wv = xmalloc_widget_value ();
prev_wv->value = pane_string;
if (keymaps && !NILP (prefix))
prev_wv->name++;
prev_wv->enabled = 1;
prev_wv->name = "message";
+ prev_wv->help = Qnil;
first_wv = prev_wv;
-
+
/* Loop over all panes and items, filling in the tree. */
i = MENU_ITEMS_PANE_LENGTH;
while (i < menu_items_used)
{
-
+
/* Create a new item within current pane. */
Lisp_Object item_name, enable, descrip, help;
- item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
- enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
- descrip
- = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
- help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
-
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+ help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
if (NILP (item_name))
{
free_menubar_widget_value_tree (first_wv);
prev_wv->next = wv;
wv->name = (char *) button_names[nb_buttons];
if (!NILP (descrip))
- wv->key = (char *) XSTRING (descrip)->data;
- wv->value = (char *) XSTRING (item_name)->data;
- wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+ wv->key = (char *) SDATA (descrip);
+ wv->value = (char *) SDATA (item_name);
+ wv->call_data = (void *) &AREF (menu_items, i);
wv->enabled = !NILP (enable);
+ wv->help = Qnil;
prev_wv = wv;
if (! boundary_seen)
wv = xmalloc_widget_value ();
wv->name = dialog_name;
+ wv->help = Qnil;
/* Dialog boxes use a really stupid name encoding
which specifies how many buttons to use
}
/* Actually create the dialog. */
-#ifdef HAVE_DIALOGS
dialog_id = widget_id_tick++;
menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
f->output_data.w32->widget, 1, 0,
dialog_selection_callback, 0);
lw_modify_all_widgets (dialog_id, first_wv->contents, TRUE);
-#endif
/* Free the widget_value objects we used to specify the contents. */
free_menubar_widget_value_tree (first_wv);
menu_item_selection = 0;
/* Display the menu. */
-#ifdef HAVE_DIALOGS
lw_pop_up_all_widgets (dialog_id);
popup_activated_flag = 1;
/* Process events that apply to the menu. */
popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
- lw_destroy_all_widgets (dialog_id);
-#endif
+ lw_destroy_all_widgets (dialog_id);
/* Find the selected item, and its pane, to return
the proper value. */
{
Lisp_Object entry;
- if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+ if (EQ (AREF (menu_items, i), Qt))
{
- prefix
- = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
i += MENU_ITEMS_PANE_LENGTH;
}
else
{
- entry
- = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
if (menu_item_selection == i)
{
if (keymaps != 0)
return Qnil;
}
+#endif /* HAVE_DIALOGS */
\f
/* Is this item a separator? */
fuFlags = MF_SEPARATOR;
out_string = NULL;
}
- else
+ else
{
if (wv->enabled)
fuFlags = MF_STRING;
else
out_string = wv->name;
- if (wv->title || wv->call_data == 0)
+ if (item != NULL)
+ fuFlags = MF_POPUP;
+ else if (wv->title || wv->call_data == 0)
{
-#if 0 /* no GC while popup menu is active */
- out_string = LocalAlloc (0, strlen (wv->name) + 1);
- strcpy (out_string, wv->name);
+ /* Only use MF_OWNERDRAW if GetMenuItemInfo is usable, since
+ we can't deallocate the memory otherwise. */
+ if (get_menu_item_info)
+ {
+ out_string = (char *) local_alloc (strlen (wv->name) + 1);
+ strcpy (out_string, wv->name);
+#ifdef MENU_DEBUG
+ DebPrint ("Menu: allocing %ld for owner-draw", out_string);
#endif
- fuFlags = MF_OWNERDRAW | MF_DISABLED;
+ fuFlags = MF_OWNERDRAW | MF_DISABLED;
+ }
+ else
+ fuFlags = MF_DISABLED;
}
+
/* Draw radio buttons and tickboxes. */
else if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
wv->button_type == BUTTON_TYPE_RADIO))
fuFlags |= MF_UNCHECKED;
}
- if (item != NULL)
- fuFlags = MF_POPUP;
-
return_value =
AppendMenu (menu,
fuFlags,
/* This must be done after the menu item is created. */
if (!wv->title && wv->call_data != 0)
{
- HMODULE user32 = GetModuleHandle ("user32.dll");
- FARPROC set_menu_item_info = GetProcAddress (user32, "SetMenuItemInfoA");
-
if (set_menu_item_info)
{
MENUITEMINFO info;
info.cbSize = sizeof (info);
info.fMask = MIIM_DATA;
- /* Set help string for menu item. */
- info.dwItemData = (DWORD)wv->help;
+ /* Set help string for menu item. Leave it as a Lisp_Object
+ until it is ready to be displayed, since GC can happen while
+ menus are active. */
+ if (wv->help)
+ info.dwItemData = (DWORD) wv->help;
if (wv->button_type == BUTTON_TYPE_RADIO)
{
supported on NT 3.51 and earlier, as GetMenuItemInfo is not
available. */
void
-w32_menu_display_help (HMENU menu, UINT item, UINT flags)
+w32_menu_display_help (HWND owner, HMENU menu, UINT item, UINT flags)
{
- int pane = 0; /* TODO: Set this to pane number. */
+ if (get_menu_item_info)
+ {
+ struct frame *f = x_window_to_frame (&one_w32_display_info, owner);
+ Lisp_Object frame, help;
- HMODULE user32 = GetModuleHandle ("user32.dll");
- FARPROC get_menu_item_info = GetProcAddress (user32, "GetMenuItemInfoA");
+ // No help echo on owner-draw menu items.
+ if (flags & MF_OWNERDRAW || flags & MF_POPUP)
+ help = Qnil;
+ else
+ {
+ MENUITEMINFO info;
- if (get_menu_item_info)
+ bzero (&info, sizeof (info));
+ info.cbSize = sizeof (info);
+ info.fMask = MIIM_DATA;
+ get_menu_item_info (menu, item, FALSE, &info);
+
+ help = info.dwItemData ? (Lisp_Object) info.dwItemData : Qnil;
+ }
+
+ /* Store the help echo in the keyboard buffer as the X toolkit
+ version does, rather than directly showing it. This seems to
+ solve the GC problems that were present when we based the
+ Windows code on the non-toolkit version. */
+ if (f)
+ {
+ XSETFRAME (frame, f);
+ kbd_buffer_store_help_event (frame, help);
+ }
+ else
+ /* X version has a loop through frames here, which doesn't
+ appear to do anything, unless it has some side effect. */
+ show_help_echo (help, Qnil, Qnil, Qnil, 1);
+ }
+}
+
+/* Free memory used by owner-drawn strings. */
+static void
+w32_free_submenu_strings (menu)
+ HMENU menu;
+{
+ int i, num = GetMenuItemCount (menu);
+ for (i = 0; i < num; i++)
{
- extern Lisp_Object Qmenu_item;
- Lisp_Object *first_item;
- Lisp_Object pane_name;
- Lisp_Object menu_object;
MENUITEMINFO info;
-
bzero (&info, sizeof (info));
info.cbSize = sizeof (info);
- info.fMask = MIIM_DATA;
- get_menu_item_info (menu, item, FALSE, &info);
-
- first_item = XVECTOR (menu_items)->contents;
- if (EQ (first_item[0], Qt))
- pane_name = first_item[MENU_ITEMS_PANE_NAME];
- else if (EQ (first_item[0], Qquote))
- /* This shouldn't happen, see w32_menu_show. */
- pane_name = empty_string;
- else
- pane_name = first_item[MENU_ITEMS_ITEM_NAME];
+ info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU;
+
+ get_menu_item_info (menu, i, TRUE, &info);
- /* (menu-item MENU-NAME PANE-NUMBER) */
- menu_object = Fcons (Qmenu_item,
- Fcons (pane_name,
- Fcons (make_number (pane), Qnil)));
+ /* Owner-drawn names are held in dwItemData. */
+ if ((info.fType & MF_OWNERDRAW) && info.dwItemData)
+ {
+#ifdef MENU_DEBUG
+ DebPrint ("Menu: freeing %ld for owner-draw", info.dwItemData);
+#endif
+ local_free (info.dwItemData);
+ }
- show_help_echo (info.dwItemData ?
- build_string ((char *) info.dwItemData) : Qnil,
- Qnil, menu_object, make_number (item), 1);
+ /* Recurse down submenus. */
+ if (info.hSubMenu)
+ w32_free_submenu_strings (info.hSubMenu);
}
}
+void
+w32_free_menu_strings (hwnd)
+ HWND hwnd;
+{
+ HMENU menu = current_popup_menu;
+
+ if (get_menu_item_info)
+ {
+ /* If there is no popup menu active, free the strings from the frame's
+ menubar. */
+ if (!menu)
+ menu = GetMenu (hwnd);
+
+ if (menu)
+ w32_free_submenu_strings (menu);
+ }
+ current_popup_menu = NULL;
+}
#endif /* HAVE_MENUS */
-\f
-syms_of_w32menu ()
+
+void syms_of_w32menu ()
{
+ globals_of_w32menu ();
staticpro (&menu_items);
menu_items = Qnil;
+ current_popup_menu = NULL;
+
Qdebug_on_next_call = intern ("debug-on-next-call");
staticpro (&Qdebug_on_next_call);
defsubr (&Sx_popup_dialog);
#endif
}
+
+/*
+ globals_of_w32menu is used to initialize those global variables that
+ must always be initialized on startup even when the global variable
+ initialized is non zero (see the function main in emacs.c).
+ globals_of_w32menu is called from syms_of_w32menu when the global
+ variable initialized is 0 and directly from main when initialized
+ is non zero.
+ */
+void globals_of_w32menu ()
+{
+ /* See if Get/SetMenuItemInfo functions are available. */
+ HMODULE user32 = GetModuleHandle ("user32.dll");
+ get_menu_item_info = (GetMenuItemInfoA_Proc) GetProcAddress (user32, "GetMenuItemInfoA");
+ set_menu_item_info = (SetMenuItemInfoA_Proc) GetProcAddress (user32, "SetMenuItemInfoA");
+}