]> code.delx.au - gnu-emacs/blob - src/gtkutil.c
Don't install keyboard hook when debugged on MS-Windows
[gnu-emacs] / src / gtkutil.c
1 /* Functions for creating and updating GTK widgets.
2
3 Copyright (C) 2003-2016 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21
22 #ifdef USE_GTK
23 #include <float.h>
24 #include <stdio.h>
25
26 #include <c-ctype.h>
27
28 #include "lisp.h"
29 #include "dispextern.h"
30 #include "frame.h"
31 #include "systime.h"
32 #include "xterm.h"
33 #include "blockinput.h"
34 #include "window.h"
35 #include "gtkutil.h"
36 #include "termhooks.h"
37 #include "keyboard.h"
38 #include "coding.h"
39
40 #include <gdk/gdkkeysyms.h>
41
42 #ifdef HAVE_XFT
43 #include <X11/Xft/Xft.h>
44 #endif
45
46 #ifdef HAVE_GTK3
47 #include <gtk/gtkx.h>
48 #include "emacsgtkfixed.h"
49 #endif
50
51 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
52 #define gtk_widget_set_has_window(w, b) \
53 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
54 #endif
55 #ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
56 #define gtk_dialog_get_action_area(w) ((w)->action_area)
57 #define gtk_dialog_get_content_area(w) ((w)->vbox)
58 #endif
59 #ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
60 #define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
61 #endif
62 #ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
63 #define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
64 #define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
65 #define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
66 #define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
67 #endif
68 #if GTK_CHECK_VERSION (2, 12, 0)
69 #define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
70 #else
71 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
72 #endif
73
74 #if ! GTK_CHECK_VERSION (2, 14, 0)
75 #define gtk_adjustment_configure(adj, xvalue, xlower, \
76 xupper, xstep_increment, \
77 xpage_increment, xpagesize) \
78 do { \
79 adj->lower = xlower; \
80 adj->upper = xupper; \
81 adj->page_size = xpagesize; \
82 gtk_adjustment_set_value (adj, xvalue); \
83 adj->page_increment = xpage_increment; \
84 adj->step_increment = xstep_increment; \
85 } while (0)
86 #endif /* < Gtk+ 2.14 */
87
88 #ifdef HAVE_FREETYPE
89 #if GTK_CHECK_VERSION (3, 2, 0)
90 #define USE_NEW_GTK_FONT_CHOOSER 1
91 #else
92 #define USE_NEW_GTK_FONT_CHOOSER 0
93 #define gtk_font_chooser_dialog_new(x, y) \
94 gtk_font_selection_dialog_new (x)
95 #undef GTK_FONT_CHOOSER
96 #define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
97 #define gtk_font_chooser_set_font(x, y) \
98 gtk_font_selection_dialog_set_font_name (x, y)
99 #endif
100 #endif /* HAVE_FREETYPE */
101
102 #if GTK_CHECK_VERSION (3, 10, 0)
103 #define XG_TEXT_CANCEL "Cancel"
104 #define XG_TEXT_OK "OK"
105 #define XG_TEXT_OPEN "Open"
106 #else
107 #define XG_TEXT_CANCEL GTK_STOCK_CANCEL
108 #define XG_TEXT_OK GTK_STOCK_OK
109 #define XG_TEXT_OPEN GTK_STOCK_OPEN
110 #endif
111
112 #ifndef HAVE_GTK3
113 #ifdef USE_GTK_TOOLTIP
114 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
115 #endif
116 #define gdk_window_get_geometry(w, a, b, c, d) \
117 gdk_window_get_geometry (w, a, b, c, d, 0)
118 #define gdk_x11_window_lookup_for_display(d, w) \
119 gdk_xid_table_lookup_for_display (d, w)
120 #define gtk_box_new(ori, spacing) \
121 ((ori) == GTK_ORIENTATION_HORIZONTAL \
122 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
123 #define gtk_scrollbar_new(ori, spacing) \
124 ((ori) == GTK_ORIENTATION_HORIZONTAL \
125 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
126 #ifndef GDK_KEY_g
127 #define GDK_KEY_g GDK_g
128 #endif
129 #endif /* HAVE_GTK3 */
130
131 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
132
133 static void update_theme_scrollbar_width (void);
134 static void update_theme_scrollbar_height (void);
135
136 #define TB_INFO_KEY "xg_frame_tb_info"
137 struct xg_frame_tb_info
138 {
139 Lisp_Object last_tool_bar;
140 Lisp_Object style;
141 int n_last_items;
142 int hmargin, vmargin;
143 GtkTextDirection dir;
144 };
145
146 \f
147 /***********************************************************************
148 Display handling functions
149 ***********************************************************************/
150
151 /* Keep track of the default display, or NULL if there is none. Emacs
152 may close all its displays. */
153
154 static GdkDisplay *gdpy_def;
155
156 /* When the GTK widget W is to be created on a display for F that
157 is not the default display, set the display for W.
158 W can be a GtkMenu or a GtkWindow widget. */
159
160 static void
161 xg_set_screen (GtkWidget *w, struct frame *f)
162 {
163 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
164 {
165 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
166 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
167
168 if (GTK_IS_MENU (w))
169 gtk_menu_set_screen (GTK_MENU (w), gscreen);
170 else
171 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
172 }
173 }
174
175
176 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
177 *DPY is set to NULL if the display can't be opened.
178
179 Returns non-zero if display could be opened, zero if display could not
180 be opened, and less than zero if the GTK version doesn't support
181 multiple displays. */
182
183 void
184 xg_display_open (char *display_name, Display **dpy)
185 {
186 GdkDisplay *gdpy;
187
188 unrequest_sigio (); // See comment in x_display_ok, xterm.c.
189 gdpy = gdk_display_open (display_name);
190 request_sigio ();
191 if (!gdpy_def && gdpy)
192 {
193 gdpy_def = gdpy;
194 gdk_display_manager_set_default_display (gdk_display_manager_get (),
195 gdpy);
196 }
197
198 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
199 }
200
201
202 /* Close display DPY. */
203
204 void
205 xg_display_close (Display *dpy)
206 {
207 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
208
209 /* If this is the default display, try to change it before closing.
210 If there is no other display to use, gdpy_def is set to NULL, and
211 the next call to xg_display_open resets the default display. */
212 if (gdk_display_get_default () == gdpy)
213 {
214 struct x_display_info *dpyinfo;
215 GdkDisplay *gdpy_new = NULL;
216
217 /* Find another display. */
218 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
219 if (dpyinfo->display != dpy)
220 {
221 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
222 gdk_display_manager_set_default_display (gdk_display_manager_get (),
223 gdpy_new);
224 break;
225 }
226 gdpy_def = gdpy_new;
227 }
228
229 #if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
230 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
231 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
232 can continue running, but there will be memory leaks. */
233 g_object_run_dispose (G_OBJECT (gdpy));
234 #else
235 /* This seems to be fixed in GTK 2.10. */
236 gdk_display_close (gdpy);
237 #endif
238 }
239
240 \f
241 /***********************************************************************
242 Utility functions
243 ***********************************************************************/
244
245 /* Create and return the cursor to be used for popup menus and
246 scroll bars on display DPY. */
247
248 GdkCursor *
249 xg_create_default_cursor (Display *dpy)
250 {
251 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
252 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
253 }
254
255 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
256
257 static GdkPixbuf *
258 xg_get_pixbuf_from_pix_and_mask (struct frame *f,
259 Pixmap pix,
260 Pixmap mask)
261 {
262 GdkPixbuf *icon_buf = 0;
263 int iunused;
264 Window wunused;
265 unsigned int width, height, depth, uunused;
266
267 if (FRAME_DISPLAY_INFO (f)->red_bits != 8)
268 return 0;
269 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
270 &width, &height, &uunused, &depth);
271 if (depth != 24)
272 return 0;
273 XImage *xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
274 ~0, XYPixmap);
275 if (xim)
276 {
277 XImage *xmm = (! mask ? 0
278 : XGetImage (FRAME_X_DISPLAY (f), mask, 0, 0,
279 width, height, ~0, XYPixmap));
280 icon_buf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
281 if (icon_buf)
282 {
283 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
284 int rowjunkwidth = gdk_pixbuf_get_rowstride (icon_buf) - width * 4;
285 for (int y = 0; y < height; y++, pixels += rowjunkwidth)
286 for (int x = 0; x < width; x++)
287 {
288 unsigned long rgb = XGetPixel (xim, x, y);
289 *pixels++ = (rgb >> 16) & 255;
290 *pixels++ = (rgb >> 8) & 255;
291 *pixels++ = rgb & 255;
292 *pixels++ = xmm && !XGetPixel (xmm, x, y) ? 0 : 255;
293 }
294 }
295
296 if (xmm)
297 XDestroyImage (xmm);
298 XDestroyImage (xim);
299 }
300
301 return icon_buf;
302 }
303
304 static Lisp_Object
305 file_for_image (Lisp_Object image)
306 {
307 Lisp_Object specified_file = Qnil;
308 Lisp_Object tail;
309
310 for (tail = XCDR (image);
311 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
312 tail = XCDR (XCDR (tail)))
313 if (EQ (XCAR (tail), QCfile))
314 specified_file = XCAR (XCDR (tail));
315
316 return specified_file;
317 }
318
319 /* For the image defined in IMG, make and return a GtkImage. For displays with
320 8 planes or less we must make a GdkPixbuf and apply the mask manually.
321 Otherwise the highlighting and dimming the tool bar code in GTK does
322 will look bad. For display with more than 8 planes we just use the
323 pixmap and mask directly. For monochrome displays, GTK doesn't seem
324 able to use external pixmaps, it looks bad whatever we do.
325 The image is defined on the display where frame F is.
326 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
327 If OLD_WIDGET is NULL, a new widget is constructed and returned.
328 If OLD_WIDGET is not NULL, that widget is modified. */
329
330 static GtkWidget *
331 xg_get_image_for_pixmap (struct frame *f,
332 struct image *img,
333 GtkWidget *widget,
334 GtkImage *old_widget)
335 {
336 GdkPixbuf *icon_buf;
337
338 /* If we have a file, let GTK do all the image handling.
339 This seems to be the only way to make insensitive and activated icons
340 look good in all cases. */
341 Lisp_Object specified_file = file_for_image (img->spec);
342 Lisp_Object file;
343
344 /* We already loaded the image once before calling this
345 function, so this only fails if the image file has been removed.
346 In that case, use the pixmap already loaded. */
347
348 if (STRINGP (specified_file)
349 && STRINGP (file = x_find_image_file (specified_file)))
350 {
351 char *encoded_file = SSDATA (ENCODE_FILE (file));
352 if (! old_widget)
353 old_widget = GTK_IMAGE (gtk_image_new_from_file (encoded_file));
354 else
355 gtk_image_set_from_file (old_widget, encoded_file);
356
357 return GTK_WIDGET (old_widget);
358 }
359
360 /* No file, do the image handling ourselves. This will look very bad
361 on a monochrome display, and sometimes bad on all displays with
362 certain themes. */
363
364 /* This is a workaround to make icons look good on pseudo color
365 displays. Apparently GTK expects the images to have an alpha
366 channel. If they don't, insensitive and activated icons will
367 look bad. This workaround does not work on monochrome displays,
368 and is strictly not needed on true color/static color displays (i.e.
369 16 bits and higher). But we do it anyway so we get a pixbuf that is
370 not associated with the img->pixmap. The img->pixmap may be removed
371 by clearing the image cache and then the tool bar redraw fails, since
372 Gtk+ assumes the pixmap is always there. */
373 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
374
375 if (icon_buf)
376 {
377 if (! old_widget)
378 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
379 else
380 gtk_image_set_from_pixbuf (old_widget, icon_buf);
381
382 g_object_unref (G_OBJECT (icon_buf));
383 }
384
385 return GTK_WIDGET (old_widget);
386 }
387
388
389 /* Set CURSOR on W and all widgets W contain. We must do like this
390 for scroll bars and menu because they create widgets internally,
391 and it is those widgets that are visible. */
392
393 static void
394 xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
395 {
396 GdkWindow *window = gtk_widget_get_window (w);
397 GList *children = gdk_window_peek_children (window);
398
399 gdk_window_set_cursor (window, cursor);
400
401 /* The scroll bar widget has more than one GDK window (had to look at
402 the source to figure this out), and there is no way to set cursor
403 on widgets in GTK. So we must set the cursor for all GDK windows.
404 Ditto for menus. */
405
406 for ( ; children; children = g_list_next (children))
407 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
408 }
409
410 /* Insert NODE into linked LIST. */
411
412 static void
413 xg_list_insert (xg_list_node *list, xg_list_node *node)
414 {
415 xg_list_node *list_start = list->next;
416
417 if (list_start) list_start->prev = node;
418 node->next = list_start;
419 node->prev = 0;
420 list->next = node;
421 }
422
423 /* Remove NODE from linked LIST. */
424
425 static void
426 xg_list_remove (xg_list_node *list, xg_list_node *node)
427 {
428 xg_list_node *list_start = list->next;
429 if (node == list_start)
430 {
431 list->next = node->next;
432 if (list->next) list->next->prev = 0;
433 }
434 else
435 {
436 node->prev->next = node->next;
437 if (node->next) node->next->prev = node->prev;
438 }
439 }
440
441 /* Allocate and return a utf8 version of STR. If STR is already
442 utf8 or NULL, just return a copy of STR.
443 A new string is allocated and the caller must free the result
444 with g_free. */
445
446 static char *
447 get_utf8_string (const char *str)
448 {
449 char *utf8_str;
450
451 if (!str) return NULL;
452
453 /* If not UTF-8, try current locale. */
454 if (!g_utf8_validate (str, -1, NULL))
455 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
456 else
457 return g_strdup (str);
458
459 if (!utf8_str)
460 {
461 /* Probably some control characters in str. Escape them. */
462 ptrdiff_t len;
463 ptrdiff_t nr_bad = 0;
464 gsize bytes_read;
465 gsize bytes_written;
466 unsigned char *p = (unsigned char *)str;
467 char *cp, *up;
468 GError *err = NULL;
469
470 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
471 &bytes_written, &err))
472 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
473 {
474 ++nr_bad;
475 p += bytes_written+1;
476 g_error_free (err);
477 err = NULL;
478 }
479
480 if (err)
481 {
482 g_error_free (err);
483 err = NULL;
484 }
485 if (cp) g_free (cp);
486
487 len = strlen (str);
488 ptrdiff_t alloc;
489 if (INT_MULTIPLY_WRAPV (nr_bad, 4, &alloc)
490 || INT_ADD_WRAPV (len + 1, alloc, &alloc)
491 || SIZE_MAX < alloc)
492 memory_full (SIZE_MAX);
493 up = utf8_str = xmalloc (alloc);
494 p = (unsigned char *)str;
495
496 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
497 &bytes_written, &err))
498 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
499 {
500 memcpy (up, p, bytes_written);
501 up += bytes_written;
502 up += sprintf (up, "\\%03o", p[bytes_written]);
503 p += bytes_written + 1;
504 g_error_free (err);
505 err = NULL;
506 }
507
508 if (cp)
509 {
510 strcpy (up, cp);
511 g_free (cp);
512 }
513 if (err)
514 {
515 g_error_free (err);
516 err = NULL;
517 }
518 }
519 return utf8_str;
520 }
521
522 /* Check for special colors used in face spec for region face.
523 The colors are fetched from the Gtk+ theme.
524 Return true if color was found, false if not. */
525
526 bool
527 xg_check_special_colors (struct frame *f,
528 const char *color_name,
529 XColor *color)
530 {
531 bool success_p = 0;
532 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
533 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
534
535 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
536 return success_p;
537
538 block_input ();
539 {
540 #ifdef HAVE_GTK3
541 GtkStyleContext *gsty
542 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
543 GdkRGBA col;
544 char buf[sizeof "rgb://rrrr/gggg/bbbb"];
545 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
546 if (get_fg)
547 gtk_style_context_get_color (gsty, state, &col);
548 else
549 gtk_style_context_get_background_color (gsty, state, &col);
550
551 sprintf (buf, "rgb:%04x/%04x/%04x",
552 (unsigned) (col.red * 65535),
553 (unsigned) (col.green * 65535),
554 (unsigned) (col.blue * 65535));
555 success_p = x_parse_color (f, buf, color) != 0;
556 #else
557 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
558 GdkColor *grgb = get_bg
559 ? &gsty->bg[GTK_STATE_SELECTED]
560 : &gsty->fg[GTK_STATE_SELECTED];
561
562 color->red = grgb->red;
563 color->green = grgb->green;
564 color->blue = grgb->blue;
565 color->pixel = grgb->pixel;
566 success_p = 1;
567 #endif
568
569 }
570 unblock_input ();
571 return success_p;
572 }
573
574
575 \f
576 /***********************************************************************
577 Tooltips
578 ***********************************************************************/
579 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
580 We use that to pop down the tooltip. This happens if Gtk+ for some
581 reason wants to change or hide the tooltip. */
582
583 #ifdef USE_GTK_TOOLTIP
584
585 static void
586 hierarchy_ch_cb (GtkWidget *widget,
587 GtkWidget *previous_toplevel,
588 gpointer user_data)
589 {
590 struct frame *f = user_data;
591 struct x_output *x = f->output_data.x;
592 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
593
594 if (! top || ! GTK_IS_WINDOW (top))
595 gtk_widget_hide (previous_toplevel);
596 }
597
598 /* Callback called when Gtk+ thinks a tooltip should be displayed.
599 We use it to get the tooltip window and the tooltip widget so
600 we can manipulate the ourselves.
601
602 Return FALSE ensures that the tooltip is not shown. */
603
604 static gboolean
605 qttip_cb (GtkWidget *widget,
606 gint xpos,
607 gint ypos,
608 gboolean keyboard_mode,
609 GtkTooltip *tooltip,
610 gpointer user_data)
611 {
612 struct frame *f = user_data;
613 struct x_output *x = f->output_data.x;
614 if (x->ttip_widget == NULL)
615 {
616 GtkWidget *p;
617 GList *list, *iter;
618
619 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
620 x->ttip_widget = tooltip;
621 g_object_ref (G_OBJECT (tooltip));
622 x->ttip_lbl = gtk_label_new ("");
623 g_object_ref (G_OBJECT (x->ttip_lbl));
624 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
625 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
626
627 /* Change stupid Gtk+ default line wrapping. */
628 p = gtk_widget_get_parent (x->ttip_lbl);
629 list = gtk_container_get_children (GTK_CONTAINER (p));
630 for (iter = list; iter; iter = g_list_next (iter))
631 {
632 GtkWidget *w = GTK_WIDGET (iter->data);
633 if (GTK_IS_LABEL (w))
634 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
635 }
636 g_list_free (list);
637
638 /* ATK needs an empty title for some reason. */
639 gtk_window_set_title (x->ttip_window, "");
640 /* Realize so we can safely get screen later on. */
641 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
642 gtk_widget_realize (x->ttip_lbl);
643
644 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
645 G_CALLBACK (hierarchy_ch_cb), f);
646 }
647 return FALSE;
648 }
649
650 #endif /* USE_GTK_TOOLTIP */
651
652 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
653 Return true if a system tooltip is available. */
654
655 bool
656 xg_prepare_tooltip (struct frame *f,
657 Lisp_Object string,
658 int *width,
659 int *height)
660 {
661 #ifndef USE_GTK_TOOLTIP
662 return 0;
663 #else
664 struct x_output *x = f->output_data.x;
665 GtkWidget *widget;
666 GdkWindow *gwin;
667 GdkScreen *screen;
668 GtkSettings *settings;
669 gboolean tt_enabled = TRUE;
670 GtkRequisition req;
671 Lisp_Object encoded_string;
672
673 if (!x->ttip_lbl) return 0;
674
675 block_input ();
676 encoded_string = ENCODE_UTF_8 (string);
677 widget = GTK_WIDGET (x->ttip_lbl);
678 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
679 screen = gdk_window_get_screen (gwin);
680 settings = gtk_settings_get_for_screen (screen);
681 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
682 if (tt_enabled)
683 {
684 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
685 /* Record that we disabled it so it can be enabled again. */
686 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
687 (gpointer)f);
688 }
689
690 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
691 g_object_set_data (G_OBJECT
692 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
693 "gdk-display-current-tooltip", NULL);
694
695 /* Put our dummy widget in so we can get callbacks for unrealize and
696 hierarchy-changed. */
697 gtk_tooltip_set_custom (x->ttip_widget, widget);
698 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
699 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
700 if (width) *width = req.width;
701 if (height) *height = req.height;
702
703 unblock_input ();
704
705 return 1;
706 #endif /* USE_GTK_TOOLTIP */
707 }
708
709 /* Show the tooltip at ROOT_X and ROOT_Y.
710 xg_prepare_tooltip must have been called before this function. */
711
712 void
713 xg_show_tooltip (struct frame *f, int root_x, int root_y)
714 {
715 #ifdef USE_GTK_TOOLTIP
716 struct x_output *x = f->output_data.x;
717 if (x->ttip_window)
718 {
719 block_input ();
720 gtk_window_move (x->ttip_window, root_x, root_y);
721 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
722 unblock_input ();
723 }
724 #endif
725 }
726
727 /* Hide tooltip if shown. Do nothing if not shown.
728 Return true if tip was hidden, false if not (i.e. not using
729 system tooltips). */
730
731 bool
732 xg_hide_tooltip (struct frame *f)
733 {
734 #ifdef USE_GTK_TOOLTIP
735 struct x_output *x = FRAME_X_OUTPUT (f);
736
737 if (x->ttip_window)
738 {
739 GtkWindow *win = f->output_data.x->ttip_window;
740
741 block_input ();
742 gtk_widget_hide (GTK_WIDGET (win));
743
744 /* Cancel call to xg_hide_tip. */
745 if (x->ttip_timeout != 0)
746 {
747 g_source_remove (x->ttip_timeout);
748 x->ttip_timeout = 0;
749 }
750
751 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
752 {
753 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
754 GdkScreen *screen = gdk_window_get_screen (gwin);
755 GtkSettings *settings = gtk_settings_get_for_screen (screen);
756 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
757 }
758 unblock_input ();
759 return 1;
760 }
761 #endif
762 return 0;
763 }
764
765 /* One-shot timeout handler attached to GTK event loop in Fx_show_tip. */
766
767 gboolean
768 xg_hide_tip (gpointer data)
769 {
770 #ifdef USE_GTK_TOOLTIP
771 xg_hide_tooltip ((struct frame *) data);
772 #endif
773 return FALSE;
774 }
775
776 \f
777 /***********************************************************************
778 General functions for creating widgets, resizing, events, e.t.c.
779 ***********************************************************************/
780
781 static void
782 my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
783 const gchar *msg, gpointer user_data)
784 {
785 if (!strstr (msg, "visible children"))
786 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
787 }
788
789 /* Make a geometry string and pass that to GTK. It seems this is the
790 only way to get geometry position right if the user explicitly
791 asked for a position when starting Emacs.
792 F is the frame we shall set geometry for. */
793
794 static void
795 xg_set_geometry (struct frame *f)
796 {
797 if (f->size_hint_flags & (USPosition | PPosition))
798 {
799 int left = f->left_pos;
800 int xneg = f->size_hint_flags & XNegative;
801 int top = f->top_pos;
802 int yneg = f->size_hint_flags & YNegative;
803 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
804 guint id;
805
806 if (xneg)
807 left = -left;
808 if (yneg)
809 top = -top;
810
811 sprintf (geom_str, "=%dx%d%c%d%c%d",
812 FRAME_PIXEL_WIDTH (f),
813 FRAME_PIXEL_HEIGHT (f),
814 (xneg ? '-' : '+'), left,
815 (yneg ? '-' : '+'), top);
816
817 /* Silence warning about visible children. */
818 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
819 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
820
821 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
822 geom_str))
823 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
824
825 g_log_remove_handler ("Gtk", id);
826 }
827 }
828
829 /* Clear under internal border if any. As we use a mix of Gtk+ and X calls
830 and use a GtkFixed widget, this doesn't happen automatically. */
831
832 void
833 xg_clear_under_internal_border (struct frame *f)
834 {
835 if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
836 {
837 #ifndef USE_CAIRO
838 GtkWidget *wfixed = f->output_data.x->edit_widget;
839
840 gtk_widget_queue_draw (wfixed);
841 gdk_window_process_all_updates ();
842 #endif
843 x_clear_area (f, 0, 0,
844 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
845
846 x_clear_area (f, 0, 0,
847 FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
848
849 x_clear_area (f, 0,
850 FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
851 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
852
853 x_clear_area (f,
854 FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
855 0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
856 }
857 }
858
859 static int
860 xg_get_gdk_scale (void)
861 {
862 const char *sscale = getenv ("GDK_SCALE");
863
864 if (sscale)
865 {
866 long scale = atol (sscale);
867 if (0 < scale)
868 return min (scale, INT_MAX);
869 }
870
871 return 1;
872 }
873
874 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
875 and a Gtk+ menu bar, we get resize events for the edit part of the
876 frame only. We let Gtk+ deal with the Gtk+ parts.
877 F is the frame to resize.
878 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
879
880 void
881 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
882 {
883 int width, height;
884
885 if (pixelwidth == -1 && pixelheight == -1)
886 {
887 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
888 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
889 0, 0, &pixelwidth, &pixelheight);
890 else
891 return;
892 }
893
894 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
895 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
896
897 frame_size_history_add
898 (f, Qxg_frame_resized, width, height, Qnil);
899
900 if (width != FRAME_TEXT_WIDTH (f)
901 || height != FRAME_TEXT_HEIGHT (f)
902 || pixelwidth != FRAME_PIXEL_WIDTH (f)
903 || pixelheight != FRAME_PIXEL_HEIGHT (f))
904 {
905 xg_clear_under_internal_border (f);
906 change_frame_size (f, width, height, 0, 1, 0, 1);
907 SET_FRAME_GARBAGED (f);
908 cancel_mouse_face (f);
909 }
910 }
911
912 /* Resize the outer window of frame F after changing the height.
913 COLUMNS/ROWS is the size the edit area shall have after the resize. */
914
915 void
916 xg_frame_set_char_size (struct frame *f, int width, int height)
917 {
918 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
919 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
920 Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
921 gint gwidth, gheight;
922 int totalheight
923 = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
924 int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
925
926 if (FRAME_PIXEL_HEIGHT (f) == 0)
927 return;
928
929 gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
930 &gwidth, &gheight);
931
932 /* Do this before resize, as we don't know yet if we will be resized. */
933 xg_clear_under_internal_border (f);
934
935 if (FRAME_VISIBLE_P (f))
936 {
937 int scale = xg_get_gdk_scale ();
938 totalheight /= scale;
939 totalwidth /= scale;
940 }
941
942 x_wm_set_size_hint (f, 0, 0);
943
944 /* Resize the top level widget so rows and columns remain constant.
945
946 When the frame is fullheight and we only want to change the width
947 or it is fullwidth and we only want to change the height we should
948 be able to preserve the fullscreen property. However, due to the
949 fact that we have to send a resize request anyway, the window
950 manager will abolish it. At least the respective size should
951 remain unchanged but giving the frame back its normal size will
952 be broken ... */
953 if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
954 {
955 frame_size_history_add
956 (f, Qxg_frame_set_char_size_1, width, height,
957 list2 (make_number (gheight), make_number (totalheight)));
958
959 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
960 gwidth, totalheight);
961 }
962 else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
963 {
964 frame_size_history_add
965 (f, Qxg_frame_set_char_size_2, width, height,
966 list2 (make_number (gwidth), make_number (totalwidth)));
967
968 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
969 totalwidth, gheight);
970 }
971 else
972 {
973 frame_size_history_add
974 (f, Qxg_frame_set_char_size_3, width, height,
975 list2 (make_number (totalwidth), make_number (totalheight)));
976
977 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
978 totalwidth, totalheight);
979 fullscreen = Qnil;
980 }
981
982 SET_FRAME_GARBAGED (f);
983 cancel_mouse_face (f);
984
985 /* We can not call change_frame_size for a mapped frame,
986 we can not set pixel width/height either. The window manager may
987 override our resize request, XMonad does this all the time.
988 The best we can do is try to sync, so lisp code sees the updated
989 size as fast as possible.
990 For unmapped windows, we can set rows/cols. When
991 the frame is mapped again we will (hopefully) get the correct size. */
992 if (FRAME_VISIBLE_P (f))
993 {
994 /* Must call this to flush out events */
995 (void)gtk_events_pending ();
996 gdk_flush ();
997 x_wait_for_event (f, ConfigureNotify);
998
999 if (!NILP (fullscreen))
1000 /* Try to restore fullscreen state. */
1001 {
1002 store_frame_param (f, Qfullscreen, fullscreen);
1003 x_set_fullscreen (f, fullscreen, fullscreen);
1004 }
1005 }
1006 else
1007 adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
1008
1009 }
1010
1011 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
1012 The policy is to keep the number of editable lines. */
1013
1014 #if 0
1015 static void
1016 xg_height_or_width_changed (struct frame *f)
1017 {
1018 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1019 FRAME_TOTAL_PIXEL_WIDTH (f),
1020 FRAME_TOTAL_PIXEL_HEIGHT (f));
1021 f->output_data.x->hint_flags = 0;
1022 x_wm_set_size_hint (f, 0, 0);
1023 }
1024 #endif
1025
1026 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1027 Must be done like this, because GtkWidget:s can have "hidden"
1028 X Window that aren't accessible.
1029
1030 Return 0 if no widget match WDESC. */
1031
1032 GtkWidget *
1033 xg_win_to_widget (Display *dpy, Window wdesc)
1034 {
1035 gpointer gdkwin;
1036 GtkWidget *gwdesc = 0;
1037
1038 block_input ();
1039
1040 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1041 wdesc);
1042 if (gdkwin)
1043 {
1044 GdkEvent event;
1045 event.any.window = gdkwin;
1046 event.any.type = GDK_NOTHING;
1047 gwdesc = gtk_get_event_widget (&event);
1048 }
1049
1050 unblock_input ();
1051 return gwdesc;
1052 }
1053
1054 /* Set the background of widget W to PIXEL. */
1055
1056 static void
1057 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
1058 {
1059 #ifdef HAVE_GTK3
1060 GdkRGBA bg;
1061 XColor xbg;
1062 xbg.pixel = pixel;
1063 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1064 {
1065 bg.red = (double)xbg.red/65535.0;
1066 bg.green = (double)xbg.green/65535.0;
1067 bg.blue = (double)xbg.blue/65535.0;
1068 bg.alpha = 1.0;
1069 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1070 }
1071 #else
1072 GdkColor bg;
1073 GdkColormap *map = gtk_widget_get_colormap (w);
1074 gdk_colormap_query_color (map, pixel, &bg);
1075 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1076 #endif
1077 }
1078
1079 /* Callback called when the gtk theme changes.
1080 We notify lisp code so it can fix faces used for region for example. */
1081
1082 static void
1083 style_changed_cb (GObject *go,
1084 GParamSpec *spec,
1085 gpointer user_data)
1086 {
1087 struct input_event event;
1088 GdkDisplay *gdpy = user_data;
1089 const char *display_name = gdk_display_get_name (gdpy);
1090 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1091
1092 EVENT_INIT (event);
1093 event.kind = CONFIG_CHANGED_EVENT;
1094 event.frame_or_window = build_string (display_name);
1095 /* Theme doesn't change often, so intern is called seldom. */
1096 event.arg = intern ("theme-name");
1097 kbd_buffer_store_event (&event);
1098
1099 update_theme_scrollbar_width ();
1100 update_theme_scrollbar_height ();
1101
1102 /* If scroll bar width changed, we need set the new size on all frames
1103 on this display. */
1104 if (dpy)
1105 {
1106 Lisp_Object rest, frame;
1107 FOR_EACH_FRAME (rest, frame)
1108 {
1109 struct frame *f = XFRAME (frame);
1110 if (FRAME_LIVE_P (f)
1111 && FRAME_X_P (f)
1112 && FRAME_X_DISPLAY (f) == dpy)
1113 {
1114 x_set_scroll_bar_default_width (f);
1115 x_set_scroll_bar_default_height (f);
1116 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
1117 }
1118 }
1119 }
1120 }
1121
1122 /* Called when a delete-event occurs on WIDGET. */
1123
1124 static gboolean
1125 delete_cb (GtkWidget *widget,
1126 GdkEvent *event,
1127 gpointer user_data)
1128 {
1129 return TRUE;
1130 }
1131
1132 /* Create and set up the GTK widgets for frame F.
1133 Return true if creation succeeded. */
1134
1135 bool
1136 xg_create_frame_widgets (struct frame *f)
1137 {
1138 GtkWidget *wtop;
1139 GtkWidget *wvbox, *whbox;
1140 GtkWidget *wfixed;
1141 #ifndef HAVE_GTK3
1142 GtkRcStyle *style;
1143 #endif
1144 char *title = 0;
1145
1146 block_input ();
1147
1148 if (FRAME_X_EMBEDDED_P (f))
1149 {
1150 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1151 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1152 }
1153 else
1154 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1155
1156 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1157 has backported it to Gtk+ 2.0 and they add the resize grip for
1158 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1159 forever, so disable the grip. */
1160 #if (! GTK_CHECK_VERSION (3, 0, 0) \
1161 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1162 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1163 #endif
1164
1165 xg_set_screen (wtop, f);
1166
1167 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1168 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1169 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1170 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1171
1172 #ifdef HAVE_GTK3
1173 wfixed = emacs_fixed_new (f);
1174 #else
1175 wfixed = gtk_fixed_new ();
1176 #endif
1177
1178 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1179 {
1180 if (wtop) gtk_widget_destroy (wtop);
1181 if (wvbox) gtk_widget_destroy (wvbox);
1182 if (whbox) gtk_widget_destroy (whbox);
1183 if (wfixed) gtk_widget_destroy (wfixed);
1184
1185 unblock_input ();
1186 return 0;
1187 }
1188
1189 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1190 gtk_widget_set_name (wtop, EMACS_CLASS);
1191 gtk_widget_set_name (wvbox, "pane");
1192 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1193
1194 /* If this frame has a title or name, set it in the title bar. */
1195 if (! NILP (f->title))
1196 title = SSDATA (ENCODE_UTF_8 (f->title));
1197 else if (! NILP (f->name))
1198 title = SSDATA (ENCODE_UTF_8 (f->name));
1199
1200 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
1201
1202 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1203 FRAME_GTK_WIDGET (f) = wfixed;
1204 f->output_data.x->vbox_widget = wvbox;
1205 f->output_data.x->hbox_widget = whbox;
1206
1207 gtk_widget_set_has_window (wfixed, TRUE);
1208
1209 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1210 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1211 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1212
1213 if (FRAME_EXTERNAL_TOOL_BAR (f))
1214 update_frame_tool_bar (f);
1215
1216 /* We don't want this widget double buffered, because we draw on it
1217 with regular X drawing primitives, so from a GTK/GDK point of
1218 view, the widget is totally blank. When an expose comes, this
1219 will make the widget blank, and then Emacs redraws it. This flickers
1220 a lot, so we turn off double buffering. */
1221 gtk_widget_set_double_buffered (wfixed, FALSE);
1222
1223 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1224 SSDATA (Vx_resource_name),
1225 SSDATA (Vx_resource_class));
1226
1227 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1228 GTK is to destroy the widget. We want Emacs to do that instead. */
1229 g_signal_connect (G_OBJECT (wtop), "delete-event",
1230 G_CALLBACK (delete_cb), f);
1231
1232 /* Convert our geometry parameters into a geometry string
1233 and specify it.
1234 GTK will itself handle calculating the real position this way. */
1235 xg_set_geometry (f);
1236 f->win_gravity
1237 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1238
1239 gtk_widget_add_events (wfixed,
1240 GDK_POINTER_MOTION_MASK
1241 | GDK_EXPOSURE_MASK
1242 | GDK_BUTTON_PRESS_MASK
1243 | GDK_BUTTON_RELEASE_MASK
1244 | GDK_KEY_PRESS_MASK
1245 | GDK_ENTER_NOTIFY_MASK
1246 | GDK_LEAVE_NOTIFY_MASK
1247 | GDK_FOCUS_CHANGE_MASK
1248 | GDK_STRUCTURE_MASK
1249 | GDK_VISIBILITY_NOTIFY_MASK);
1250
1251 /* Must realize the windows so the X window gets created. It is used
1252 by callers of this function. */
1253 gtk_widget_realize (wfixed);
1254 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1255
1256 /* Since GTK clears its window by filling with the background color,
1257 we must keep X and GTK background in sync. */
1258 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1259
1260 #ifndef HAVE_GTK3
1261 /* Also, do not let any background pixmap to be set, this looks very
1262 bad as Emacs overwrites the background pixmap with its own idea
1263 of background color. */
1264 style = gtk_widget_get_modifier_style (wfixed);
1265
1266 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1267 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1268 gtk_widget_modify_style (wfixed, style);
1269 #else
1270 gtk_widget_set_can_focus (wfixed, TRUE);
1271 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1272 #endif
1273
1274 #ifdef USE_GTK_TOOLTIP
1275 /* Steal a tool tip window we can move ourselves. */
1276 f->output_data.x->ttip_widget = 0;
1277 f->output_data.x->ttip_lbl = 0;
1278 f->output_data.x->ttip_window = 0;
1279 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1280 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1281 #endif
1282
1283 {
1284 GdkScreen *screen = gtk_widget_get_screen (wtop);
1285 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1286 /* Only connect this signal once per screen. */
1287 if (! g_signal_handler_find (G_OBJECT (gs),
1288 G_SIGNAL_MATCH_FUNC,
1289 0, 0, 0,
1290 (gpointer) G_CALLBACK (style_changed_cb),
1291 0))
1292 {
1293 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1294 G_CALLBACK (style_changed_cb),
1295 gdk_screen_get_display (screen));
1296 }
1297 }
1298
1299 unblock_input ();
1300
1301 return 1;
1302 }
1303
1304 void
1305 xg_free_frame_widgets (struct frame *f)
1306 {
1307 if (FRAME_GTK_OUTER_WIDGET (f))
1308 {
1309 #ifdef USE_GTK_TOOLTIP
1310 struct x_output *x = f->output_data.x;
1311 #endif
1312 struct xg_frame_tb_info *tbinfo
1313 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1314 TB_INFO_KEY);
1315 if (tbinfo)
1316 xfree (tbinfo);
1317
1318 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1319 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1320 FRAME_GTK_OUTER_WIDGET (f) = 0;
1321 #ifdef USE_GTK_TOOLTIP
1322 if (x->ttip_lbl)
1323 gtk_widget_destroy (x->ttip_lbl);
1324 if (x->ttip_widget)
1325 g_object_unref (G_OBJECT (x->ttip_widget));
1326 #endif
1327 }
1328 }
1329
1330 /* Set the normal size hints for the window manager, for frame F.
1331 FLAGS is the flags word to use--or 0 meaning preserve the flags
1332 that the window now has.
1333 If USER_POSITION, set the User Position
1334 flag (this is useful when FLAGS is 0). */
1335
1336 void
1337 x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
1338 {
1339 /* Must use GTK routines here, otherwise GTK resets the size hints
1340 to its own defaults. */
1341 GdkGeometry size_hints;
1342 gint hint_flags = 0;
1343 int base_width, base_height;
1344 int min_rows = 0, min_cols = 0;
1345 int win_gravity = f->win_gravity;
1346 Lisp_Object fs_state, frame;
1347 int scale = xg_get_gdk_scale ();
1348
1349 /* Don't set size hints during initialization; that apparently leads
1350 to a race condition. See the thread at
1351 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1352 if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
1353 return;
1354
1355 XSETFRAME (frame, f);
1356 fs_state = Fframe_parameter (frame, Qfullscreen);
1357 if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
1358 (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
1359 x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
1360 {
1361 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1362 Gtk3 don't get along and the frame shrinks (!).
1363 */
1364 return;
1365 }
1366
1367 if (flags)
1368 {
1369 memset (&size_hints, 0, sizeof (size_hints));
1370 f->output_data.x->size_hints = size_hints;
1371 f->output_data.x->hint_flags = hint_flags;
1372 }
1373 else
1374 flags = f->size_hint_flags;
1375
1376 size_hints = f->output_data.x->size_hints;
1377 hint_flags = f->output_data.x->hint_flags;
1378
1379 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1380 size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
1381 size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
1382
1383 hint_flags |= GDK_HINT_BASE_SIZE;
1384 /* Use one row/col here so base_height/width does not become zero.
1385 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
1386 Obviously this makes the row/col value displayed off by 1. */
1387 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1388 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1389 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1390
1391 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1392 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1393
1394 size_hints.base_width = base_width;
1395 size_hints.base_height = base_height;
1396 size_hints.min_width = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
1397 size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
1398
1399 /* These currently have a one to one mapping with the X values, but I
1400 don't think we should rely on that. */
1401 hint_flags |= GDK_HINT_WIN_GRAVITY;
1402 size_hints.win_gravity = 0;
1403 if (win_gravity == NorthWestGravity)
1404 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1405 else if (win_gravity == NorthGravity)
1406 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1407 else if (win_gravity == NorthEastGravity)
1408 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1409 else if (win_gravity == WestGravity)
1410 size_hints.win_gravity = GDK_GRAVITY_WEST;
1411 else if (win_gravity == CenterGravity)
1412 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1413 else if (win_gravity == EastGravity)
1414 size_hints.win_gravity = GDK_GRAVITY_EAST;
1415 else if (win_gravity == SouthWestGravity)
1416 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1417 else if (win_gravity == SouthGravity)
1418 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1419 else if (win_gravity == SouthEastGravity)
1420 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1421 else if (win_gravity == StaticGravity)
1422 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1423
1424 if (user_position)
1425 {
1426 hint_flags &= ~GDK_HINT_POS;
1427 hint_flags |= GDK_HINT_USER_POS;
1428 }
1429
1430 size_hints.base_width /= scale;
1431 size_hints.base_height /= scale;
1432 size_hints.width_inc /= scale;
1433 size_hints.height_inc /= scale;
1434
1435 if (hint_flags != f->output_data.x->hint_flags
1436 || memcmp (&size_hints,
1437 &f->output_data.x->size_hints,
1438 sizeof (size_hints)) != 0)
1439 {
1440 block_input ();
1441 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1442 NULL, &size_hints, hint_flags);
1443 f->output_data.x->size_hints = size_hints;
1444 f->output_data.x->hint_flags = hint_flags;
1445 unblock_input ();
1446 }
1447 }
1448
1449 /* Change background color of a frame.
1450 Since GTK uses the background color to clear the window, we must
1451 keep the GTK and X colors in sync.
1452 F is the frame to change,
1453 BG is the pixel value to change to. */
1454
1455 void
1456 xg_set_background_color (struct frame *f, unsigned long bg)
1457 {
1458 if (FRAME_GTK_WIDGET (f))
1459 {
1460 block_input ();
1461 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1462 unblock_input ();
1463 }
1464 }
1465
1466
1467 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1468 functions so GTK does not overwrite the icon. */
1469
1470 void
1471 xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1472 {
1473 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1474 icon_pixmap,
1475 icon_mask);
1476 if (gp)
1477 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1478 }
1479
1480
1481 \f
1482 /***********************************************************************
1483 Dialog functions
1484 ***********************************************************************/
1485 /* Return the dialog title to use for a dialog of type KEY.
1486 This is the encoding used by lwlib. We use the same for GTK. */
1487
1488 static const char *
1489 get_dialog_title (char key)
1490 {
1491 const char *title = "";
1492
1493 switch (key) {
1494 case 'E': case 'e':
1495 title = "Error";
1496 break;
1497
1498 case 'I': case 'i':
1499 title = "Information";
1500 break;
1501
1502 case 'L': case 'l':
1503 title = "Prompt";
1504 break;
1505
1506 case 'P': case 'p':
1507 title = "Prompt";
1508 break;
1509
1510 case 'Q': case 'q':
1511 title = "Question";
1512 break;
1513 }
1514
1515 return title;
1516 }
1517
1518 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1519 the dialog, but return TRUE so the event does not propagate further
1520 in GTK. This prevents GTK from destroying the dialog widget automatically
1521 and we can always destroy the widget manually, regardless of how
1522 it was popped down (button press or WM_DELETE_WINDOW).
1523 W is the dialog widget.
1524 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1525 user_data is NULL (not used).
1526
1527 Returns TRUE to end propagation of event. */
1528
1529 static gboolean
1530 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1531 {
1532 gtk_widget_unmap (w);
1533 return TRUE;
1534 }
1535
1536 /* Create a popup dialog window. See also xg_create_widget below.
1537 WV is a widget_value describing the dialog.
1538 SELECT_CB is the callback to use when a button has been pressed.
1539 DEACTIVATE_CB is the callback to use when the dialog pops down.
1540
1541 Returns the GTK dialog widget. */
1542
1543 static GtkWidget *
1544 create_dialog (widget_value *wv,
1545 GCallback select_cb,
1546 GCallback deactivate_cb)
1547 {
1548 const char *title = get_dialog_title (wv->name[0]);
1549 int total_buttons = wv->name[1] - '0';
1550 int right_buttons = wv->name[4] - '0';
1551 int left_buttons;
1552 int button_nr = 0;
1553 int button_spacing = 10;
1554 GtkWidget *wdialog = gtk_dialog_new ();
1555 GtkDialog *wd = GTK_DIALOG (wdialog);
1556 widget_value *item;
1557 GtkWidget *whbox_down;
1558
1559 /* If the number of buttons is greater than 4, make two rows of buttons
1560 instead. This looks better. */
1561 bool make_two_rows = total_buttons > 4;
1562
1563 #if GTK_CHECK_VERSION (3, 12, 0)
1564 GtkBuilder *gbld = gtk_builder_new ();
1565 GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
1566 gbld,
1567 "action_area");
1568 GtkBox *cur_box = GTK_BOX (go);
1569 g_object_unref (G_OBJECT (gbld));
1570 #else
1571 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1572 #endif
1573
1574 if (right_buttons == 0) right_buttons = total_buttons/2;
1575 left_buttons = total_buttons - right_buttons;
1576
1577 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1578 gtk_widget_set_name (wdialog, "emacs-dialog");
1579
1580
1581 if (make_two_rows)
1582 {
1583 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1584 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1585 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1586 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1587 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1588 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1589
1590 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1591 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1592 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1593
1594 cur_box = GTK_BOX (whbox_up);
1595 }
1596
1597 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1598 G_CALLBACK (dialog_delete_callback), 0);
1599
1600 if (deactivate_cb)
1601 {
1602 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1603 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1604 }
1605
1606 for (item = wv->contents; item; item = item->next)
1607 {
1608 char *utf8_label = get_utf8_string (item->value);
1609 GtkWidget *w;
1610 GtkRequisition req;
1611
1612 if (item->name && strcmp (item->name, "message") == 0)
1613 {
1614 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1615 /* This is the text part of the dialog. */
1616 w = gtk_label_new (utf8_label);
1617 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1618 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1619 #if GTK_CHECK_VERSION (3, 14, 0)
1620 gtk_widget_set_halign (w, GTK_ALIGN_START);
1621 gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
1622 #else
1623 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1624 #endif
1625 /* Try to make dialog look better. Must realize first so
1626 the widget can calculate the size it needs. */
1627 gtk_widget_realize (w);
1628 gtk_widget_get_preferred_size (w, NULL, &req);
1629 gtk_box_set_spacing (wvbox, req.height);
1630 if (item->value && strlen (item->value) > 0)
1631 button_spacing = 2*req.width/strlen (item->value);
1632 if (button_spacing < 10) button_spacing = 10;
1633 }
1634 else
1635 {
1636 /* This is one button to add to the dialog. */
1637 w = gtk_button_new_with_label (utf8_label);
1638 if (! item->enabled)
1639 gtk_widget_set_sensitive (w, FALSE);
1640 if (select_cb)
1641 g_signal_connect (G_OBJECT (w), "clicked",
1642 select_cb, item->call_data);
1643
1644 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1645 if (++button_nr == left_buttons)
1646 {
1647 if (make_two_rows)
1648 cur_box = GTK_BOX (whbox_down);
1649 }
1650 }
1651
1652 if (utf8_label)
1653 g_free (utf8_label);
1654 }
1655
1656 return wdialog;
1657 }
1658
1659 struct xg_dialog_data
1660 {
1661 GMainLoop *loop;
1662 int response;
1663 GtkWidget *w;
1664 guint timerid;
1665 };
1666
1667 /* Function that is called when the file or font dialogs pop down.
1668 W is the dialog widget, RESPONSE is the response code.
1669 USER_DATA is what we passed in to g_signal_connect. */
1670
1671 static void
1672 xg_dialog_response_cb (GtkDialog *w,
1673 gint response,
1674 gpointer user_data)
1675 {
1676 struct xg_dialog_data *dd = user_data;
1677 dd->response = response;
1678 g_main_loop_quit (dd->loop);
1679 }
1680
1681
1682 /* Destroy the dialog. This makes it pop down. */
1683
1684 static void
1685 pop_down_dialog (void *arg)
1686 {
1687 struct xg_dialog_data *dd = arg;
1688
1689 block_input ();
1690 if (dd->w) gtk_widget_destroy (dd->w);
1691 if (dd->timerid != 0) g_source_remove (dd->timerid);
1692
1693 g_main_loop_quit (dd->loop);
1694 g_main_loop_unref (dd->loop);
1695
1696 unblock_input ();
1697 }
1698
1699 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1700 We pass in DATA as gpointer* so we can use this as a callback. */
1701
1702 static gboolean
1703 xg_maybe_add_timer (gpointer data)
1704 {
1705 struct xg_dialog_data *dd = data;
1706 struct timespec next_time = timer_check ();
1707
1708 dd->timerid = 0;
1709
1710 if (timespec_valid_p (next_time))
1711 {
1712 time_t s = next_time.tv_sec;
1713 int per_ms = TIMESPEC_RESOLUTION / 1000;
1714 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
1715 if (s <= ((guint) -1 - ms) / 1000)
1716 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1717 }
1718 return FALSE;
1719 }
1720
1721
1722 /* Pops up a modal dialog W and waits for response.
1723 We don't use gtk_dialog_run because we want to process emacs timers.
1724 The dialog W is not destroyed when this function returns. */
1725
1726 static int
1727 xg_dialog_run (struct frame *f, GtkWidget *w)
1728 {
1729 ptrdiff_t count = SPECPDL_INDEX ();
1730 struct xg_dialog_data dd;
1731
1732 xg_set_screen (w, f);
1733 gtk_window_set_transient_for (GTK_WINDOW (w),
1734 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1735 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1736 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1737
1738 dd.loop = g_main_loop_new (NULL, FALSE);
1739 dd.response = GTK_RESPONSE_CANCEL;
1740 dd.w = w;
1741 dd.timerid = 0;
1742
1743 g_signal_connect (G_OBJECT (w),
1744 "response",
1745 G_CALLBACK (xg_dialog_response_cb),
1746 &dd);
1747 /* Don't destroy the widget if closed by the window manager close button. */
1748 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1749 gtk_widget_show (w);
1750
1751 record_unwind_protect_ptr (pop_down_dialog, &dd);
1752
1753 (void) xg_maybe_add_timer (&dd);
1754 g_main_loop_run (dd.loop);
1755
1756 dd.w = 0;
1757 unbind_to (count, Qnil);
1758
1759 return dd.response;
1760 }
1761
1762 \f
1763 /***********************************************************************
1764 File dialog functions
1765 ***********************************************************************/
1766 /* Return true if the old file selection dialog is being used. */
1767
1768 bool
1769 xg_uses_old_file_dialog (void)
1770 {
1771 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1772 return x_gtk_use_old_file_dialog;
1773 #else
1774 return 0;
1775 #endif
1776 }
1777
1778
1779 typedef char * (*xg_get_file_func) (GtkWidget *);
1780
1781 /* Return the selected file for file chooser dialog W.
1782 The returned string must be free:d. */
1783
1784 static char *
1785 xg_get_file_name_from_chooser (GtkWidget *w)
1786 {
1787 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1788 }
1789
1790 /* Callback called when the "Show hidden files" toggle is pressed.
1791 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1792
1793 static void
1794 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1795 {
1796 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1797 gboolean visible;
1798 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1799 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1800 }
1801
1802
1803 /* Callback called when a property changes in a file chooser.
1804 GOBJECT is the file chooser dialog, ARG1 describes the property.
1805 USER_DATA is the toggle widget in the file chooser dialog.
1806 We use this to update the "Show hidden files" toggle when the user
1807 changes that property by right clicking in the file list. */
1808
1809 static void
1810 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1811 {
1812 if (strcmp (arg1->name, "show-hidden") == 0)
1813 {
1814 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1815 gboolean visible, toggle_on;
1816
1817 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1818 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1819
1820 if (!!visible != !!toggle_on)
1821 {
1822 gpointer cb = (gpointer) G_CALLBACK (xg_toggle_visibility_cb);
1823 g_signal_handlers_block_by_func (G_OBJECT (wtoggle), cb, gobject);
1824 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1825 g_signal_handlers_unblock_by_func (G_OBJECT (wtoggle), cb, gobject);
1826 }
1827 x_gtk_show_hidden_files = visible;
1828 }
1829 }
1830
1831 /* Read a file name from the user using a file chooser dialog.
1832 F is the current frame.
1833 PROMPT is a prompt to show to the user. May not be NULL.
1834 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1835 If MUSTMATCH_P, the returned file name must be an existing
1836 file. (Actually, this only has cosmetic effects, the user can
1837 still enter a non-existing file.) *FUNC is set to a function that
1838 can be used to retrieve the selected file name from the returned widget.
1839
1840 Returns the created widget. */
1841
1842 static GtkWidget *
1843 xg_get_file_with_chooser (struct frame *f,
1844 char *prompt,
1845 char *default_filename,
1846 bool mustmatch_p, bool only_dir_p,
1847 xg_get_file_func *func)
1848 {
1849 char msgbuf[1024];
1850
1851 GtkWidget *filewin, *wtoggle, *wbox;
1852 GtkWidget *wmessage UNINIT;
1853 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1854 GtkFileChooserAction action = (mustmatch_p ?
1855 GTK_FILE_CHOOSER_ACTION_OPEN :
1856 GTK_FILE_CHOOSER_ACTION_SAVE);
1857
1858 if (only_dir_p)
1859 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1860
1861 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1862 XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
1863 (mustmatch_p || only_dir_p ?
1864 XG_TEXT_OPEN : XG_TEXT_OK),
1865 GTK_RESPONSE_OK,
1866 NULL);
1867 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
1868
1869 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1870 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
1871 gtk_widget_show (wbox);
1872 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
1873
1874 if (x_gtk_show_hidden_files)
1875 {
1876 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
1877 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
1878 }
1879 gtk_widget_show (wtoggle);
1880 g_signal_connect (G_OBJECT (wtoggle), "clicked",
1881 G_CALLBACK (xg_toggle_visibility_cb), filewin);
1882 g_signal_connect (G_OBJECT (filewin), "notify",
1883 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
1884
1885 if (x_gtk_file_dialog_help_text)
1886 {
1887 char *z = msgbuf;
1888 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
1889 Show the C-l help text only for versions < 2.10. */
1890 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
1891 z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
1892 strcpy (z, "\nIf you don't like this file selector, use the "
1893 "corresponding\nkey binding or customize "
1894 "use-file-dialog to turn it off.");
1895
1896 wmessage = gtk_label_new (msgbuf);
1897 gtk_widget_show (wmessage);
1898 }
1899
1900 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
1901 if (x_gtk_file_dialog_help_text)
1902 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
1903 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
1904
1905 if (default_filename)
1906 {
1907 Lisp_Object file;
1908 char *utf8_filename;
1909
1910 file = build_string (default_filename);
1911
1912 /* File chooser does not understand ~/... in the file name. It must be
1913 an absolute name starting with /. */
1914 if (default_filename[0] != '/')
1915 file = Fexpand_file_name (file, Qnil);
1916
1917 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
1918 if (! NILP (Ffile_directory_p (file)))
1919 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
1920 utf8_filename);
1921 else
1922 {
1923 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1924 utf8_filename);
1925 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
1926 {
1927 char *cp = strrchr (utf8_filename, '/');
1928 if (cp) ++cp;
1929 else cp = utf8_filename;
1930 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
1931 }
1932 }
1933 }
1934
1935 *func = xg_get_file_name_from_chooser;
1936 return filewin;
1937 }
1938
1939 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1940
1941 /* Return the selected file for file selector dialog W.
1942 The returned string must be free:d. */
1943
1944 static char *
1945 xg_get_file_name_from_selector (GtkWidget *w)
1946 {
1947 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
1948 return xstrdup (gtk_file_selection_get_filename (filesel));
1949 }
1950
1951 /* Create a file selection dialog.
1952 F is the current frame.
1953 PROMPT is a prompt to show to the user. May not be NULL.
1954 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1955 If MUSTMATCH_P, the returned file name must be an existing
1956 file. *FUNC is set to a function that can be used to retrieve the
1957 selected file name from the returned widget.
1958
1959 Returns the created widget. */
1960
1961 static GtkWidget *
1962 xg_get_file_with_selection (struct frame *f,
1963 char *prompt,
1964 char *default_filename,
1965 bool mustmatch_p, bool only_dir_p,
1966 xg_get_file_func *func)
1967 {
1968 GtkWidget *filewin;
1969 GtkFileSelection *filesel;
1970
1971 filewin = gtk_file_selection_new (prompt);
1972 filesel = GTK_FILE_SELECTION (filewin);
1973
1974 if (default_filename)
1975 gtk_file_selection_set_filename (filesel, default_filename);
1976
1977 if (mustmatch_p)
1978 {
1979 /* The selection_entry part of filesel is not documented. */
1980 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1981 gtk_file_selection_hide_fileop_buttons (filesel);
1982 }
1983
1984 *func = xg_get_file_name_from_selector;
1985
1986 return filewin;
1987 }
1988 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
1989
1990 /* Read a file name from the user using a file dialog, either the old
1991 file selection dialog, or the new file chooser dialog. Which to use
1992 depends on what the GTK version used has, and what the value of
1993 gtk-use-old-file-dialog.
1994 F is the current frame.
1995 PROMPT is a prompt to show to the user. May not be NULL.
1996 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1997 If MUSTMATCH_P, the returned file name must be an existing
1998 file.
1999
2000 Returns a file name or NULL if no file was selected.
2001 The returned string must be freed by the caller. */
2002
2003 char *
2004 xg_get_file_name (struct frame *f,
2005 char *prompt,
2006 char *default_filename,
2007 bool mustmatch_p,
2008 bool only_dir_p)
2009 {
2010 GtkWidget *w = 0;
2011 char *fn = 0;
2012 int filesel_done = 0;
2013 xg_get_file_func func;
2014
2015 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2016
2017 if (xg_uses_old_file_dialog ())
2018 w = xg_get_file_with_selection (f, prompt, default_filename,
2019 mustmatch_p, only_dir_p, &func);
2020 else
2021 w = xg_get_file_with_chooser (f, prompt, default_filename,
2022 mustmatch_p, only_dir_p, &func);
2023
2024 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
2025 w = xg_get_file_with_chooser (f, prompt, default_filename,
2026 mustmatch_p, only_dir_p, &func);
2027 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
2028
2029 gtk_widget_set_name (w, "emacs-filedialog");
2030
2031 filesel_done = xg_dialog_run (f, w);
2032 if (filesel_done == GTK_RESPONSE_OK)
2033 fn = (*func) (w);
2034
2035 gtk_widget_destroy (w);
2036 return fn;
2037 }
2038
2039 /***********************************************************************
2040 GTK font chooser
2041 ***********************************************************************/
2042
2043 #ifdef HAVE_FREETYPE
2044
2045 #if USE_NEW_GTK_FONT_CHOOSER
2046
2047 #define XG_WEIGHT_TO_SYMBOL(w) \
2048 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2049 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2050 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2051 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2052 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2053 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2054 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2055 : Qultra_bold)
2056
2057 #define XG_STYLE_TO_SYMBOL(s) \
2058 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2059 : s == PANGO_STYLE_ITALIC ? Qitalic \
2060 : Qnormal)
2061
2062 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2063
2064
2065 static char *x_last_font_name;
2066
2067 /* Pop up a GTK font selector and return the name of the font the user
2068 selects, as a C string. The returned font name follows GTK's own
2069 format:
2070
2071 `FAMILY [VALUE1 VALUE2] SIZE'
2072
2073 This can be parsed using font_parse_fcname in font.c.
2074 DEFAULT_NAME, if non-zero, is the default font name. */
2075
2076 Lisp_Object
2077 xg_get_font (struct frame *f, const char *default_name)
2078 {
2079 GtkWidget *w;
2080 int done = 0;
2081 Lisp_Object font = Qnil;
2082
2083 w = gtk_font_chooser_dialog_new
2084 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2085
2086 if (default_name)
2087 {
2088 /* Convert fontconfig names to Gtk names, i.e. remove - before
2089 number */
2090 char *p = strrchr (default_name, '-');
2091 if (p)
2092 {
2093 char *ep = p+1;
2094 while (c_isdigit (*ep))
2095 ++ep;
2096 if (*ep == '\0') *p = ' ';
2097 }
2098 }
2099 else if (x_last_font_name)
2100 default_name = x_last_font_name;
2101
2102 if (default_name)
2103 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2104
2105 gtk_widget_set_name (w, "emacs-fontdialog");
2106 done = xg_dialog_run (f, w);
2107 if (done == GTK_RESPONSE_OK)
2108 {
2109 #if USE_NEW_GTK_FONT_CHOOSER
2110 /* Use the GTK3 font chooser. */
2111 PangoFontDescription *desc
2112 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2113
2114 if (desc)
2115 {
2116 const char *name = pango_font_description_get_family (desc);
2117 gint size = pango_font_description_get_size (desc);
2118 PangoWeight weight = pango_font_description_get_weight (desc);
2119 PangoStyle style = pango_font_description_get_style (desc);
2120
2121 #ifdef USE_CAIRO
2122 #define FONT_TYPE_WANTED (Qftcr)
2123 #else
2124 #define FONT_TYPE_WANTED (Qxft)
2125 #endif
2126 font = CALLN (Ffont_spec,
2127 QCname, build_string (name),
2128 QCsize, make_float (pango_units_to_double (size)),
2129 QCweight, XG_WEIGHT_TO_SYMBOL (weight),
2130 QCslant, XG_STYLE_TO_SYMBOL (style),
2131 QCtype,
2132 FONT_TYPE_WANTED);
2133
2134 pango_font_description_free (desc);
2135 dupstring (&x_last_font_name, name);
2136 }
2137
2138 #else /* Use old font selector, which just returns the font name. */
2139
2140 char *font_name
2141 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2142
2143 if (font_name)
2144 {
2145 font = build_string (font_name);
2146 g_free (x_last_font_name);
2147 x_last_font_name = font_name;
2148 }
2149 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2150 }
2151
2152 gtk_widget_destroy (w);
2153 return font;
2154 }
2155 #endif /* HAVE_FREETYPE */
2156
2157
2158 \f
2159 /***********************************************************************
2160 Menu functions.
2161 ***********************************************************************/
2162
2163 /* The name of menu items that can be used for customization. Since GTK
2164 RC files are very crude and primitive, we have to set this on all
2165 menu item names so a user can easily customize menu items. */
2166
2167 #define MENU_ITEM_NAME "emacs-menuitem"
2168
2169
2170 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2171 during GC. The next member points to the items. */
2172 static xg_list_node xg_menu_cb_list;
2173
2174 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2175 during GC. The next member points to the items. */
2176 static xg_list_node xg_menu_item_cb_list;
2177
2178 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2179 F is the frame CL_DATA will be initialized for.
2180 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2181
2182 The menu bar and all sub menus under the menu bar in a frame
2183 share the same structure, hence the reference count.
2184
2185 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2186 allocated xg_menu_cb_data if CL_DATA is NULL. */
2187
2188 static xg_menu_cb_data *
2189 make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
2190 {
2191 if (! cl_data)
2192 {
2193 cl_data = xmalloc (sizeof *cl_data);
2194 cl_data->f = f;
2195 cl_data->menu_bar_vector = f->menu_bar_vector;
2196 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2197 cl_data->highlight_cb = highlight_cb;
2198 cl_data->ref_count = 0;
2199
2200 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2201 }
2202
2203 cl_data->ref_count++;
2204
2205 return cl_data;
2206 }
2207
2208 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2209 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2210
2211 When the menu bar is updated, menu items may have been added and/or
2212 removed, so menu_bar_vector and menu_bar_items_used change. We must
2213 then update CL_DATA since it is used to determine which menu
2214 item that is invoked in the menu.
2215 HIGHLIGHT_CB could change, there is no check that the same
2216 function is given when modifying a menu bar as was given when
2217 creating the menu bar. */
2218
2219 static void
2220 update_cl_data (xg_menu_cb_data *cl_data,
2221 struct frame *f,
2222 GCallback highlight_cb)
2223 {
2224 if (cl_data)
2225 {
2226 cl_data->f = f;
2227 cl_data->menu_bar_vector = f->menu_bar_vector;
2228 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2229 cl_data->highlight_cb = highlight_cb;
2230 }
2231 }
2232
2233 /* Decrease reference count for CL_DATA.
2234 If reference count is zero, free CL_DATA. */
2235
2236 static void
2237 unref_cl_data (xg_menu_cb_data *cl_data)
2238 {
2239 if (cl_data && cl_data->ref_count > 0)
2240 {
2241 cl_data->ref_count--;
2242 if (cl_data->ref_count == 0)
2243 {
2244 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2245 xfree (cl_data);
2246 }
2247 }
2248 }
2249
2250 /* Function that marks all lisp data during GC. */
2251
2252 void
2253 xg_mark_data (void)
2254 {
2255 xg_list_node *iter;
2256 Lisp_Object rest, frame;
2257
2258 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2259 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2260
2261 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2262 {
2263 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2264
2265 if (! NILP (cb_data->help))
2266 mark_object (cb_data->help);
2267 }
2268
2269 FOR_EACH_FRAME (rest, frame)
2270 {
2271 struct frame *f = XFRAME (frame);
2272
2273 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2274 {
2275 struct xg_frame_tb_info *tbinfo
2276 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2277 TB_INFO_KEY);
2278 if (tbinfo)
2279 {
2280 mark_object (tbinfo->last_tool_bar);
2281 mark_object (tbinfo->style);
2282 }
2283 }
2284 }
2285 }
2286
2287
2288 /* Callback called when a menu item is destroyed. Used to free data.
2289 W is the widget that is being destroyed (not used).
2290 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2291
2292 static void
2293 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2294 {
2295 if (client_data)
2296 {
2297 xg_menu_item_cb_data *data = client_data;
2298 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2299 xfree (data);
2300 }
2301 }
2302
2303 /* Callback called when the pointer enters/leaves a menu item.
2304 W is the parent of the menu item.
2305 EVENT is either an enter event or leave event.
2306 CLIENT_DATA is not used.
2307
2308 Returns FALSE to tell GTK to keep processing this event. */
2309
2310 static gboolean
2311 menuitem_highlight_callback (GtkWidget *w,
2312 GdkEventCrossing *event,
2313 gpointer client_data)
2314 {
2315 GdkEvent ev;
2316 GtkWidget *subwidget;
2317 xg_menu_item_cb_data *data;
2318
2319 ev.crossing = *event;
2320 subwidget = gtk_get_event_widget (&ev);
2321 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
2322 if (data)
2323 {
2324 if (! NILP (data->help) && data->cl_data->highlight_cb)
2325 {
2326 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2327 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2328 (*func) (subwidget, call_data);
2329 }
2330 }
2331
2332 return FALSE;
2333 }
2334
2335 /* Callback called when a menu is destroyed. Used to free data.
2336 W is the widget that is being destroyed (not used).
2337 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2338
2339 static void
2340 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2341 {
2342 unref_cl_data (client_data);
2343 }
2344
2345 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2346 must be non-NULL) and can be inserted into a menu item.
2347
2348 Returns the GtkHBox. */
2349
2350 static GtkWidget *
2351 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2352 {
2353 GtkWidget *wlbl;
2354 GtkWidget *wkey;
2355 GtkWidget *wbox;
2356
2357 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2358 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2359 wlbl = gtk_label_new (utf8_label);
2360 wkey = gtk_label_new (utf8_key);
2361
2362 #if GTK_CHECK_VERSION (3, 14, 0)
2363 gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
2364 gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
2365 gtk_widget_set_halign (wkey, GTK_ALIGN_START);
2366 gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
2367 #else
2368 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2369 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2370 #endif
2371 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2372 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2373
2374 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2375 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2376 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2377
2378 return wbox;
2379 }
2380
2381 /* Make and return a menu item widget with the key to the right.
2382 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2383 UTF8_KEY is the text representing the key binding.
2384 ITEM is the widget_value describing the menu item.
2385
2386 GROUP is an in/out parameter. If the menu item to be created is not
2387 part of any radio menu group, *GROUP contains NULL on entry and exit.
2388 If the menu item to be created is part of a radio menu group, on entry
2389 *GROUP contains the group to use, or NULL if this is the first item
2390 in the group. On exit, *GROUP contains the radio item group.
2391
2392 Unfortunately, keys don't line up as nicely as in Motif,
2393 but the MacOS X version doesn't either, so I guess that is OK. */
2394
2395 static GtkWidget *
2396 make_menu_item (const char *utf8_label,
2397 const char *utf8_key,
2398 widget_value *item,
2399 GSList **group)
2400 {
2401 GtkWidget *w;
2402 GtkWidget *wtoadd = 0;
2403
2404 /* It has been observed that some menu items have a NULL name field.
2405 This will lead to this function being called with a NULL utf8_label.
2406 GTK crashes on that so we set a blank label. Why there is a NULL
2407 name remains to be investigated. */
2408 if (! utf8_label) utf8_label = " ";
2409
2410 if (utf8_key)
2411 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2412
2413 if (item->button_type == BUTTON_TYPE_TOGGLE)
2414 {
2415 *group = NULL;
2416 if (utf8_key) w = gtk_check_menu_item_new ();
2417 else w = gtk_check_menu_item_new_with_label (utf8_label);
2418 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2419 }
2420 else if (item->button_type == BUTTON_TYPE_RADIO)
2421 {
2422 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2423 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2424 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2425 if (item->selected)
2426 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2427 }
2428 else
2429 {
2430 *group = NULL;
2431 if (utf8_key) w = gtk_menu_item_new ();
2432 else w = gtk_menu_item_new_with_label (utf8_label);
2433 }
2434
2435 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2436 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2437
2438 return w;
2439 }
2440
2441 /* Create a menu item widget, and connect the callbacks.
2442 ITEM describes the menu item.
2443 F is the frame the created menu belongs to.
2444 SELECT_CB is the callback to use when a menu item is selected.
2445 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2446 CL_DATA points to the callback data to be used for this menu.
2447 GROUP is an in/out parameter. If the menu item to be created is not
2448 part of any radio menu group, *GROUP contains NULL on entry and exit.
2449 If the menu item to be created is part of a radio menu group, on entry
2450 *GROUP contains the group to use, or NULL if this is the first item
2451 in the group. On exit, *GROUP contains the radio item group.
2452
2453 Returns the created GtkWidget. */
2454
2455 static GtkWidget *
2456 xg_create_one_menuitem (widget_value *item,
2457 struct frame *f,
2458 GCallback select_cb,
2459 GCallback highlight_cb,
2460 xg_menu_cb_data *cl_data,
2461 GSList **group)
2462 {
2463 char *utf8_label;
2464 char *utf8_key;
2465 GtkWidget *w;
2466 xg_menu_item_cb_data *cb_data;
2467
2468 utf8_label = get_utf8_string (item->name);
2469 utf8_key = get_utf8_string (item->key);
2470
2471 w = make_menu_item (utf8_label, utf8_key, item, group);
2472
2473 if (utf8_label) g_free (utf8_label);
2474 if (utf8_key) g_free (utf8_key);
2475
2476 cb_data = xmalloc (sizeof *cb_data);
2477
2478 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2479
2480 cb_data->select_id = 0;
2481 cb_data->help = item->help;
2482 cb_data->cl_data = cl_data;
2483 cb_data->call_data = item->call_data;
2484
2485 g_signal_connect (G_OBJECT (w),
2486 "destroy",
2487 G_CALLBACK (menuitem_destroy_callback),
2488 cb_data);
2489
2490 /* Put cb_data in widget, so we can get at it when modifying menubar */
2491 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2492
2493 /* final item, not a submenu */
2494 if (item->call_data && ! item->contents)
2495 {
2496 if (select_cb)
2497 cb_data->select_id
2498 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2499 }
2500
2501 return w;
2502 }
2503
2504 /* Create a full menu tree specified by DATA.
2505 F is the frame the created menu belongs to.
2506 SELECT_CB is the callback to use when a menu item is selected.
2507 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2508 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2509 If POP_UP_P, create a popup menu.
2510 If MENU_BAR_P, create a menu bar.
2511 TOPMENU is the topmost GtkWidget that others shall be placed under.
2512 It may be NULL, in that case we create the appropriate widget
2513 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2514 CL_DATA is the callback data we shall use for this menu, or NULL
2515 if we haven't set the first callback yet.
2516 NAME is the name to give to the top level menu if this function
2517 creates it. May be NULL to not set any name.
2518
2519 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2520 not NULL.
2521
2522 This function calls itself to create submenus. */
2523
2524 static GtkWidget *
2525 create_menus (widget_value *data,
2526 struct frame *f,
2527 GCallback select_cb,
2528 GCallback deactivate_cb,
2529 GCallback highlight_cb,
2530 bool pop_up_p,
2531 bool menu_bar_p,
2532 GtkWidget *topmenu,
2533 xg_menu_cb_data *cl_data,
2534 const char *name)
2535 {
2536 widget_value *item;
2537 GtkWidget *wmenu = topmenu;
2538 GSList *group = NULL;
2539
2540 if (! topmenu)
2541 {
2542 if (! menu_bar_p)
2543 {
2544 wmenu = gtk_menu_new ();
2545 xg_set_screen (wmenu, f);
2546 /* Connect this to the menu instead of items so we get enter/leave for
2547 disabled items also. TODO: Still does not get enter/leave for
2548 disabled items in detached menus. */
2549 g_signal_connect (G_OBJECT (wmenu),
2550 "enter-notify-event",
2551 G_CALLBACK (menuitem_highlight_callback),
2552 NULL);
2553 g_signal_connect (G_OBJECT (wmenu),
2554 "leave-notify-event",
2555 G_CALLBACK (menuitem_highlight_callback),
2556 NULL);
2557 }
2558 else
2559 {
2560 wmenu = gtk_menu_bar_new ();
2561 /* Set width of menu bar to a small value so it doesn't enlarge
2562 a small initial frame size. The width will be set to the
2563 width of the frame later on when it is added to a container.
2564 height -1: Natural height. */
2565 gtk_widget_set_size_request (wmenu, 1, -1);
2566 }
2567
2568 /* Put cl_data on the top menu for easier access. */
2569 cl_data = make_cl_data (cl_data, f, highlight_cb);
2570 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2571 g_signal_connect (G_OBJECT (wmenu), "destroy",
2572 G_CALLBACK (menu_destroy_callback), cl_data);
2573
2574 if (name)
2575 gtk_widget_set_name (wmenu, name);
2576
2577 if (deactivate_cb)
2578 g_signal_connect (G_OBJECT (wmenu),
2579 "selection-done", deactivate_cb, 0);
2580 }
2581
2582 for (item = data; item; item = item->next)
2583 {
2584 GtkWidget *w;
2585
2586 if (pop_up_p && !item->contents && !item->call_data
2587 && !menu_separator_name_p (item->name))
2588 {
2589 char *utf8_label;
2590 /* A title for a popup. We do the same as GTK does when
2591 creating titles, but it does not look good. */
2592 group = NULL;
2593 utf8_label = get_utf8_string (item->name);
2594
2595 w = gtk_menu_item_new_with_label (utf8_label);
2596 gtk_widget_set_sensitive (w, FALSE);
2597 if (utf8_label) g_free (utf8_label);
2598 }
2599 else if (menu_separator_name_p (item->name))
2600 {
2601 group = NULL;
2602 /* GTK only have one separator type. */
2603 w = gtk_separator_menu_item_new ();
2604 }
2605 else
2606 {
2607 w = xg_create_one_menuitem (item,
2608 f,
2609 item->contents ? 0 : select_cb,
2610 highlight_cb,
2611 cl_data,
2612 &group);
2613
2614 /* Create a possibly empty submenu for menu bar items, since some
2615 themes don't highlight items correctly without it. */
2616 if (item->contents || menu_bar_p)
2617 {
2618 GtkWidget *submenu = create_menus (item->contents,
2619 f,
2620 select_cb,
2621 deactivate_cb,
2622 highlight_cb,
2623 0,
2624 0,
2625 0,
2626 cl_data,
2627 0);
2628 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2629 }
2630 }
2631
2632 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2633 gtk_widget_set_name (w, MENU_ITEM_NAME);
2634 }
2635
2636 return wmenu;
2637 }
2638
2639 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2640 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2641 with some text and buttons.
2642 F is the frame the created item belongs to.
2643 NAME is the name to use for the top widget.
2644 VAL is a widget_value structure describing items to be created.
2645 SELECT_CB is the callback to use when a menu item is selected or
2646 a dialog button is pressed.
2647 DEACTIVATE_CB is the callback to use when an item is deactivated.
2648 For a menu, when a sub menu is not shown anymore, for a dialog it is
2649 called when the dialog is popped down.
2650 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2651
2652 Returns the widget created. */
2653
2654 GtkWidget *
2655 xg_create_widget (const char *type, const char *name, struct frame *f,
2656 widget_value *val, GCallback select_cb,
2657 GCallback deactivate_cb, GCallback highlight_cb)
2658 {
2659 GtkWidget *w = 0;
2660 bool menu_bar_p = strcmp (type, "menubar") == 0;
2661 bool pop_up_p = strcmp (type, "popup") == 0;
2662
2663 if (strcmp (type, "dialog") == 0)
2664 {
2665 w = create_dialog (val, select_cb, deactivate_cb);
2666 xg_set_screen (w, f);
2667 gtk_window_set_transient_for (GTK_WINDOW (w),
2668 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2669 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2670 gtk_widget_set_name (w, "emacs-dialog");
2671 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2672 }
2673 else if (menu_bar_p || pop_up_p)
2674 {
2675 w = create_menus (val->contents,
2676 f,
2677 select_cb,
2678 deactivate_cb,
2679 highlight_cb,
2680 pop_up_p,
2681 menu_bar_p,
2682 0,
2683 0,
2684 name);
2685
2686 /* Set the cursor to an arrow for popup menus when they are mapped.
2687 This is done by default for menu bar menus. */
2688 if (pop_up_p)
2689 {
2690 /* Must realize so the GdkWindow inside the widget is created. */
2691 gtk_widget_realize (w);
2692 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
2693 }
2694 }
2695 else
2696 {
2697 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2698 type);
2699 }
2700
2701 return w;
2702 }
2703
2704 /* Return the label for menu item WITEM. */
2705
2706 static const char *
2707 xg_get_menu_item_label (GtkMenuItem *witem)
2708 {
2709 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2710 return gtk_label_get_label (wlabel);
2711 }
2712
2713 /* Return true if the menu item WITEM has the text LABEL. */
2714
2715 static bool
2716 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2717 {
2718 bool is_same = 0;
2719 char *utf8_label = get_utf8_string (label);
2720 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2721
2722 if (! old_label && ! utf8_label)
2723 is_same = 1;
2724 else if (old_label && utf8_label)
2725 is_same = strcmp (utf8_label, old_label) == 0;
2726
2727 if (utf8_label) g_free (utf8_label);
2728
2729 return is_same;
2730 }
2731
2732 /* Destroy widgets in LIST. */
2733
2734 static void
2735 xg_destroy_widgets (GList *list)
2736 {
2737 GList *iter;
2738
2739 for (iter = list; iter; iter = g_list_next (iter))
2740 {
2741 GtkWidget *w = GTK_WIDGET (iter->data);
2742
2743 /* Destroying the widget will remove it from the container it is in. */
2744 gtk_widget_destroy (w);
2745 }
2746 }
2747
2748 /* Update the top level names in MENUBAR (i.e. not submenus).
2749 F is the frame the menu bar belongs to.
2750 *LIST is a list with the current menu bar names (menu item widgets).
2751 ITER is the item within *LIST that shall be updated.
2752 POS is the numerical position, starting at 0, of ITER in *LIST.
2753 VAL describes what the menu bar shall look like after the update.
2754 SELECT_CB is the callback to use when a menu item is selected.
2755 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2756 CL_DATA points to the callback data to be used for this menu bar.
2757
2758 This function calls itself to walk through the menu bar names. */
2759
2760 static void
2761 xg_update_menubar (GtkWidget *menubar,
2762 struct frame *f,
2763 GList **list,
2764 GList *iter,
2765 int pos,
2766 widget_value *val,
2767 GCallback select_cb,
2768 GCallback deactivate_cb,
2769 GCallback highlight_cb,
2770 xg_menu_cb_data *cl_data)
2771 {
2772 if (! iter && ! val)
2773 return;
2774 else if (iter && ! val)
2775 {
2776 /* Item(s) have been removed. Remove all remaining items. */
2777 xg_destroy_widgets (iter);
2778
2779 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2780 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2781 gtk_menu_item_new_with_label (""),
2782 0);
2783 /* All updated. */
2784 val = 0;
2785 iter = 0;
2786 }
2787 else if (! iter && val)
2788 {
2789 /* Item(s) added. Add all new items in one call. */
2790 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2791 0, 1, menubar, cl_data, 0);
2792
2793 /* All updated. */
2794 val = 0;
2795 iter = 0;
2796 }
2797 /* Below this neither iter or val is NULL */
2798 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2799 {
2800 /* This item is still the same, check next item. */
2801 val = val->next;
2802 iter = g_list_next (iter);
2803 ++pos;
2804 }
2805 else /* This item is changed. */
2806 {
2807 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2808 GtkMenuItem *witem2 = 0;
2809 bool val_in_menubar = 0;
2810 bool iter_in_new_menubar = 0;
2811 GList *iter2;
2812 widget_value *cur;
2813
2814 /* See if the changed entry (val) is present later in the menu bar */
2815 for (iter2 = iter;
2816 iter2 && ! val_in_menubar;
2817 iter2 = g_list_next (iter2))
2818 {
2819 witem2 = GTK_MENU_ITEM (iter2->data);
2820 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2821 }
2822
2823 /* See if the current entry (iter) is present later in the
2824 specification for the new menu bar. */
2825 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2826 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2827
2828 if (val_in_menubar && ! iter_in_new_menubar)
2829 {
2830 int nr = pos;
2831
2832 /* This corresponds to:
2833 Current: A B C
2834 New: A C
2835 Remove B. */
2836
2837 g_object_ref (G_OBJECT (witem));
2838 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2839 gtk_widget_destroy (GTK_WIDGET (witem));
2840
2841 /* Must get new list since the old changed. */
2842 g_list_free (*list);
2843 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2844 while (nr-- > 0) iter = g_list_next (iter);
2845 }
2846 else if (! val_in_menubar && ! iter_in_new_menubar)
2847 {
2848 /* This corresponds to:
2849 Current: A B C
2850 New: A X C
2851 Rename B to X. This might seem to be a strange thing to do,
2852 since if there is a menu under B it will be totally wrong for X.
2853 But consider editing a C file. Then there is a C-mode menu
2854 (corresponds to B above).
2855 If then doing C-x C-f the minibuf menu (X above) replaces the
2856 C-mode menu. When returning from the minibuffer, we get
2857 back the C-mode menu. Thus we do:
2858 Rename B to X (C-mode to minibuf menu)
2859 Rename X to B (minibuf to C-mode menu).
2860 If the X menu hasn't been invoked, the menu under B
2861 is up to date when leaving the minibuffer. */
2862 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2863 char *utf8_label = get_utf8_string (val->name);
2864
2865 /* GTK menu items don't notice when their labels have been
2866 changed from underneath them, so we have to explicitly
2867 use g_object_notify to tell listeners (e.g., a GMenuModel
2868 bridge that might be loaded) that the item's label has
2869 changed. */
2870 gtk_label_set_text (wlabel, utf8_label);
2871 #if GTK_CHECK_VERSION (2, 16, 0)
2872 g_object_notify (G_OBJECT (witem), "label");
2873 #endif
2874 if (utf8_label) g_free (utf8_label);
2875 iter = g_list_next (iter);
2876 val = val->next;
2877 ++pos;
2878 }
2879 else if (! val_in_menubar && iter_in_new_menubar)
2880 {
2881 /* This corresponds to:
2882 Current: A B C
2883 New: A X B C
2884 Insert X. */
2885
2886 int nr = pos;
2887 GSList *group = 0;
2888 GtkWidget *w = xg_create_one_menuitem (val,
2889 f,
2890 select_cb,
2891 highlight_cb,
2892 cl_data,
2893 &group);
2894
2895 /* Create a possibly empty submenu for menu bar items, since some
2896 themes don't highlight items correctly without it. */
2897 GtkWidget *submenu = create_menus (NULL, f,
2898 select_cb, deactivate_cb,
2899 highlight_cb,
2900 0, 0, 0, cl_data, 0);
2901
2902 gtk_widget_set_name (w, MENU_ITEM_NAME);
2903 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2904 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2905
2906 g_list_free (*list);
2907 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2908 while (nr-- > 0) iter = g_list_next (iter);
2909 iter = g_list_next (iter);
2910 val = val->next;
2911 ++pos;
2912 }
2913 else /* if (val_in_menubar && iter_in_new_menubar) */
2914 {
2915 int nr = pos;
2916 /* This corresponds to:
2917 Current: A B C
2918 New: A C B
2919 Move C before B */
2920
2921 g_object_ref (G_OBJECT (witem2));
2922 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2923 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2924 GTK_WIDGET (witem2), pos);
2925 g_object_unref (G_OBJECT (witem2));
2926
2927 g_list_free (*list);
2928 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2929 while (nr-- > 0) iter = g_list_next (iter);
2930 if (iter) iter = g_list_next (iter);
2931 val = val->next;
2932 ++pos;
2933 }
2934 }
2935
2936 /* Update the rest of the menu bar. */
2937 xg_update_menubar (menubar, f, list, iter, pos, val,
2938 select_cb, deactivate_cb, highlight_cb, cl_data);
2939 }
2940
2941 /* Update the menu item W so it corresponds to VAL.
2942 SELECT_CB is the callback to use when a menu item is selected.
2943 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2944 CL_DATA is the data to set in the widget for menu invocation. */
2945
2946 static void
2947 xg_update_menu_item (widget_value *val,
2948 GtkWidget *w,
2949 GCallback select_cb,
2950 GCallback highlight_cb,
2951 xg_menu_cb_data *cl_data)
2952 {
2953 GtkWidget *wchild;
2954 GtkLabel *wlbl = 0;
2955 GtkLabel *wkey = 0;
2956 char *utf8_label;
2957 char *utf8_key;
2958 const char *old_label = 0;
2959 const char *old_key = 0;
2960 xg_menu_item_cb_data *cb_data;
2961 bool label_changed = false;
2962
2963 wchild = XG_BIN_CHILD (w);
2964 utf8_label = get_utf8_string (val->name);
2965 utf8_key = get_utf8_string (val->key);
2966
2967 /* See if W is a menu item with a key. See make_menu_item above. */
2968 if (GTK_IS_BOX (wchild))
2969 {
2970 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2971
2972 wlbl = GTK_LABEL (list->data);
2973 wkey = GTK_LABEL (list->next->data);
2974 g_list_free (list);
2975
2976 if (! utf8_key)
2977 {
2978 /* Remove the key and keep just the label. */
2979 g_object_ref (G_OBJECT (wlbl));
2980 gtk_container_remove (GTK_CONTAINER (w), wchild);
2981 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
2982 g_object_unref (G_OBJECT (wlbl));
2983 wkey = 0;
2984 }
2985
2986 }
2987 else /* Just a label. */
2988 {
2989 wlbl = GTK_LABEL (wchild);
2990
2991 /* Check if there is now a key. */
2992 if (utf8_key)
2993 {
2994 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2995 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
2996
2997 wlbl = GTK_LABEL (list->data);
2998 wkey = GTK_LABEL (list->next->data);
2999 g_list_free (list);
3000
3001 gtk_container_remove (GTK_CONTAINER (w), wchild);
3002 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3003 }
3004 }
3005
3006 if (wkey) old_key = gtk_label_get_label (wkey);
3007 if (wlbl) old_label = gtk_label_get_label (wlbl);
3008
3009 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3010 {
3011 label_changed = true;
3012 gtk_label_set_text (wkey, utf8_key);
3013 }
3014
3015 if (! old_label || strcmp (utf8_label, old_label) != 0)
3016 {
3017 label_changed = true;
3018 gtk_label_set_text (wlbl, utf8_label);
3019 }
3020
3021 if (utf8_key) g_free (utf8_key);
3022 if (utf8_label) g_free (utf8_label);
3023
3024 if (! val->enabled && gtk_widget_get_sensitive (w))
3025 gtk_widget_set_sensitive (w, FALSE);
3026 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3027 gtk_widget_set_sensitive (w, TRUE);
3028
3029 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
3030 if (cb_data)
3031 {
3032 cb_data->call_data = val->call_data;
3033 cb_data->help = val->help;
3034 cb_data->cl_data = cl_data;
3035
3036 /* We assume the callback functions don't change. */
3037 if (val->call_data && ! val->contents)
3038 {
3039 /* This item shall have a select callback. */
3040 if (! cb_data->select_id)
3041 cb_data->select_id
3042 = g_signal_connect (G_OBJECT (w), "activate",
3043 select_cb, cb_data);
3044 }
3045 else if (cb_data->select_id)
3046 {
3047 g_signal_handler_disconnect (w, cb_data->select_id);
3048 cb_data->select_id = 0;
3049 }
3050 }
3051
3052 #if GTK_CHECK_VERSION (2, 16, 0)
3053 if (label_changed) /* See comment in xg_update_menubar. */
3054 g_object_notify (G_OBJECT (w), "label");
3055 #endif
3056 }
3057
3058 /* Update the toggle menu item W so it corresponds to VAL. */
3059
3060 static void
3061 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3062 {
3063 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3064 }
3065
3066 /* Update the radio menu item W so it corresponds to VAL. */
3067
3068 static void
3069 xg_update_radio_item (widget_value *val, GtkWidget *w)
3070 {
3071 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3072 }
3073
3074 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3075 SUBMENU may be NULL, in that case a new menu is created.
3076 F is the frame the menu bar belongs to.
3077 VAL describes the contents of the menu bar.
3078 SELECT_CB is the callback to use when a menu item is selected.
3079 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3080 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3081 CL_DATA is the call back data to use for any newly created items.
3082
3083 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3084 was NULL. */
3085
3086 static GtkWidget *
3087 xg_update_submenu (GtkWidget *submenu,
3088 struct frame *f,
3089 widget_value *val,
3090 GCallback select_cb,
3091 GCallback deactivate_cb,
3092 GCallback highlight_cb,
3093 xg_menu_cb_data *cl_data)
3094 {
3095 GtkWidget *newsub = submenu;
3096 GList *list = 0;
3097 GList *iter;
3098 widget_value *cur;
3099 GList *first_radio = 0;
3100
3101 if (submenu)
3102 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3103
3104 for (cur = val, iter = list;
3105 cur && iter;
3106 iter = g_list_next (iter), cur = cur->next)
3107 {
3108 GtkWidget *w = GTK_WIDGET (iter->data);
3109
3110 /* Remember first radio button in a group. If we get a mismatch in
3111 a radio group we must rebuild the whole group so that the connections
3112 in GTK becomes correct. */
3113 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3114 first_radio = iter;
3115 else if (cur->button_type != BUTTON_TYPE_RADIO
3116 && ! GTK_IS_RADIO_MENU_ITEM (w))
3117 first_radio = 0;
3118
3119 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3120 {
3121 if (! menu_separator_name_p (cur->name))
3122 break;
3123 }
3124 else if (GTK_IS_CHECK_MENU_ITEM (w))
3125 {
3126 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3127 break;
3128 xg_update_toggle_item (cur, w);
3129 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3130 }
3131 else if (GTK_IS_RADIO_MENU_ITEM (w))
3132 {
3133 if (cur->button_type != BUTTON_TYPE_RADIO)
3134 break;
3135 xg_update_radio_item (cur, w);
3136 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3137 }
3138 else if (GTK_IS_MENU_ITEM (w))
3139 {
3140 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3141 GtkWidget *sub;
3142
3143 if (cur->button_type != BUTTON_TYPE_NONE ||
3144 menu_separator_name_p (cur->name))
3145 break;
3146
3147 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3148
3149 sub = gtk_menu_item_get_submenu (witem);
3150 if (sub && ! cur->contents)
3151 {
3152 /* Not a submenu anymore. */
3153 g_object_ref (G_OBJECT (sub));
3154 remove_submenu (witem);
3155 gtk_widget_destroy (sub);
3156 }
3157 else if (cur->contents)
3158 {
3159 GtkWidget *nsub;
3160
3161 nsub = xg_update_submenu (sub, f, cur->contents,
3162 select_cb, deactivate_cb,
3163 highlight_cb, cl_data);
3164
3165 /* If this item just became a submenu, we must set it. */
3166 if (nsub != sub)
3167 gtk_menu_item_set_submenu (witem, nsub);
3168 }
3169 }
3170 else
3171 {
3172 /* Structural difference. Remove everything from here and down
3173 in SUBMENU. */
3174 break;
3175 }
3176 }
3177
3178 /* Remove widgets from first structural change. */
3179 if (iter)
3180 {
3181 /* If we are adding new menu items below, we must remove from
3182 first radio button so that radio groups become correct. */
3183 if (cur && first_radio) xg_destroy_widgets (first_radio);
3184 else xg_destroy_widgets (iter);
3185 }
3186
3187 if (cur)
3188 {
3189 /* More items added. Create them. */
3190 newsub = create_menus (cur,
3191 f,
3192 select_cb,
3193 deactivate_cb,
3194 highlight_cb,
3195 0,
3196 0,
3197 submenu,
3198 cl_data,
3199 0);
3200 }
3201
3202 if (list) g_list_free (list);
3203
3204 return newsub;
3205 }
3206
3207 /* Update the MENUBAR.
3208 F is the frame the menu bar belongs to.
3209 VAL describes the contents of the menu bar.
3210 If DEEP_P, rebuild all but the top level menu names in
3211 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3212 SELECT_CB is the callback to use when a menu item is selected.
3213 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3214 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3215
3216 void
3217 xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3218 widget_value *val, bool deep_p,
3219 GCallback select_cb, GCallback deactivate_cb,
3220 GCallback highlight_cb)
3221 {
3222 xg_menu_cb_data *cl_data;
3223 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3224
3225 if (! list) return;
3226
3227 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
3228
3229 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3230 select_cb, deactivate_cb, highlight_cb, cl_data);
3231
3232 if (deep_p)
3233 {
3234 widget_value *cur;
3235
3236 /* Update all sub menus.
3237 We must keep the submenus (GTK menu item widgets) since the
3238 X Window in the XEvent that activates the menu are those widgets. */
3239
3240 /* Update cl_data, menu_item things in F may have changed. */
3241 update_cl_data (cl_data, f, highlight_cb);
3242
3243 for (cur = val->contents; cur; cur = cur->next)
3244 {
3245 GList *iter;
3246 GtkWidget *sub = 0;
3247 GtkWidget *newsub;
3248 GtkMenuItem *witem = 0;
3249
3250 /* Find sub menu that corresponds to val and update it. */
3251 for (iter = list ; iter; iter = g_list_next (iter))
3252 {
3253 witem = GTK_MENU_ITEM (iter->data);
3254 if (xg_item_label_same_p (witem, cur->name))
3255 {
3256 sub = gtk_menu_item_get_submenu (witem);
3257 break;
3258 }
3259 }
3260
3261 newsub = xg_update_submenu (sub,
3262 f,
3263 cur->contents,
3264 select_cb,
3265 deactivate_cb,
3266 highlight_cb,
3267 cl_data);
3268 /* sub may still be NULL. If we just updated non deep and added
3269 a new menu bar item, it has no sub menu yet. So we set the
3270 newly created sub menu under witem. */
3271 if (newsub != sub && witem != 0)
3272 {
3273 xg_set_screen (newsub, f);
3274 gtk_menu_item_set_submenu (witem, newsub);
3275 }
3276 }
3277 }
3278
3279 g_list_free (list);
3280 gtk_widget_show_all (menubar);
3281 }
3282
3283 /* Callback called when the menu bar W is mapped.
3284 Used to find the height of the menu bar if we didn't get it
3285 after showing the widget. */
3286
3287 static void
3288 menubar_map_cb (GtkWidget *w, gpointer user_data)
3289 {
3290 GtkRequisition req;
3291 struct frame *f = user_data;
3292 gtk_widget_get_preferred_size (w, NULL, &req);
3293 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3294 {
3295 FRAME_MENUBAR_HEIGHT (f) = req.height;
3296 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3297 }
3298 }
3299
3300 /* Recompute all the widgets of frame F, when the menu bar has been
3301 changed. */
3302
3303 void
3304 xg_update_frame_menubar (struct frame *f)
3305 {
3306 struct x_output *x = f->output_data.x;
3307 GtkRequisition req;
3308
3309 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3310 return;
3311
3312 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3313 return; /* Already done this, happens for frames created invisible. */
3314
3315 block_input ();
3316
3317 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3318 FALSE, FALSE, 0);
3319 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3320
3321 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3322 gtk_widget_show_all (x->menubar_widget);
3323 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3324
3325 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3326 {
3327 FRAME_MENUBAR_HEIGHT (f) = req.height;
3328 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3329 }
3330 unblock_input ();
3331 }
3332
3333 /* Get rid of the menu bar of frame F, and free its storage.
3334 This is used when deleting a frame, and when turning off the menu bar. */
3335
3336 void
3337 free_frame_menubar (struct frame *f)
3338 {
3339 struct x_output *x = f->output_data.x;
3340
3341 if (x->menubar_widget)
3342 {
3343 block_input ();
3344
3345 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3346 /* The menubar and its children shall be deleted when removed from
3347 the container. */
3348 x->menubar_widget = 0;
3349 FRAME_MENUBAR_HEIGHT (f) = 0;
3350 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3351 unblock_input ();
3352 }
3353 }
3354
3355 bool
3356 xg_event_is_for_menubar (struct frame *f, const XEvent *event)
3357 {
3358 struct x_output *x = f->output_data.x;
3359 GList *iter;
3360 GdkRectangle rec;
3361 GList *list;
3362 GdkDisplay *gdpy;
3363 GdkWindow *gw;
3364 GdkEvent gevent;
3365 GtkWidget *gwdesc;
3366
3367 if (! x->menubar_widget) return 0;
3368
3369 if (! (event->xbutton.x >= 0
3370 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3371 && event->xbutton.y >= 0
3372 && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
3373 && event->xbutton.same_screen))
3374 return 0;
3375
3376 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3377 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3378 if (! gw) return 0;
3379 gevent.any.window = gw;
3380 gevent.any.type = GDK_NOTHING;
3381 gwdesc = gtk_get_event_widget (&gevent);
3382 if (! gwdesc) return 0;
3383 if (! GTK_IS_MENU_BAR (gwdesc)
3384 && ! GTK_IS_MENU_ITEM (gwdesc)
3385 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3386 return 0;
3387
3388 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3389 if (! list) return 0;
3390 rec.x = event->xbutton.x;
3391 rec.y = event->xbutton.y;
3392 rec.width = 1;
3393 rec.height = 1;
3394
3395 for (iter = list ; iter; iter = g_list_next (iter))
3396 {
3397 GtkWidget *w = GTK_WIDGET (iter->data);
3398 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3399 break;
3400 }
3401 g_list_free (list);
3402 return iter != 0;
3403 }
3404
3405
3406 \f
3407 /***********************************************************************
3408 Scroll bar functions
3409 ***********************************************************************/
3410
3411
3412 /* Setting scroll bar values invokes the callback. Use this variable
3413 to indicate that callback should do nothing. */
3414
3415 bool xg_ignore_gtk_scrollbar;
3416
3417 /* Width and height of scroll bars for the current theme. */
3418 static int scroll_bar_width_for_theme;
3419 static int scroll_bar_height_for_theme;
3420
3421 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3422 may be larger than 32 bits. Keep a mapping from integer index to widget
3423 pointers to get around the 32 bit limitation. */
3424
3425 static struct
3426 {
3427 GtkWidget **widgets;
3428 ptrdiff_t max_size;
3429 ptrdiff_t used;
3430 } id_to_widget;
3431
3432 /* Grow this much every time we need to allocate more */
3433
3434 #define ID_TO_WIDGET_INCR 32
3435
3436 /* Store the widget pointer W in id_to_widget and return the integer index. */
3437
3438 static ptrdiff_t
3439 xg_store_widget_in_map (GtkWidget *w)
3440 {
3441 ptrdiff_t i;
3442
3443 if (id_to_widget.max_size == id_to_widget.used)
3444 {
3445 ptrdiff_t new_size;
3446 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3447 memory_full (SIZE_MAX);
3448
3449 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3450 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3451 new_size, sizeof (GtkWidget *));
3452
3453 for (i = id_to_widget.max_size; i < new_size; ++i)
3454 id_to_widget.widgets[i] = 0;
3455 id_to_widget.max_size = new_size;
3456 }
3457
3458 /* Just loop over the array and find a free place. After all,
3459 how many scroll bars are we creating? Should be a small number.
3460 The check above guarantees we will find a free place. */
3461 for (i = 0; i < id_to_widget.max_size; ++i)
3462 {
3463 if (! id_to_widget.widgets[i])
3464 {
3465 id_to_widget.widgets[i] = w;
3466 ++id_to_widget.used;
3467
3468 return i;
3469 }
3470 }
3471
3472 /* Should never end up here */
3473 emacs_abort ();
3474 }
3475
3476 /* Remove pointer at IDX from id_to_widget.
3477 Called when scroll bar is destroyed. */
3478
3479 static void
3480 xg_remove_widget_from_map (ptrdiff_t idx)
3481 {
3482 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3483 {
3484 id_to_widget.widgets[idx] = 0;
3485 --id_to_widget.used;
3486 }
3487 }
3488
3489 /* Get the widget pointer at IDX from id_to_widget. */
3490
3491 static GtkWidget *
3492 xg_get_widget_from_map (ptrdiff_t idx)
3493 {
3494 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3495 return id_to_widget.widgets[idx];
3496
3497 return 0;
3498 }
3499
3500 static void
3501 update_theme_scrollbar_width (void)
3502 {
3503 #ifdef HAVE_GTK3
3504 GtkAdjustment *vadj;
3505 #else
3506 GtkObject *vadj;
3507 #endif
3508 GtkWidget *wscroll;
3509 int w = 0, b = 0;
3510
3511 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3512 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3513 g_object_ref_sink (G_OBJECT (wscroll));
3514 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3515 gtk_widget_destroy (wscroll);
3516 g_object_unref (G_OBJECT (wscroll));
3517 w += 2*b;
3518 #ifndef HAVE_GTK3
3519 if (w < 16) w = 16;
3520 #endif
3521 scroll_bar_width_for_theme = w;
3522 }
3523
3524 static void
3525 update_theme_scrollbar_height (void)
3526 {
3527 #ifdef HAVE_GTK3
3528 GtkAdjustment *hadj;
3529 #else
3530 GtkObject *hadj;
3531 #endif
3532 GtkWidget *wscroll;
3533 int w = 0, b = 0;
3534
3535 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
3536 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3537 g_object_ref_sink (G_OBJECT (wscroll));
3538 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3539 gtk_widget_destroy (wscroll);
3540 g_object_unref (G_OBJECT (wscroll));
3541 w += 2*b;
3542 if (w < 12) w = 12;
3543 scroll_bar_height_for_theme = w;
3544 }
3545
3546 int
3547 xg_get_default_scrollbar_width (void)
3548 {
3549 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3550 }
3551
3552 int
3553 xg_get_default_scrollbar_height (void)
3554 {
3555 /* Apparently there's no default height for themes. */
3556 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3557 }
3558
3559 /* Return the scrollbar id for X Window WID on display DPY.
3560 Return -1 if WID not in id_to_widget. */
3561
3562 ptrdiff_t
3563 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3564 {
3565 ptrdiff_t idx;
3566 GtkWidget *w;
3567
3568 w = xg_win_to_widget (dpy, wid);
3569
3570 if (w)
3571 {
3572 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3573 if (id_to_widget.widgets[idx] == w)
3574 return idx;
3575 }
3576
3577 return -1;
3578 }
3579
3580 /* Callback invoked when scroll bar WIDGET is destroyed.
3581 DATA is the index into id_to_widget for WIDGET.
3582 We free pointer to last scroll bar values here and remove the index. */
3583
3584 static void
3585 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3586 {
3587 intptr_t id = (intptr_t) data;
3588 xg_remove_widget_from_map (id);
3589 }
3590
3591 /* Create a scroll bar widget for frame F. Store the scroll bar
3592 in BAR.
3593 SCROLL_CALLBACK is the callback to invoke when the value of the
3594 bar changes.
3595 END_CALLBACK is the callback to invoke when scrolling ends.
3596 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3597 to set resources for the widget. */
3598
3599 void
3600 xg_create_scroll_bar (struct frame *f,
3601 struct scroll_bar *bar,
3602 GCallback scroll_callback,
3603 GCallback end_callback,
3604 const char *scroll_bar_name)
3605 {
3606 GtkWidget *wscroll;
3607 GtkWidget *webox;
3608 intptr_t scroll_id;
3609 #ifdef HAVE_GTK3
3610 GtkAdjustment *vadj;
3611 #else
3612 GtkObject *vadj;
3613 #endif
3614
3615 /* Page, step increment values are not so important here, they
3616 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3617 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3618 0.1, 0.1, 0.1);
3619
3620 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3621 webox = gtk_event_box_new ();
3622 gtk_widget_set_name (wscroll, scroll_bar_name);
3623 #ifndef HAVE_GTK3
3624 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3625 #endif
3626 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3627
3628 scroll_id = xg_store_widget_in_map (wscroll);
3629
3630 g_signal_connect (G_OBJECT (wscroll),
3631 "destroy",
3632 G_CALLBACK (xg_gtk_scroll_destroy),
3633 (gpointer) scroll_id);
3634 g_signal_connect (G_OBJECT (wscroll),
3635 "change-value",
3636 scroll_callback,
3637 (gpointer) bar);
3638 g_signal_connect (G_OBJECT (wscroll),
3639 "button-release-event",
3640 end_callback,
3641 (gpointer) bar);
3642
3643 /* The scroll bar widget does not draw on a window of its own. Instead
3644 it draws on the parent window, in this case the edit widget. So
3645 whenever the edit widget is cleared, the scroll bar needs to redraw
3646 also, which causes flicker. Put an event box between the edit widget
3647 and the scroll bar, so the scroll bar instead draws itself on the
3648 event box window. */
3649 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3650 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3651
3652
3653 /* Set the cursor to an arrow. */
3654 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3655
3656 bar->x_window = scroll_id;
3657 bar->horizontal = 0;
3658 }
3659
3660 /* Create a horizontal scroll bar widget for frame F. Store the scroll
3661 bar in BAR. SCROLL_CALLBACK is the callback to invoke when the value
3662 of the bar changes. END_CALLBACK is the callback to invoke when
3663 scrolling ends. SCROLL_BAR_NAME is the name we use for the scroll
3664 bar. Can be used to set resources for the widget. */
3665
3666 void
3667 xg_create_horizontal_scroll_bar (struct frame *f,
3668 struct scroll_bar *bar,
3669 GCallback scroll_callback,
3670 GCallback end_callback,
3671 const char *scroll_bar_name)
3672 {
3673 GtkWidget *wscroll;
3674 GtkWidget *webox;
3675 intptr_t scroll_id;
3676 #ifdef HAVE_GTK3
3677 GtkAdjustment *hadj;
3678 #else
3679 GtkObject *hadj;
3680 #endif
3681
3682 /* Page, step increment values are not so important here, they
3683 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3684 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
3685 0.1, 0.1, 0.1);
3686
3687 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3688 webox = gtk_event_box_new ();
3689 gtk_widget_set_name (wscroll, scroll_bar_name);
3690 #ifndef HAVE_GTK3
3691 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3692 #endif
3693 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3694
3695 scroll_id = xg_store_widget_in_map (wscroll);
3696
3697 g_signal_connect (G_OBJECT (wscroll),
3698 "destroy",
3699 G_CALLBACK (xg_gtk_scroll_destroy),
3700 (gpointer) scroll_id);
3701 g_signal_connect (G_OBJECT (wscroll),
3702 "change-value",
3703 scroll_callback,
3704 (gpointer) bar);
3705 g_signal_connect (G_OBJECT (wscroll),
3706 "button-release-event",
3707 end_callback,
3708 (gpointer) bar);
3709
3710 /* The scroll bar widget does not draw on a window of its own. Instead
3711 it draws on the parent window, in this case the edit widget. So
3712 whenever the edit widget is cleared, the scroll bar needs to redraw
3713 also, which causes flicker. Put an event box between the edit widget
3714 and the scroll bar, so the scroll bar instead draws itself on the
3715 event box window. */
3716 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3717 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3718
3719
3720 /* Set the cursor to an arrow. */
3721 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3722
3723 bar->x_window = scroll_id;
3724 bar->horizontal = 1;
3725 }
3726
3727 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3728
3729 void
3730 xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
3731 {
3732 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3733 if (w)
3734 {
3735 GtkWidget *wparent = gtk_widget_get_parent (w);
3736 gtk_widget_destroy (w);
3737 gtk_widget_destroy (wparent);
3738 SET_FRAME_GARBAGED (f);
3739 }
3740 }
3741
3742 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3743 in frame F.
3744 TOP/LEFT are the new pixel positions where the bar shall appear.
3745 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3746
3747 void
3748 xg_update_scrollbar_pos (struct frame *f,
3749 ptrdiff_t scrollbar_id,
3750 int top,
3751 int left,
3752 int width,
3753 int height)
3754 {
3755 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3756 if (wscroll)
3757 {
3758 GtkWidget *wfixed = f->output_data.x->edit_widget;
3759 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3760 gint msl;
3761 int scale = xg_get_gdk_scale ();
3762
3763 top /= scale;
3764 left /= scale;
3765 height /= scale;
3766 left -= (scale - 1) * ((width / scale) >> 1);
3767
3768 /* Clear out old position. */
3769 int oldx = -1, oldy = -1, oldw, oldh;
3770 if (gtk_widget_get_parent (wparent) == wfixed)
3771 {
3772 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3773 "x", &oldx, "y", &oldy, NULL);
3774 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3775 }
3776
3777 /* Move and resize to new values. */
3778 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3779 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3780 if (msl > height)
3781 {
3782 /* No room. Hide scroll bar as some themes output a warning if
3783 the height is less than the min size. */
3784 gtk_widget_hide (wparent);
3785 gtk_widget_hide (wscroll);
3786 }
3787 else
3788 {
3789 gtk_widget_show_all (wparent);
3790 gtk_widget_set_size_request (wscroll, width, height);
3791 }
3792 #ifndef USE_CAIRO
3793 gtk_widget_queue_draw (wfixed);
3794 gdk_window_process_all_updates ();
3795 #endif
3796 if (oldx != -1 && oldw > 0 && oldh > 0)
3797 {
3798 /* Clear under old scroll bar position. This must be done after
3799 the gtk_widget_queue_draw and gdk_window_process_all_updates
3800 above. */
3801 oldw += (scale - 1) * oldw;
3802 oldx -= (scale - 1) * oldw;
3803 x_clear_area (f, oldx, oldy, oldw, oldh);
3804 }
3805
3806 /* GTK does not redraw until the main loop is entered again, but
3807 if there are no X events pending we will not enter it. So we sync
3808 here to get some events. */
3809
3810 x_sync (f);
3811 SET_FRAME_GARBAGED (f);
3812 cancel_mouse_face (f);
3813 }
3814 }
3815
3816
3817 /* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
3818 in frame F.
3819 TOP/LEFT are the new pixel positions where the bar shall appear.
3820 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3821
3822 void
3823 xg_update_horizontal_scrollbar_pos (struct frame *f,
3824 ptrdiff_t scrollbar_id,
3825 int top,
3826 int left,
3827 int width,
3828 int height)
3829 {
3830
3831 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3832
3833 if (wscroll)
3834 {
3835 GtkWidget *wfixed = f->output_data.x->edit_widget;
3836 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3837 gint msl;
3838
3839 /* Clear out old position. */
3840 int oldx = -1, oldy = -1, oldw, oldh;
3841 if (gtk_widget_get_parent (wparent) == wfixed)
3842 {
3843 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3844 "x", &oldx, "y", &oldy, NULL);
3845 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3846 }
3847
3848 /* Move and resize to new values. */
3849 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3850 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3851 if (msl > width)
3852 {
3853 /* No room. Hide scroll bar as some themes output a warning if
3854 the width is less than the min size. */
3855 gtk_widget_hide (wparent);
3856 gtk_widget_hide (wscroll);
3857 }
3858 else
3859 {
3860 gtk_widget_show_all (wparent);
3861 gtk_widget_set_size_request (wscroll, width, height);
3862 }
3863 gtk_widget_queue_draw (wfixed);
3864 gdk_window_process_all_updates ();
3865 if (oldx != -1 && oldw > 0 && oldh > 0)
3866 /* Clear under old scroll bar position. This must be done after
3867 the gtk_widget_queue_draw and gdk_window_process_all_updates
3868 above. */
3869 x_clear_area (f,
3870 oldx, oldy, oldw, oldh);
3871
3872 /* GTK does not redraw until the main loop is entered again, but
3873 if there are no X events pending we will not enter it. So we sync
3874 here to get some events. */
3875
3876 x_sync (f);
3877 SET_FRAME_GARBAGED (f);
3878 cancel_mouse_face (f);
3879 }
3880 }
3881
3882
3883 /* Get the current value of the range, truncated to an integer. */
3884
3885 static int
3886 int_gtk_range_get_value (GtkRange *range)
3887 {
3888 return gtk_range_get_value (range);
3889 }
3890
3891
3892 /* Set the thumb size and position of scroll bar BAR. We are currently
3893 displaying PORTION out of a whole WHOLE, and our position POSITION. */
3894
3895 void
3896 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
3897 int portion,
3898 int position,
3899 int whole)
3900 {
3901 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3902
3903 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
3904
3905 if (wscroll && bar->dragging == -1)
3906 {
3907 GtkAdjustment *adj;
3908 gdouble shown;
3909 gdouble top;
3910 int size, value;
3911 int old_size;
3912 int new_step;
3913 bool changed = 0;
3914
3915 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3916
3917 if (scroll_bar_adjust_thumb_portion_p)
3918 {
3919 /* We do the same as for MOTIF in xterm.c, use 30 chars per
3920 line rather than the real portion value. This makes the
3921 thumb less likely to resize and that looks better. */
3922 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
3923
3924 /* When the thumb is at the bottom, position == whole.
3925 So we need to increase `whole' to make space for the thumb. */
3926 whole += portion;
3927 }
3928
3929 if (whole <= 0)
3930 top = 0, shown = 1;
3931 else
3932 {
3933 top = (gdouble) position / whole;
3934 shown = (gdouble) portion / whole;
3935 }
3936
3937 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
3938 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
3939
3940 /* Assume all lines are of equal size. */
3941 new_step = size / max (1, FRAME_LINES (f));
3942
3943 old_size = gtk_adjustment_get_page_size (adj);
3944 if (old_size != size)
3945 {
3946 int old_step = gtk_adjustment_get_step_increment (adj);
3947 if (old_step != new_step)
3948 {
3949 gtk_adjustment_set_page_size (adj, size);
3950 gtk_adjustment_set_step_increment (adj, new_step);
3951 /* Assume a page increment is about 95% of the page size */
3952 gtk_adjustment_set_page_increment (adj, size - size / 20);
3953 changed = 1;
3954 }
3955 }
3956
3957 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3958 {
3959 block_input ();
3960
3961 /* gtk_range_set_value invokes the callback. Set
3962 ignore_gtk_scrollbar to make the callback do nothing */
3963 xg_ignore_gtk_scrollbar = 1;
3964
3965 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3966 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3967 else if (changed)
3968 gtk_adjustment_changed (adj);
3969
3970 xg_ignore_gtk_scrollbar = 0;
3971
3972 unblock_input ();
3973 }
3974 }
3975 }
3976
3977 /* Set the thumb size and position of horizontal scroll bar BAR. We are
3978 currently displaying PORTION out of a whole WHOLE, and our position
3979 POSITION. */
3980 void
3981 xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
3982 int portion,
3983 int position,
3984 int whole)
3985 {
3986 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3987
3988 if (wscroll && bar->dragging == -1)
3989 {
3990 GtkAdjustment *adj;
3991 int lower = 0;
3992 int upper = max (whole - 1, 0);
3993 int pagesize = min (upper, max (portion, 0));
3994 int value = max (0, min (position, upper - pagesize));
3995 /* These should be set to something more <portion, whole>
3996 related. */
3997 int page_increment = 4;
3998 int step_increment = 1;
3999
4000 block_input ();
4001 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4002 gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
4003 (gdouble) upper, (gdouble) step_increment,
4004 (gdouble) page_increment, (gdouble) pagesize);
4005 gtk_adjustment_changed (adj);
4006 unblock_input ();
4007 }
4008 }
4009
4010 /* Return true if EVENT is for a scroll bar in frame F.
4011 When the same X window is used for several Gtk+ widgets, we cannot
4012 say for sure based on the X window alone if an event is for the
4013 frame. This function does additional checks. */
4014
4015 bool
4016 xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
4017 {
4018 bool retval = 0;
4019
4020 if (f && event->type == ButtonPress && event->xbutton.button < 4)
4021 {
4022 /* Check if press occurred outside the edit widget. */
4023 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
4024 GdkWindow *gwin;
4025 #ifdef HAVE_GTK3
4026 GdkDevice *gdev = gdk_device_manager_get_client_pointer
4027 (gdk_display_get_device_manager (gdpy));
4028 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
4029 #else
4030 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
4031 #endif
4032 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
4033 }
4034 else if (f
4035 && ((event->type == ButtonRelease && event->xbutton.button < 4)
4036 || event->type == MotionNotify))
4037 {
4038 /* If we are releasing or moving the scroll bar, it has the grab. */
4039 GtkWidget *w = gtk_grab_get_current ();
4040 retval = w != 0 && GTK_IS_SCROLLBAR (w);
4041 }
4042
4043 return retval;
4044 }
4045
4046 \f
4047 /***********************************************************************
4048 Printing
4049 ***********************************************************************/
4050 #ifdef USE_CAIRO
4051 static GtkPrintSettings *print_settings = NULL;
4052 static GtkPageSetup *page_setup = NULL;
4053
4054 void
4055 xg_page_setup_dialog (void)
4056 {
4057 GtkPageSetup *new_page_setup = NULL;
4058
4059 if (print_settings == NULL)
4060 print_settings = gtk_print_settings_new ();
4061 new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
4062 print_settings);
4063 if (page_setup)
4064 g_object_unref (page_setup);
4065 page_setup = new_page_setup;
4066 }
4067
4068 Lisp_Object
4069 xg_get_page_setup (void)
4070 {
4071 Lisp_Object orientation_symbol;
4072
4073 if (page_setup == NULL)
4074 page_setup = gtk_page_setup_new ();
4075
4076 switch (gtk_page_setup_get_orientation (page_setup))
4077 {
4078 case GTK_PAGE_ORIENTATION_PORTRAIT:
4079 orientation_symbol = Qportrait;
4080 break;
4081 case GTK_PAGE_ORIENTATION_LANDSCAPE:
4082 orientation_symbol = Qlandscape;
4083 break;
4084 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4085 orientation_symbol = Qreverse_portrait;
4086 break;
4087 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4088 orientation_symbol = Qreverse_landscape;
4089 break;
4090 default:
4091 eassume (false);
4092 }
4093
4094 return listn (CONSTYPE_HEAP, 7,
4095 Fcons (Qorientation, orientation_symbol),
4096 #define MAKE_FLOAT_PAGE_SETUP(f) make_float (f (page_setup, GTK_UNIT_POINTS))
4097 Fcons (Qwidth,
4098 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_width)),
4099 Fcons (Qheight,
4100 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_height)),
4101 Fcons (Qleft_margin,
4102 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_left_margin)),
4103 Fcons (Qright_margin,
4104 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_right_margin)),
4105 Fcons (Qtop_margin,
4106 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_top_margin)),
4107 Fcons (Qbottom_margin,
4108 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_bottom_margin))
4109 #undef MAKE_FLOAT_PAGE_SETUP
4110 );
4111 }
4112
4113 static void
4114 draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
4115 gint page_nr, gpointer user_data)
4116 {
4117 Lisp_Object frames = *((Lisp_Object *) user_data);
4118 struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
4119 cairo_t *cr = gtk_print_context_get_cairo_context (context);
4120
4121 x_cr_draw_frame (cr, f);
4122 }
4123
4124 void
4125 xg_print_frames_dialog (Lisp_Object frames)
4126 {
4127 GtkPrintOperation *print;
4128 GtkPrintOperationResult res;
4129
4130 print = gtk_print_operation_new ();
4131 if (print_settings != NULL)
4132 gtk_print_operation_set_print_settings (print, print_settings);
4133 if (page_setup != NULL)
4134 gtk_print_operation_set_default_page_setup (print, page_setup);
4135 gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
4136 g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
4137 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
4138 NULL, NULL);
4139 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
4140 {
4141 if (print_settings != NULL)
4142 g_object_unref (print_settings);
4143 print_settings =
4144 g_object_ref (gtk_print_operation_get_print_settings (print));
4145 }
4146 g_object_unref (print);
4147 }
4148
4149 #endif /* USE_CAIRO */
4150
4151
4152 \f
4153 /***********************************************************************
4154 Tool bar functions
4155 ***********************************************************************/
4156 /* The key for the data we put in the GtkImage widgets. The data is
4157 the image used by Emacs. We use this to see if we need to update
4158 the GtkImage with a new image. */
4159 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
4160
4161 /* The key for storing the latest modifiers so the activate callback can
4162 get them. */
4163 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
4164
4165 /* The key for the data we put in the GtkImage widgets. The data is
4166 the stock name used by Emacs. We use this to see if we need to update
4167 the GtkImage with a new image. */
4168 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
4169
4170 /* As above, but this is used for named theme widgets, as opposed to
4171 stock items. */
4172 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
4173
4174 /* Callback function invoked when a tool bar item is pressed.
4175 W is the button widget in the tool bar that got pressed,
4176 CLIENT_DATA is an integer that is the index of the button in the
4177 tool bar. 0 is the first button. */
4178
4179 static gboolean
4180 xg_tool_bar_button_cb (GtkWidget *widget,
4181 GdkEventButton *event,
4182 gpointer user_data)
4183 {
4184 intptr_t state = event->state;
4185 gpointer ptr = (gpointer) state;
4186 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
4187 return FALSE;
4188 }
4189
4190
4191 /* Callback function invoked when a tool bar item is pressed.
4192 W is the button widget in the tool bar that got pressed,
4193 CLIENT_DATA is an integer that is the index of the button in the
4194 tool bar. 0 is the first button. */
4195
4196 static void
4197 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
4198 {
4199 intptr_t idx = (intptr_t) client_data;
4200 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
4201 intptr_t mod = (intptr_t) gmod;
4202
4203 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4204 Lisp_Object key, frame;
4205 struct input_event event;
4206 EVENT_INIT (event);
4207
4208 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4209 return;
4210
4211 idx *= TOOL_BAR_ITEM_NSLOTS;
4212
4213 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
4214 XSETFRAME (frame, f);
4215
4216 /* We generate two events here. The first one is to set the prefix
4217 to `(tool_bar)', see keyboard.c. */
4218 event.kind = TOOL_BAR_EVENT;
4219 event.frame_or_window = frame;
4220 event.arg = frame;
4221 kbd_buffer_store_event (&event);
4222
4223 event.kind = TOOL_BAR_EVENT;
4224 event.frame_or_window = frame;
4225 event.arg = key;
4226 /* Convert between the modifier bits GDK uses and the modifier bits
4227 Emacs uses. This assumes GDK and X masks are the same, which they are when
4228 this is written. */
4229 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
4230 kbd_buffer_store_event (&event);
4231
4232 /* Return focus to the frame after we have clicked on a detached
4233 tool bar button. */
4234 x_focus_frame (f);
4235 }
4236
4237 static GtkWidget *
4238 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4239 {
4240 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
4241 GtkWidget *c1 = clist->data;
4242 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
4243
4244 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4245 g_list_free (clist);
4246 return GTK_IS_LABEL (c1) ? c1 : c2;
4247 }
4248
4249
4250 /* This callback is called when the mouse enters or leaves a tool bar item.
4251 It is used for displaying and hiding the help text.
4252 W is the tool bar item, a button.
4253 EVENT is either an enter event or leave event.
4254 CLIENT_DATA is an integer that is the index of the button in the
4255 tool bar. 0 is the first button.
4256
4257 Returns FALSE to tell GTK to keep processing this event. */
4258
4259 static gboolean
4260 xg_tool_bar_help_callback (GtkWidget *w,
4261 GdkEventCrossing *event,
4262 gpointer client_data)
4263 {
4264 intptr_t idx = (intptr_t) client_data;
4265 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4266 Lisp_Object help, frame;
4267
4268 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4269 return FALSE;
4270
4271 if (event->type == GDK_ENTER_NOTIFY)
4272 {
4273 idx *= TOOL_BAR_ITEM_NSLOTS;
4274 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4275
4276 if (NILP (help))
4277 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4278 }
4279 else
4280 help = Qnil;
4281
4282 XSETFRAME (frame, f);
4283 kbd_buffer_store_help_event (frame, help);
4284
4285 return FALSE;
4286 }
4287
4288
4289 /* This callback is called when a tool bar item shall be redrawn.
4290 It modifies the expose event so that the GtkImage widget redraws the
4291 whole image. This to overcome a bug that makes GtkImage draw the image
4292 in the wrong place when it tries to redraw just a part of the image.
4293 W is the GtkImage to be redrawn.
4294 EVENT is the expose event for W.
4295 CLIENT_DATA is unused.
4296
4297 Returns FALSE to tell GTK to keep processing this event. */
4298
4299 #ifndef HAVE_GTK3
4300 static gboolean
4301 xg_tool_bar_item_expose_callback (GtkWidget *w,
4302 GdkEventExpose *event,
4303 gpointer client_data)
4304 {
4305 gint width, height;
4306
4307 gdk_drawable_get_size (event->window, &width, &height);
4308 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4309 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4310
4311 event->area.x = max (0, event->area.x);
4312 event->area.y = max (0, event->area.y);
4313
4314 event->area.width = max (width, event->area.width);
4315 event->area.height = max (height, event->area.height);
4316
4317 return FALSE;
4318 }
4319 #endif
4320
4321 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4322 #define toolbar_set_orientation(w, o) \
4323 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4324 #else
4325 #define toolbar_set_orientation(w, o) \
4326 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4327 #endif
4328
4329 /* Attach a tool bar to frame F. */
4330
4331 static void
4332 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
4333 {
4334 struct x_output *x = f->output_data.x;
4335 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4336 GtkWidget *top_widget = x->toolbar_widget;
4337
4338 toolbar_set_orientation (x->toolbar_widget,
4339 into_hbox
4340 ? GTK_ORIENTATION_VERTICAL
4341 : GTK_ORIENTATION_HORIZONTAL);
4342
4343 if (into_hbox)
4344 {
4345 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
4346 FALSE, FALSE, 0);
4347
4348 if (EQ (pos, Qleft))
4349 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4350 top_widget,
4351 0);
4352 x->toolbar_in_hbox = true;
4353 }
4354 else
4355 {
4356 bool vbox_pos = x->menubar_widget != 0;
4357 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
4358 FALSE, FALSE, 0);
4359
4360 if (EQ (pos, Qtop))
4361 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4362 top_widget,
4363 vbox_pos);
4364 x->toolbar_in_hbox = false;
4365 }
4366 x->toolbar_is_packed = true;
4367 }
4368
4369 static bool xg_update_tool_bar_sizes (struct frame *f);
4370
4371 static void
4372 tb_size_cb (GtkWidget *widget,
4373 GdkRectangle *allocation,
4374 gpointer user_data)
4375 {
4376 /* When tool bar is created it has one preferred size. But when size is
4377 allocated between widgets, it may get another. So we must update
4378 size hints if tool bar size changes. Seen on Fedora 18 at least. */
4379 struct frame *f = user_data;
4380
4381 if (xg_update_tool_bar_sizes (f))
4382 {
4383 frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
4384 adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
4385 }
4386 }
4387
4388 /* Create a tool bar for frame F. */
4389
4390 static void
4391 xg_create_tool_bar (struct frame *f)
4392 {
4393 struct x_output *x = f->output_data.x;
4394 #if GTK_CHECK_VERSION (3, 3, 6)
4395 GtkStyleContext *gsty;
4396 #endif
4397 struct xg_frame_tb_info *tbinfo
4398 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4399 TB_INFO_KEY);
4400 if (! tbinfo)
4401 {
4402 tbinfo = xmalloc (sizeof (*tbinfo));
4403 tbinfo->last_tool_bar = Qnil;
4404 tbinfo->style = Qnil;
4405 tbinfo->hmargin = tbinfo->vmargin = 0;
4406 tbinfo->dir = GTK_TEXT_DIR_NONE;
4407 tbinfo->n_last_items = 0;
4408 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4409 TB_INFO_KEY,
4410 tbinfo);
4411 }
4412
4413 x->toolbar_widget = gtk_toolbar_new ();
4414
4415 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4416
4417 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4418 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4419 g_signal_connect (x->toolbar_widget, "size-allocate",
4420 G_CALLBACK (tb_size_cb), f);
4421 #if GTK_CHECK_VERSION (3, 3, 6)
4422 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4423 gtk_style_context_add_class (gsty, "primary-toolbar");
4424 #endif
4425 }
4426
4427
4428 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4429
4430 /* Find the right-to-left image named by RTL in the tool bar images for F.
4431 Returns IMAGE if RTL is not found. */
4432
4433 static Lisp_Object
4434 find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
4435 {
4436 int i;
4437 Lisp_Object file, rtl_name;
4438
4439 rtl_name = Ffile_name_nondirectory (rtl);
4440
4441 for (i = 0; i < f->n_tool_bar_items; ++i)
4442 {
4443 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4444 if (!NILP (file = file_for_image (rtl_image)))
4445 {
4446 file = call1 (intern ("file-name-sans-extension"),
4447 Ffile_name_nondirectory (file));
4448 if (! NILP (Fequal (file, rtl_name)))
4449 {
4450 image = rtl_image;
4451 break;
4452 }
4453 }
4454 }
4455
4456 return image;
4457 }
4458
4459 static GtkToolItem *
4460 xg_make_tool_item (struct frame *f,
4461 GtkWidget *wimage,
4462 GtkWidget **wbutton,
4463 const char *label,
4464 int i, bool horiz, bool text_image)
4465 {
4466 GtkToolItem *ti = gtk_tool_item_new ();
4467 GtkWidget *vb = gtk_box_new (horiz
4468 ? GTK_ORIENTATION_HORIZONTAL
4469 : GTK_ORIENTATION_VERTICAL,
4470 0);
4471 GtkWidget *wb = gtk_button_new ();
4472 /* The eventbox is here so we can have tooltips on disabled items. */
4473 GtkWidget *weventbox = gtk_event_box_new ();
4474 #if GTK_CHECK_VERSION (3, 3, 6)
4475 GtkCssProvider *css_prov = gtk_css_provider_new ();
4476 GtkStyleContext *gsty;
4477
4478 gtk_css_provider_load_from_data (css_prov,
4479 "GtkEventBox {"
4480 " background-color: transparent;"
4481 "}",
4482 -1, NULL);
4483
4484 gsty = gtk_widget_get_style_context (weventbox);
4485 gtk_style_context_add_provider (gsty,
4486 GTK_STYLE_PROVIDER (css_prov),
4487 GTK_STYLE_PROVIDER_PRIORITY_USER);
4488 g_object_unref (css_prov);
4489 #endif
4490
4491 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4492
4493 if (wimage && !text_image)
4494 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4495 if (label)
4496 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4497 if (wimage && text_image)
4498 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4499
4500 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4501 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4502 gtk_container_add (GTK_CONTAINER (wb), vb);
4503 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4504 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4505
4506 if (wimage || label)
4507 {
4508 intptr_t ii = i;
4509 gpointer gi = (gpointer) ii;
4510
4511 g_signal_connect (G_OBJECT (wb), "clicked",
4512 G_CALLBACK (xg_tool_bar_callback),
4513 gi);
4514
4515 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4516
4517 #ifndef HAVE_GTK3
4518 /* Catch expose events to overcome an annoying redraw bug, see
4519 comment for xg_tool_bar_item_expose_callback. */
4520 g_signal_connect (G_OBJECT (ti),
4521 "expose-event",
4522 G_CALLBACK (xg_tool_bar_item_expose_callback),
4523 0);
4524 #endif
4525 gtk_tool_item_set_homogeneous (ti, FALSE);
4526
4527 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4528 no distinction based on modifiers in the activate callback,
4529 so we have to do it ourselves. */
4530 g_signal_connect (wb, "button-release-event",
4531 G_CALLBACK (xg_tool_bar_button_cb),
4532 NULL);
4533
4534 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4535
4536 /* Use enter/leave notify to show help. We use the events
4537 rather than the GtkButton specific signals "enter" and
4538 "leave", so we can have only one callback. The event
4539 will tell us what kind of event it is. */
4540 g_signal_connect (G_OBJECT (weventbox),
4541 "enter-notify-event",
4542 G_CALLBACK (xg_tool_bar_help_callback),
4543 gi);
4544 g_signal_connect (G_OBJECT (weventbox),
4545 "leave-notify-event",
4546 G_CALLBACK (xg_tool_bar_help_callback),
4547 gi);
4548 }
4549
4550 if (wbutton) *wbutton = wb;
4551
4552 return ti;
4553 }
4554
4555 static bool
4556 is_box_type (GtkWidget *vb, bool is_horizontal)
4557 {
4558 #ifdef HAVE_GTK3
4559 bool ret = 0;
4560 if (GTK_IS_BOX (vb))
4561 {
4562 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4563 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4564 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4565 }
4566 return ret;
4567 #else
4568 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4569 #endif
4570 }
4571
4572
4573 static bool
4574 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4575 const char *icon_name, const struct image *img,
4576 const char *label, bool horiz)
4577 {
4578 gpointer old;
4579 GtkWidget *wimage;
4580 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4581 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4582
4583 /* Check if the tool icon matches. */
4584 if (stock_name && wimage)
4585 {
4586 old = g_object_get_data (G_OBJECT (wimage),
4587 XG_TOOL_BAR_STOCK_NAME);
4588 if (!old || strcmp (old, stock_name))
4589 return 1;
4590 }
4591 else if (icon_name && wimage)
4592 {
4593 old = g_object_get_data (G_OBJECT (wimage),
4594 XG_TOOL_BAR_ICON_NAME);
4595 if (!old || strcmp (old, icon_name))
4596 return 1;
4597 }
4598 else if (wimage)
4599 {
4600 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4601 XG_TOOL_BAR_IMAGE_DATA);
4602 Pixmap old_img = (Pixmap) gold_img;
4603 if (old_img != img->pixmap)
4604 return 1;
4605 }
4606
4607 /* Check button configuration and label. */
4608 if (is_box_type (vb, horiz)
4609 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4610 return 1;
4611
4612 /* Ensure label is correct. */
4613 if (label && wlbl)
4614 gtk_label_set_text (GTK_LABEL (wlbl), label);
4615 return 0;
4616 }
4617
4618 static bool
4619 xg_update_tool_bar_sizes (struct frame *f)
4620 {
4621 struct x_output *x = f->output_data.x;
4622 GtkRequisition req;
4623 int nl = 0, nr = 0, nt = 0, nb = 0;
4624 GtkWidget *top_widget = x->toolbar_widget;
4625
4626 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
4627 if (x->toolbar_in_hbox)
4628 {
4629 int pos;
4630 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4631 top_widget,
4632 "position", &pos, NULL);
4633 if (pos == 0) nl = req.width;
4634 else nr = req.width;
4635 }
4636 else
4637 {
4638 int pos;
4639 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4640 top_widget,
4641 "position", &pos, NULL);
4642 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4643 else nb = req.height;
4644 }
4645
4646 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4647 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4648 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4649 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4650 {
4651 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4652 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4653 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4654 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4655 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4656 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4657
4658 return true;
4659 }
4660 else
4661 return false;
4662 }
4663
4664 static char *
4665 find_icon_from_name (char *name,
4666 GtkIconTheme *icon_theme,
4667 char **icon_name)
4668 {
4669 #if ! GTK_CHECK_VERSION (3, 10, 0)
4670 GtkStockItem stock_item;
4671 #endif
4672
4673 if (name[0] == 'n' && name[1] == ':')
4674 {
4675 *icon_name = name + 2;
4676 name = NULL;
4677
4678 if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
4679 *icon_name = NULL;
4680 }
4681
4682 #if ! GTK_CHECK_VERSION (3, 10, 0)
4683 else if (gtk_stock_lookup (name, &stock_item))
4684 *icon_name = NULL;
4685 #endif
4686 else if (gtk_icon_theme_has_icon (icon_theme, name))
4687 {
4688 *icon_name = name;
4689 name = NULL;
4690 }
4691 else
4692 {
4693 name = NULL;
4694 *icon_name = NULL;
4695 }
4696
4697 return name;
4698 }
4699
4700
4701 /* Update the tool bar for frame F. Add new buttons and remove old. */
4702
4703 void
4704 update_frame_tool_bar (struct frame *f)
4705 {
4706 int i, j;
4707 struct x_output *x = f->output_data.x;
4708 int hmargin = 0, vmargin = 0;
4709 GtkToolbar *wtoolbar;
4710 GtkToolItem *ti;
4711 GtkTextDirection dir;
4712 Lisp_Object style;
4713 bool text_image, horiz;
4714 struct xg_frame_tb_info *tbinfo;
4715 GdkScreen *screen;
4716 GtkIconTheme *icon_theme;
4717
4718
4719 if (! FRAME_GTK_WIDGET (f))
4720 return;
4721
4722 block_input ();
4723
4724 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4725 {
4726 hmargin = XFASTINT (Vtool_bar_button_margin);
4727 vmargin = XFASTINT (Vtool_bar_button_margin);
4728 }
4729 else if (CONSP (Vtool_bar_button_margin))
4730 {
4731 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4732 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4733
4734 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4735 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4736 }
4737
4738 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4739 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4740 i.e. zero. This means that margins less than
4741 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4742 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4743 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4744
4745 if (! x->toolbar_widget)
4746 xg_create_tool_bar (f);
4747
4748 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4749 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4750
4751 style = Ftool_bar_get_system_style ();
4752 screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4753 icon_theme = gtk_icon_theme_get_for_screen (screen);
4754
4755 /* Are we up to date? */
4756 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4757 TB_INFO_KEY);
4758
4759 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4760 && tbinfo->n_last_items == f->n_tool_bar_items
4761 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4762 && tbinfo->dir == dir
4763 && ! NILP (Fequal (tbinfo->style, style))
4764 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4765 {
4766 unblock_input ();
4767 return;
4768 }
4769
4770 tbinfo->last_tool_bar = f->tool_bar_items;
4771 tbinfo->n_last_items = f->n_tool_bar_items;
4772 tbinfo->style = style;
4773 tbinfo->hmargin = hmargin;
4774 tbinfo->vmargin = vmargin;
4775 tbinfo->dir = dir;
4776
4777 text_image = EQ (style, Qtext_image_horiz);
4778 horiz = EQ (style, Qboth_horiz) || text_image;
4779
4780 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4781 {
4782 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4783 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4784 int idx;
4785 ptrdiff_t img_id;
4786 int icon_size = 0;
4787 struct image *img = NULL;
4788 Lisp_Object image;
4789 Lisp_Object stock = Qnil;
4790 char *stock_name = NULL;
4791 char *icon_name = NULL;
4792 Lisp_Object rtl;
4793 GtkWidget *wbutton = NULL;
4794 Lisp_Object specified_file;
4795 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4796 const char *label
4797 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4798 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4799 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4800 : "";
4801
4802 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4803
4804 /* If this is a separator, use a gtk separator item. */
4805 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4806 {
4807 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4808 {
4809 if (ti)
4810 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4811 GTK_WIDGET (ti));
4812 ti = gtk_separator_tool_item_new ();
4813 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4814 }
4815 j++;
4816 continue;
4817 }
4818
4819 /* Otherwise, the tool-bar item is an ordinary button. */
4820
4821 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4822 {
4823 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4824 ti = NULL;
4825 }
4826
4827 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4828
4829 /* Ignore invalid image specifications. */
4830 image = PROP (TOOL_BAR_ITEM_IMAGES);
4831 if (!valid_image_p (image))
4832 {
4833 if (ti)
4834 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4835 GTK_WIDGET (ti));
4836 continue;
4837 }
4838
4839 specified_file = file_for_image (image);
4840 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4841 stock = call1 (Qx_gtk_map_stock, specified_file);
4842
4843 if (CONSP (stock))
4844 {
4845 Lisp_Object tem;
4846 for (tem = stock; CONSP (tem); tem = XCDR (tem))
4847 if (! NILP (tem) && STRINGP (XCAR (tem)))
4848 {
4849 stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
4850 icon_theme,
4851 &icon_name);
4852 if (stock_name || icon_name) break;
4853 }
4854 }
4855 else if (STRINGP (stock))
4856 {
4857 stock_name = find_icon_from_name (SSDATA (stock),
4858 icon_theme,
4859 &icon_name);
4860 }
4861
4862 if (stock_name || icon_name)
4863 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4864
4865 if (stock_name == NULL && icon_name == NULL)
4866 {
4867 /* No stock image, or stock item not known. Try regular
4868 image. If image is a vector, choose it according to the
4869 button state. */
4870 if (dir == GTK_TEXT_DIR_RTL
4871 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4872 && STRINGP (rtl))
4873 image = find_rtl_image (f, image, rtl);
4874
4875 if (VECTORP (image))
4876 {
4877 if (enabled_p)
4878 idx = (selected_p
4879 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4880 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4881 else
4882 idx = (selected_p
4883 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4884 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
4885
4886 eassert (ASIZE (image) >= idx);
4887 image = AREF (image, idx);
4888 }
4889 else
4890 idx = -1;
4891
4892 img_id = lookup_image (f, image);
4893 img = IMAGE_FROM_ID (f, img_id);
4894 prepare_image_for_display (f, img);
4895
4896 if (img->load_failed_p || img->pixmap == None)
4897 {
4898 if (ti)
4899 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4900 GTK_WIDGET (ti));
4901 continue;
4902 }
4903 }
4904
4905 /* If there is an existing widget, check if it's stale; if so,
4906 remove it and make a new tool item from scratch. */
4907 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
4908 img, label, horiz))
4909 {
4910 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4911 GTK_WIDGET (ti));
4912 ti = NULL;
4913 }
4914
4915 if (ti == NULL)
4916 {
4917 GtkWidget *w;
4918
4919 /* Save the image so we can see if an update is needed the
4920 next time we call xg_tool_item_match_p. */
4921 if (EQ (style, Qtext))
4922 w = NULL;
4923 else if (stock_name)
4924 {
4925
4926 #if GTK_CHECK_VERSION (3, 10, 0)
4927 w = gtk_image_new_from_icon_name (stock_name, icon_size);
4928 #else
4929 w = gtk_image_new_from_stock (stock_name, icon_size);
4930 #endif
4931 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
4932 (gpointer) xstrdup (stock_name),
4933 (GDestroyNotify) xfree);
4934 }
4935 else if (icon_name)
4936 {
4937 w = gtk_image_new_from_icon_name (icon_name, icon_size);
4938 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
4939 (gpointer) xstrdup (icon_name),
4940 (GDestroyNotify) xfree);
4941 }
4942 else
4943 {
4944 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
4945 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
4946 (gpointer)img->pixmap);
4947 }
4948
4949 #if GTK_CHECK_VERSION (3, 14, 0)
4950 if (w)
4951 {
4952 gtk_widget_set_margin_start (w, hmargin);
4953 gtk_widget_set_margin_end (w, hmargin);
4954 gtk_widget_set_margin_top (w, vmargin);
4955 gtk_widget_set_margin_bottom (w, vmargin);
4956 }
4957 #else
4958 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
4959 #endif
4960 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
4961 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4962 }
4963
4964 #undef PROP
4965
4966 gtk_widget_set_sensitive (wbutton, enabled_p);
4967 j++;
4968 }
4969
4970 /* Remove buttons not longer needed. */
4971 do
4972 {
4973 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4974 if (ti)
4975 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4976 } while (ti != NULL);
4977
4978 if (f->n_tool_bar_items != 0)
4979 {
4980 if (! x->toolbar_is_packed)
4981 xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
4982 gtk_widget_show_all (x->toolbar_widget);
4983 if (xg_update_tool_bar_sizes (f))
4984 {
4985 int inhibit
4986 = ((f->after_make_frame
4987 && !f->tool_bar_resized
4988 && (EQ (frame_inhibit_implied_resize, Qt)
4989 || (CONSP (frame_inhibit_implied_resize)
4990 && !NILP (Fmemq (Qtool_bar_lines,
4991 frame_inhibit_implied_resize))))
4992 /* This will probably fail to DTRT in the
4993 fullheight/-width cases. */
4994 && NILP (get_frame_param (f, Qfullscreen)))
4995 ? 0
4996 : 2);
4997
4998 frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
4999 adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
5000 }
5001 f->tool_bar_resized = f->tool_bar_redisplayed;
5002 }
5003
5004 unblock_input ();
5005 }
5006
5007 /* Deallocate all resources for the tool bar on frame F.
5008 Remove the tool bar. */
5009
5010 void
5011 free_frame_tool_bar (struct frame *f)
5012 {
5013 struct x_output *x = f->output_data.x;
5014
5015 if (x->toolbar_widget)
5016 {
5017 struct xg_frame_tb_info *tbinfo;
5018 GtkWidget *top_widget = x->toolbar_widget;
5019
5020 block_input ();
5021 if (x->toolbar_is_packed)
5022 {
5023 if (x->toolbar_in_hbox)
5024 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5025 top_widget);
5026 else
5027 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5028 top_widget);
5029 }
5030 else
5031 gtk_widget_destroy (x->toolbar_widget);
5032
5033 x->toolbar_widget = 0;
5034 x->toolbar_widget = 0;
5035 x->toolbar_is_packed = false;
5036 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
5037 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
5038
5039 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5040 TB_INFO_KEY);
5041 if (tbinfo)
5042 {
5043 xfree (tbinfo);
5044 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5045 TB_INFO_KEY,
5046 NULL);
5047 }
5048
5049 frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
5050 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5051
5052 unblock_input ();
5053 }
5054 }
5055
5056 void
5057 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
5058 {
5059 struct x_output *x = f->output_data.x;
5060 GtkWidget *top_widget = x->toolbar_widget;
5061
5062 if (! x->toolbar_widget || ! top_widget)
5063 return;
5064
5065 block_input ();
5066 g_object_ref (top_widget);
5067 if (x->toolbar_is_packed)
5068 {
5069 if (x->toolbar_in_hbox)
5070 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5071 top_widget);
5072 else
5073 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5074 top_widget);
5075 }
5076
5077 xg_pack_tool_bar (f, pos);
5078 g_object_unref (top_widget);
5079
5080 if (xg_update_tool_bar_sizes (f))
5081 {
5082 frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
5083 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5084 }
5085
5086
5087 unblock_input ();
5088 }
5089
5090
5091 \f
5092 /***********************************************************************
5093 Initializing
5094 ***********************************************************************/
5095 void
5096 xg_initialize (void)
5097 {
5098 GtkBindingSet *binding_set;
5099 GtkSettings *settings;
5100
5101 #if HAVE_XFT
5102 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5103 we keep it permanently linked in. */
5104 XftInit (0);
5105 #endif
5106
5107 gdpy_def = NULL;
5108 xg_ignore_gtk_scrollbar = 0;
5109 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5110 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5111
5112 id_to_widget.max_size = id_to_widget.used = 0;
5113 id_to_widget.widgets = 0;
5114
5115 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5116 (gdk_display_get_default ()));
5117 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5118 bindings. It doesn't seem to be any way to remove properties,
5119 so we set it to "" which in means "no key". */
5120 gtk_settings_set_string_property (settings,
5121 "gtk-menu-bar-accel",
5122 "",
5123 EMACS_CLASS);
5124
5125 /* Make GTK text input widgets use Emacs style keybindings. This is
5126 Emacs after all. */
5127 gtk_settings_set_string_property (settings,
5128 "gtk-key-theme-name",
5129 "Emacs",
5130 EMACS_CLASS);
5131
5132 /* Make dialogs close on C-g. Since file dialog inherits from
5133 dialog, this works for them also. */
5134 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
5135 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5136 "close", 0);
5137
5138 /* Make menus close on C-g. */
5139 binding_set = gtk_binding_set_by_class (g_type_class_ref
5140 (GTK_TYPE_MENU_SHELL));
5141 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5142 "cancel", 0);
5143 update_theme_scrollbar_width ();
5144 update_theme_scrollbar_height ();
5145
5146 #ifdef HAVE_FREETYPE
5147 x_last_font_name = NULL;
5148 #endif
5149 }
5150
5151 #endif /* USE_GTK */