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