aboutsummaryrefslogtreecommitdiffstats
path: root/src/pgtkmenu.c
diff options
context:
space:
mode:
authorJeff Walsh2019-06-15 14:44:47 +1000
committerJeff Walsh2020-11-22 14:46:55 +1100
commitcdc04b4509772f2324c4ca63732caed2858cedf3 (patch)
tree4c25236482f6faedb806dca4040c098a1364e96c /src/pgtkmenu.c
parent12cc104cd54dd80a9bc1d391a256de49f4b77077 (diff)
downloademacs-cdc04b4509772f2324c4ca63732caed2858cedf3.tar.gz
emacs-cdc04b4509772f2324c4ca63732caed2858cedf3.zip
Implement menubar for pgtk emacs
* src/xdisp.c (display_menu_bar): add pgtk case * ../src/pgtkterm.c (pgtk_create_terminal): update hooks (pgtk_menu_show): delete * src/pgtkterm.h: add decls * src/pgtkmenu.c: new file * ../src/pgtkfns.c (x_set_menu_bar_lines) (x_change_tool_bar_height, x_set_tool_bar_lines) (Fx_create_frame):
Diffstat (limited to 'src/pgtkmenu.c')
-rw-r--r--src/pgtkmenu.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/src/pgtkmenu.c b/src/pgtkmenu.c
new file mode 100644
index 00000000000..bbe47ddad6b
--- /dev/null
+++ b/src/pgtkmenu.c
@@ -0,0 +1,476 @@
1/* Pure GTK3 menu and toolbar module.
2 Copyright (C) 2019 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or (at
9your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19/*
20 */
21
22
23/* This should be the first include, as it may set up #defines affecting
24 interpretation of even the system includes. */
25#include <config.h>
26
27#include "lisp.h"
28#include "frame.h"
29#include "window.h"
30#include "character.h"
31#include "buffer.h"
32#include "keymap.h"
33#include "coding.h"
34#include "commands.h"
35#include "blockinput.h"
36#include "termhooks.h"
37#include "keyboard.h"
38#include "menu.h"
39#include "pdumper.h"
40
41#include "gtkutil.h"
42#include <gtk/gtk.h>
43
44
45Lisp_Object
46pgtk_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
47{
48 return Qnil;
49}
50
51
52
53/* Gtk calls callbacks just because we tell it what item should be
54 selected in a radio group. If this variable is set to a non-zero
55 value, we are creating menus and don't want callbacks right now.
56*/
57static bool xg_crazy_callback_abort;
58
59/* This callback is called from the menu bar pulldown menu
60 when the user makes a selection.
61 Figure out what the user chose
62 and put the appropriate events into the keyboard buffer. */
63static void
64menubar_selection_callback (GtkWidget *widget, gpointer client_data)
65{
66 xg_menu_item_cb_data *cb_data = client_data;
67
68 if (xg_crazy_callback_abort)
69 return;
70
71 if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
72 return;
73
74 /* For a group of radio buttons, GTK calls the selection callback first
75 for the item that was active before the selection and then for the one that
76 is active after the selection. For C-h k this means we get the help on
77 the deselected item and then the selected item is executed. Prevent that
78 by ignoring the non-active item. */
79 if (GTK_IS_RADIO_MENU_ITEM (widget)
80 && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
81 return;
82
83 /* When a menu is popped down, X generates a focus event (i.e. focus
84 goes back to the frame below the menu). Since GTK buffers events,
85 we force it out here before the menu selection event. Otherwise
86 sit-for will exit at once if the focus event follows the menu selection
87 event. */
88
89 block_input ();
90 while (gtk_events_pending ())
91 gtk_main_iteration ();
92 unblock_input ();
93
94 find_and_call_menu_selection (cb_data->cl_data->f,
95 cb_data->cl_data->menu_bar_items_used,
96 cb_data->cl_data->menu_bar_vector,
97 cb_data->call_data);
98}
99
100static void
101menu_highlight_callback (GtkWidget *widget, gpointer call_data)
102{
103 xg_menu_item_cb_data *cb_data;
104 Lisp_Object help;
105
106 cb_data = g_object_get_data (G_OBJECT (widget), XG_ITEM_DATA);
107 if (! cb_data) return;
108
109 help = call_data ? cb_data->help : Qnil;
110}
111
112
113/* This callback is invoked when a dialog or menu is finished being
114 used and has been unposted. */
115
116static void
117popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
118{
119}
120
121
122
123
124/* Set the contents of the menubar widgets of frame F.
125 The argument FIRST_TIME is currently ignored;
126 it is set the first time this is called, from initialize_frame_menubar. */
127
128void
129set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
130{
131 GtkWidget * menubar_widget;
132 Lisp_Object items;
133 widget_value *wv, *first_wv, *prev_wv = 0;
134 int i;
135 int *submenu_start, *submenu_end;
136 bool *submenu_top_level_items;
137 int *submenu_n_panes;
138
139
140 menubar_widget = f->output_data.pgtk->menubar_widget;
141
142 XSETFRAME(Vmenu_updating_frame, f);
143
144 if (! menubar_widget)
145 deep_p = true;
146
147 if (deep_p)
148 {
149 struct buffer *prev = current_buffer;
150 Lisp_Object buffer;
151 ptrdiff_t specpdl_count = SPECPDL_INDEX ();
152 int previous_menu_items_used = f->menu_bar_items_used;
153 Lisp_Object *previous_items
154 = alloca (previous_menu_items_used * sizeof *previous_items);
155 int subitems;
156
157 /* If we are making a new widget, its contents are empty,
158 do always reinitialize them. */
159 if (! menubar_widget)
160 previous_menu_items_used = 0;
161
162 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
163 specbind (Qinhibit_quit, Qt);
164 /* Don't let the debugger step into this code
165 because it is not reentrant. */
166 specbind (Qdebug_on_next_call, Qnil);
167
168 record_unwind_save_match_data ();
169 if (NILP (Voverriding_local_map_menu_flag))
170 {
171 specbind (Qoverriding_terminal_local_map, Qnil);
172 specbind (Qoverriding_local_map, Qnil);
173 }
174
175 set_buffer_internal_1 (XBUFFER (buffer));
176
177 /* Run the Lucid hook. */
178 safe_run_hooks (Qactivate_menubar_hook);
179
180 /* If it has changed current-menubar from previous value,
181 really recompute the menubar from the value. */
182 if (! NILP (Vlucid_menu_bar_dirty_flag))
183 call0 (Qrecompute_lucid_menubar);
184 safe_run_hooks (Qmenu_bar_update_hook);
185 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
186
187 items = FRAME_MENU_BAR_ITEMS (f);
188
189 /* Save the frame's previous menu bar contents data. */
190 if (previous_menu_items_used)
191 memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
192 previous_menu_items_used * word_size);
193
194 /* Fill in menu_items with the current menu bar contents.
195 This can evaluate Lisp code. */
196 save_menu_items ();
197
198 menu_items = f->menu_bar_vector;
199 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
200 subitems = ASIZE (items) / 4;
201 submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
202 submenu_end = alloca (subitems * sizeof *submenu_end);
203 submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
204 submenu_top_level_items = alloca (subitems
205 * sizeof *submenu_top_level_items);
206 init_menu_items ();
207 for (i = 0; i < subitems; i++)
208 {
209 Lisp_Object key, string, maps;
210
211 key = AREF (items, 4 * i);
212 string = AREF (items, 4 * i + 1);
213 maps = AREF (items, 4 * i + 2);
214 if (NILP (string))
215 break;
216
217 submenu_start[i] = menu_items_used;
218
219 menu_items_n_panes = 0;
220 submenu_top_level_items[i]
221 = parse_single_submenu (key, string, maps);
222 submenu_n_panes[i] = menu_items_n_panes;
223
224 submenu_end[i] = menu_items_used;
225 }
226
227 submenu_start[i] = -1;
228 finish_menu_items ();
229
230 /* Convert menu_items into widget_value trees
231 to display the menu. This cannot evaluate Lisp code. */
232
233 wv = make_widget_value ("menubar", NULL, true, Qnil);
234 wv->button_type = BUTTON_TYPE_NONE;
235 first_wv = wv;
236
237 for (i = 0; submenu_start[i] >= 0; i++)
238 {
239 menu_items_n_panes = submenu_n_panes[i];
240 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
241 submenu_top_level_items[i]);
242 if (prev_wv)
243 prev_wv->next = wv;
244 else
245 first_wv->contents = wv;
246 /* Don't set wv->name here; GC during the loop might relocate it. */
247 wv->enabled = true;
248 wv->button_type = BUTTON_TYPE_NONE;
249 prev_wv = wv;
250 }
251
252 set_buffer_internal_1 (prev);
253
254 /* If there has been no change in the Lisp-level contents
255 of the menu bar, skip redisplaying it. Just exit. */
256
257 /* Compare the new menu items with the ones computed last time. */
258 for (i = 0; i < previous_menu_items_used; i++)
259 if (menu_items_used == i
260 || (!EQ (previous_items[i], AREF (menu_items, i))))
261 break;
262 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
263 {
264 /* The menu items have not changed. Don't bother updating
265 the menus in any form, since it would be a no-op. */
266 free_menubar_widget_value_tree (first_wv);
267 discard_menu_items ();
268 unbind_to (specpdl_count, Qnil);
269 return;
270 }
271
272 /* The menu items are different, so store them in the frame. */
273 fset_menu_bar_vector (f, menu_items);
274 f->menu_bar_items_used = menu_items_used;
275
276 /* This undoes save_menu_items. */
277 unbind_to (specpdl_count, Qnil);
278
279 /* Now GC cannot happen during the lifetime of the widget_value,
280 so it's safe to store data from a Lisp_String. */
281 wv = first_wv->contents;
282 for (i = 0; i < ASIZE (items); i += 4)
283 {
284 Lisp_Object string;
285 string = AREF (items, i + 1);
286 if (NILP (string))
287 break;
288 wv->name = SSDATA (string);
289 update_submenu_strings (wv->contents);
290 wv = wv->next;
291 }
292
293 }
294 else
295 {
296 /* Make a widget-value tree containing
297 just the top level menu bar strings. */
298
299 wv = make_widget_value ("menubar", NULL, true, Qnil);
300 wv->button_type = BUTTON_TYPE_NONE;
301 first_wv = wv;
302
303 items = FRAME_MENU_BAR_ITEMS (f);
304 for (i = 0; i < ASIZE (items); i += 4)
305 {
306 Lisp_Object string;
307
308 string = AREF (items, i + 1);
309 if (NILP (string))
310 break;
311
312 wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
313 wv->button_type = BUTTON_TYPE_NONE;
314 /* This prevents lwlib from assuming this
315 menu item is really supposed to be empty. */
316 /* The intptr_t cast avoids a warning.
317 This value just has to be different from small integers. */
318 wv->call_data = (void *) (intptr_t) (-1);
319
320 if (prev_wv)
321 prev_wv->next = wv;
322 else
323 first_wv->contents = wv;
324 prev_wv = wv;
325 }
326
327 /* Forget what we thought we knew about what is in the
328 detailed contents of the menu bar menus.
329 Changing the top level always destroys the contents. */
330 f->menu_bar_items_used = 0;
331 }
332
333 block_input();
334
335 xg_crazy_callback_abort = true;
336 if (menubar_widget)
337 {
338 /* The fourth arg is DEEP_P, which says to consider the entire
339 menu trees we supply, rather than just the menu bar item names. */
340 xg_modify_menubar_widgets (menubar_widget,
341 f,
342 first_wv,
343 deep_p,
344 G_CALLBACK (menubar_selection_callback),
345 G_CALLBACK (popup_deactivate_callback),
346 G_CALLBACK (menu_highlight_callback));
347 }
348 else
349 {
350 menubar_widget
351 = xg_create_widget ("menubar", "menubar", f, first_wv,
352 G_CALLBACK (menubar_selection_callback),
353 G_CALLBACK (popup_deactivate_callback),
354 G_CALLBACK (menu_highlight_callback));
355
356 f->output_data.pgtk->menubar_widget = menubar_widget;
357 }
358
359 free_menubar_widget_value_tree (first_wv);
360 xg_update_frame_menubar (f);
361
362 xg_crazy_callback_abort = false;
363
364 unblock_input ();
365}
366
367
368
369/* Called from Fx_create_frame to create the initial menubar of a frame
370 before it is mapped, so that the window is mapped with the menubar already
371 there instead of us tacking it on later and thrashing the window after it
372 is visible. */
373
374void
375initialize_frame_menubar (struct frame *f)
376{
377 /* This function is called before the first chance to redisplay
378 the frame. It has to be, so the frame will have the right size. */
379 fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
380 set_frame_menubar (f, true, true);
381}
382
383
384void pgtk_activate_menubar (struct frame *f)
385{
386 set_frame_menubar(f, false, false);
387
388 /* f->output_data.pgtk->menubar_active = 1; */
389}
390
391
392Lisp_Object
393pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
394 Lisp_Object title, const char **error_name)
395{
396 Lisp_Object tem;
397
398 block_input ();
399
400
401 unblock_input ();
402
403 // not implemented.
404 return Qnil;
405}
406
407DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
408 doc: /* Start key navigation of the menu bar in FRAME.
409This initially opens the first menu bar item and you can then navigate with the
410arrow keys, select a menu entry with the return key or cancel with the
411escape key. If FRAME has no menu bar this function does nothing.
412
413If FRAME is nil or not given, use the selected frame. */)
414 (Lisp_Object frame)
415{
416 GtkWidget *menubar;
417 struct frame *f;
418
419 block_input ();
420 f = decode_window_system_frame (frame);
421
422 if (FRAME_EXTERNAL_MENU_BAR (f))
423 set_frame_menubar (f, false, true);
424
425 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
426 if (menubar)
427 {
428 /* Activate the first menu. */
429 GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
430
431 if (children)
432 {
433 g_signal_emit_by_name (children->data, "activate_item");
434 g_list_free (children);
435 }
436 }
437 unblock_input ();
438
439 return Qnil;
440}
441
442
443static const char * button_names [] = {
444 "button1", "button2", "button3", "button4", "button5",
445 "button6", "button7", "button8", "button9", "button10" };
446
447extern Lisp_Object
448pgtk_dialog_show (struct frame *f, Lisp_Object title,
449 Lisp_Object header, char **error)
450{
451 return Qnil;
452}
453
454/* The following is used by delayed window autoselection. */
455
456DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
457 doc: /* SKIP: real doc in xmenu.c. */)
458 (void)
459{
460 struct frame *f;
461 f = SELECTED_FRAME ();
462 // return (f->output_data.pgtk->menubar_active > 0) ? Qt : Qnil;
463 return Qnil;
464}
465
466void
467syms_of_pgtkmenu (void)
468{
469 // current_popup_menu = NULL;
470 // PDUMPER_IGNORE (current_popup_menu);
471
472 DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
473 DEFSYM (Qunsupported__w32_dialog, "unsupported--w32-dialog");
474
475 defsubr (&Smenu_or_popup_active_p);
476}