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