Adapt shadowfile.el for Tramp (Bug#4526, Bug#4846)
[emacs.git] / src / gtkutil.c
blob83b306a730a28f5838729a73c2b650f280e94608
1 /* Functions for creating and updating GTK widgets.
3 Copyright (C) 2003-2018 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #ifdef USE_GTK
23 #include <float.h>
24 #include <stdio.h>
26 #include <c-ctype.h>
28 #include "lisp.h"
29 #include "dispextern.h"
30 #include "frame.h"
31 #include "systime.h"
32 #include "xterm.h"
33 #include "blockinput.h"
34 #include "window.h"
35 #include "gtkutil.h"
36 #include "termhooks.h"
37 #include "keyboard.h"
38 #include "coding.h"
40 #include <gdk/gdkkeysyms.h>
42 #ifdef HAVE_XFT
43 #include <X11/Xft/Xft.h>
44 #endif
46 #ifdef HAVE_GTK3
47 #include <gtk/gtkx.h>
48 #include "emacsgtkfixed.h"
49 #endif
51 #ifdef HAVE_XDBE
52 #include <X11/extensions/Xdbe.h>
53 #endif
55 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
56 #define gtk_widget_set_has_window(w, b) \
57 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
58 #endif
59 #ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
60 #define gtk_dialog_get_action_area(w) ((w)->action_area)
61 #define gtk_dialog_get_content_area(w) ((w)->vbox)
62 #endif
63 #ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
64 #define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
65 #endif
66 #ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
67 #define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
68 #define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
69 #define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
70 #define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
71 #endif
72 #if GTK_CHECK_VERSION (2, 12, 0)
73 #define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
74 #else
75 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
76 #endif
78 #if ! GTK_CHECK_VERSION (2, 14, 0)
79 #define gtk_adjustment_configure(adj, xvalue, xlower, \
80 xupper, xstep_increment, \
81 xpage_increment, xpagesize) \
82 do { \
83 adj->lower = xlower; \
84 adj->upper = xupper; \
85 adj->page_size = xpagesize; \
86 gtk_adjustment_set_value (adj, xvalue); \
87 adj->page_increment = xpage_increment; \
88 adj->step_increment = xstep_increment; \
89 } while (0)
90 #endif /* < Gtk+ 2.14 */
92 #ifdef HAVE_FREETYPE
93 #if GTK_CHECK_VERSION (3, 2, 0)
94 #define USE_NEW_GTK_FONT_CHOOSER 1
95 #else
96 #define USE_NEW_GTK_FONT_CHOOSER 0
97 #define gtk_font_chooser_dialog_new(x, y) \
98 gtk_font_selection_dialog_new (x)
99 #undef GTK_FONT_CHOOSER
100 #define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
101 #define gtk_font_chooser_set_font(x, y) \
102 gtk_font_selection_dialog_set_font_name (x, y)
103 #endif
104 #endif /* HAVE_FREETYPE */
106 #if GTK_CHECK_VERSION (3, 10, 0)
107 #define XG_TEXT_CANCEL "Cancel"
108 #define XG_TEXT_OK "OK"
109 #define XG_TEXT_OPEN "Open"
110 #else
111 #define XG_TEXT_CANCEL GTK_STOCK_CANCEL
112 #define XG_TEXT_OK GTK_STOCK_OK
113 #define XG_TEXT_OPEN GTK_STOCK_OPEN
114 #endif
116 #ifndef HAVE_GTK3
117 #ifdef USE_GTK_TOOLTIP
118 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
119 #endif
120 #define gdk_window_get_geometry(w, a, b, c, d) \
121 gdk_window_get_geometry (w, a, b, c, d, 0)
122 #define gdk_x11_window_lookup_for_display(d, w) \
123 gdk_xid_table_lookup_for_display (d, w)
124 #define gtk_box_new(ori, spacing) \
125 ((ori) == GTK_ORIENTATION_HORIZONTAL \
126 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
127 #define gtk_scrollbar_new(ori, spacing) \
128 ((ori) == GTK_ORIENTATION_HORIZONTAL \
129 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
130 #ifndef GDK_KEY_g
131 #define GDK_KEY_g GDK_g
132 #endif
133 #endif /* HAVE_GTK3 */
135 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
137 static void update_theme_scrollbar_width (void);
138 static void update_theme_scrollbar_height (void);
140 #define TB_INFO_KEY "xg_frame_tb_info"
141 struct xg_frame_tb_info
143 Lisp_Object last_tool_bar;
144 Lisp_Object style;
145 int n_last_items;
146 int hmargin, vmargin;
147 GtkTextDirection dir;
150 static GtkWidget * xg_get_widget_from_map (ptrdiff_t idx);
153 /***********************************************************************
154 Display handling functions
155 ***********************************************************************/
157 /* Keep track of the default display, or NULL if there is none. Emacs
158 may close all its displays. */
160 static GdkDisplay *gdpy_def;
162 /* When the GTK widget W is to be created on a display for F that
163 is not the default display, set the display for W.
164 W can be a GtkMenu or a GtkWindow widget. */
166 static void
167 xg_set_screen (GtkWidget *w, struct frame *f)
169 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
171 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
172 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
174 if (GTK_IS_MENU (w))
175 gtk_menu_set_screen (GTK_MENU (w), gscreen);
176 else
177 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
182 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
183 *DPY is set to NULL if the display can't be opened.
185 Returns non-zero if display could be opened, zero if display could not
186 be opened, and less than zero if the GTK version doesn't support
187 multiple displays. */
189 void
190 xg_display_open (char *display_name, Display **dpy)
192 GdkDisplay *gdpy;
194 unrequest_sigio (); /* See comment in x_display_ok, xterm.c. */
195 gdpy = gdk_display_open (display_name);
196 request_sigio ();
197 if (!gdpy_def && gdpy)
199 gdpy_def = gdpy;
200 gdk_display_manager_set_default_display (gdk_display_manager_get (),
201 gdpy);
204 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
207 /* Scaling/HiDPI functions. */
208 static int
209 xg_get_gdk_scale (void)
211 const char *sscale = getenv ("GDK_SCALE");
213 if (sscale)
215 long scale = atol (sscale);
216 if (0 < scale)
217 return min (scale, INT_MAX);
220 return 1;
224 xg_get_scale (struct frame *f)
226 #if GTK_CHECK_VERSION (3, 10, 0)
227 if (FRAME_GTK_WIDGET (f))
228 return gtk_widget_get_scale_factor (FRAME_GTK_WIDGET (f));
229 #endif
230 return xg_get_gdk_scale ();
233 /* Close display DPY. */
235 void
236 xg_display_close (Display *dpy)
238 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
240 /* If this is the default display, try to change it before closing.
241 If there is no other display to use, gdpy_def is set to NULL, and
242 the next call to xg_display_open resets the default display. */
243 if (gdk_display_get_default () == gdpy)
245 struct x_display_info *dpyinfo;
246 GdkDisplay *gdpy_new = NULL;
248 /* Find another display. */
249 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
250 if (dpyinfo->display != dpy)
252 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
253 gdk_display_manager_set_default_display (gdk_display_manager_get (),
254 gdpy_new);
255 break;
257 gdpy_def = gdpy_new;
260 #if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
261 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
262 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
263 can continue running, but there will be memory leaks. */
264 g_object_run_dispose (G_OBJECT (gdpy));
265 #else
266 /* This seems to be fixed in GTK 2.10. */
267 gdk_display_close (gdpy);
268 #endif
272 /***********************************************************************
273 Utility functions
274 ***********************************************************************/
276 /* Create and return the cursor to be used for popup menus and
277 scroll bars on display DPY. */
279 GdkCursor *
280 xg_create_default_cursor (Display *dpy)
282 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
283 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
286 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
288 static GdkPixbuf *
289 xg_get_pixbuf_from_pix_and_mask (struct frame *f,
290 Pixmap pix,
291 Pixmap mask)
293 GdkPixbuf *icon_buf = 0;
294 int iunused;
295 Window wunused;
296 unsigned int width, height, depth, uunused;
298 if (FRAME_DISPLAY_INFO (f)->red_bits != 8)
299 return 0;
300 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
301 &width, &height, &uunused, &depth);
302 if (depth != 24)
303 return 0;
304 XImage *xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
305 ~0, XYPixmap);
306 if (xim)
308 XImage *xmm = (! mask ? 0
309 : XGetImage (FRAME_X_DISPLAY (f), mask, 0, 0,
310 width, height, ~0, XYPixmap));
311 icon_buf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
312 if (icon_buf)
314 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
315 int rowjunkwidth = gdk_pixbuf_get_rowstride (icon_buf) - width * 4;
316 for (int y = 0; y < height; y++, pixels += rowjunkwidth)
317 for (int x = 0; x < width; x++)
319 unsigned long rgb = XGetPixel (xim, x, y);
320 *pixels++ = (rgb >> 16) & 255;
321 *pixels++ = (rgb >> 8) & 255;
322 *pixels++ = rgb & 255;
323 *pixels++ = xmm && !XGetPixel (xmm, x, y) ? 0 : 255;
327 if (xmm)
328 XDestroyImage (xmm);
329 XDestroyImage (xim);
332 return icon_buf;
335 static Lisp_Object
336 file_for_image (Lisp_Object image)
338 Lisp_Object specified_file = Qnil;
339 Lisp_Object tail;
341 for (tail = XCDR (image);
342 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
343 tail = XCDR (XCDR (tail)))
344 if (EQ (XCAR (tail), QCfile))
345 specified_file = XCAR (XCDR (tail));
347 return specified_file;
350 /* For the image defined in IMG, make and return a GtkImage. For displays with
351 8 planes or less we must make a GdkPixbuf and apply the mask manually.
352 Otherwise the highlighting and dimming the tool bar code in GTK does
353 will look bad. For display with more than 8 planes we just use the
354 pixmap and mask directly. For monochrome displays, GTK doesn't seem
355 able to use external pixmaps, it looks bad whatever we do.
356 The image is defined on the display where frame F is.
357 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
358 If OLD_WIDGET is NULL, a new widget is constructed and returned.
359 If OLD_WIDGET is not NULL, that widget is modified. */
361 static GtkWidget *
362 xg_get_image_for_pixmap (struct frame *f,
363 struct image *img,
364 GtkWidget *widget,
365 GtkImage *old_widget)
367 GdkPixbuf *icon_buf;
369 /* If we have a file, let GTK do all the image handling.
370 This seems to be the only way to make insensitive and activated icons
371 look good in all cases. */
372 Lisp_Object specified_file = file_for_image (img->spec);
373 Lisp_Object file;
375 /* We already loaded the image once before calling this
376 function, so this only fails if the image file has been removed.
377 In that case, use the pixmap already loaded. */
379 if (STRINGP (specified_file)
380 && STRINGP (file = x_find_image_file (specified_file)))
382 char *encoded_file = SSDATA (ENCODE_FILE (file));
383 if (! old_widget)
384 old_widget = GTK_IMAGE (gtk_image_new_from_file (encoded_file));
385 else
386 gtk_image_set_from_file (old_widget, encoded_file);
388 return GTK_WIDGET (old_widget);
391 /* No file, do the image handling ourselves. This will look very bad
392 on a monochrome display, and sometimes bad on all displays with
393 certain themes. */
395 /* This is a workaround to make icons look good on pseudo color
396 displays. Apparently GTK expects the images to have an alpha
397 channel. If they don't, insensitive and activated icons will
398 look bad. This workaround does not work on monochrome displays,
399 and is strictly not needed on true color/static color displays (i.e.
400 16 bits and higher). But we do it anyway so we get a pixbuf that is
401 not associated with the img->pixmap. The img->pixmap may be removed
402 by clearing the image cache and then the tool bar redraw fails, since
403 Gtk+ assumes the pixmap is always there. */
404 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
406 if (icon_buf)
408 if (! old_widget)
409 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
410 else
411 gtk_image_set_from_pixbuf (old_widget, icon_buf);
413 g_object_unref (G_OBJECT (icon_buf));
416 return GTK_WIDGET (old_widget);
420 /* Set CURSOR on W and all widgets W contain. We must do like this
421 for scroll bars and menu because they create widgets internally,
422 and it is those widgets that are visible. */
424 static void
425 xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
427 GdkWindow *window = gtk_widget_get_window (w);
428 GList *children = gdk_window_peek_children (window);
430 gdk_window_set_cursor (window, cursor);
432 /* The scroll bar widget has more than one GDK window (had to look at
433 the source to figure this out), and there is no way to set cursor
434 on widgets in GTK. So we must set the cursor for all GDK windows.
435 Ditto for menus. */
437 for ( ; children; children = g_list_next (children))
438 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
441 /* Insert NODE into linked LIST. */
443 static void
444 xg_list_insert (xg_list_node *list, xg_list_node *node)
446 xg_list_node *list_start = list->next;
448 if (list_start) list_start->prev = node;
449 node->next = list_start;
450 node->prev = 0;
451 list->next = node;
454 /* Remove NODE from linked LIST. */
456 static void
457 xg_list_remove (xg_list_node *list, xg_list_node *node)
459 xg_list_node *list_start = list->next;
460 if (node == list_start)
462 list->next = node->next;
463 if (list->next) list->next->prev = 0;
465 else
467 node->prev->next = node->next;
468 if (node->next) node->next->prev = node->prev;
472 /* Allocate and return a utf8 version of STR. If STR is already
473 utf8 or NULL, just return a copy of STR.
474 A new string is allocated and the caller must free the result
475 with g_free. */
477 static char *
478 get_utf8_string (const char *str)
480 char *utf8_str;
482 if (!str) return NULL;
484 /* If not UTF-8, try current locale. */
485 if (!g_utf8_validate (str, -1, NULL))
486 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
487 else
488 return g_strdup (str);
490 if (!utf8_str)
492 /* Probably some control characters in str. Escape them. */
493 ptrdiff_t len;
494 ptrdiff_t nr_bad = 0;
495 gsize bytes_read;
496 gsize bytes_written;
497 unsigned char *p = (unsigned char *)str;
498 char *cp, *up;
499 GError *err = NULL;
501 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
502 &bytes_written, &err))
503 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
505 ++nr_bad;
506 p += bytes_written+1;
507 g_error_free (err);
508 err = NULL;
511 if (err)
513 g_error_free (err);
514 err = NULL;
516 if (cp) g_free (cp);
518 len = strlen (str);
519 ptrdiff_t alloc;
520 if (INT_MULTIPLY_WRAPV (nr_bad, 4, &alloc)
521 || INT_ADD_WRAPV (len + 1, alloc, &alloc)
522 || SIZE_MAX < alloc)
523 memory_full (SIZE_MAX);
524 up = utf8_str = xmalloc (alloc);
525 p = (unsigned char *)str;
527 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
528 &bytes_written, &err))
529 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
531 memcpy (up, p, bytes_written);
532 up += bytes_written;
533 up += sprintf (up, "\\%03o", p[bytes_written]);
534 p += bytes_written + 1;
535 g_error_free (err);
536 err = NULL;
539 if (cp)
541 strcpy (up, cp);
542 g_free (cp);
544 if (err)
546 g_error_free (err);
547 err = NULL;
550 return utf8_str;
553 /* Check for special colors used in face spec for region face.
554 The colors are fetched from the Gtk+ theme.
555 Return true if color was found, false if not. */
557 bool
558 xg_check_special_colors (struct frame *f,
559 const char *color_name,
560 XColor *color)
562 bool success_p = 0;
563 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
564 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
566 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
567 return success_p;
569 block_input ();
571 #ifdef HAVE_GTK3
572 GtkStyleContext *gsty
573 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
574 GdkRGBA col;
575 char buf[sizeof "rgb://rrrr/gggg/bbbb"];
576 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
577 if (get_fg)
578 gtk_style_context_get_color (gsty, state, &col);
579 else
581 GdkRGBA *c;
582 /* FIXME: Retrieving the background color is deprecated in
583 GTK+ 3.16. New versions of GTK+ don't use the concept of a
584 single background color any more, so we shouldn't query for
585 it. */
586 gtk_style_context_get (gsty, state,
587 GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &c,
588 NULL);
589 col = *c;
590 gdk_rgba_free (c);
593 unsigned short
594 r = col.red * 65535,
595 g = col.green * 65535,
596 b = col.blue * 65535;
597 sprintf (buf, "rgb:%04x/%04x/%04x", r, g, b);
598 success_p = x_parse_color (f, buf, color) != 0;
599 #else
600 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
601 GdkColor *grgb = get_bg
602 ? &gsty->bg[GTK_STATE_SELECTED]
603 : &gsty->fg[GTK_STATE_SELECTED];
605 color->red = grgb->red;
606 color->green = grgb->green;
607 color->blue = grgb->blue;
608 color->pixel = grgb->pixel;
609 success_p = 1;
610 #endif
613 unblock_input ();
614 return success_p;
619 /***********************************************************************
620 Tooltips
621 ***********************************************************************/
622 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
623 We use that to pop down the tooltip. This happens if Gtk+ for some
624 reason wants to change or hide the tooltip. */
626 #ifdef USE_GTK_TOOLTIP
628 static void
629 hierarchy_ch_cb (GtkWidget *widget,
630 GtkWidget *previous_toplevel,
631 gpointer user_data)
633 struct frame *f = user_data;
634 struct x_output *x = f->output_data.x;
635 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
637 if (! top || ! GTK_IS_WINDOW (top))
638 gtk_widget_hide (previous_toplevel);
641 /* Callback called when Gtk+ thinks a tooltip should be displayed.
642 We use it to get the tooltip window and the tooltip widget so
643 we can manipulate the ourselves.
645 Return FALSE ensures that the tooltip is not shown. */
647 static gboolean
648 qttip_cb (GtkWidget *widget,
649 gint xpos,
650 gint ypos,
651 gboolean keyboard_mode,
652 GtkTooltip *tooltip,
653 gpointer user_data)
655 struct frame *f = user_data;
656 struct x_output *x = f->output_data.x;
657 if (x->ttip_widget == NULL)
659 GtkWidget *p;
660 GList *list, *iter;
662 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
663 x->ttip_widget = tooltip;
664 g_object_ref (G_OBJECT (tooltip));
665 x->ttip_lbl = gtk_label_new ("");
666 g_object_ref (G_OBJECT (x->ttip_lbl));
667 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
668 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
670 /* Change stupid Gtk+ default line wrapping. */
671 p = gtk_widget_get_parent (x->ttip_lbl);
672 list = gtk_container_get_children (GTK_CONTAINER (p));
673 for (iter = list; iter; iter = g_list_next (iter))
675 GtkWidget *w = GTK_WIDGET (iter->data);
676 if (GTK_IS_LABEL (w))
677 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
679 g_list_free (list);
681 /* ATK needs an empty title for some reason. */
682 gtk_window_set_title (x->ttip_window, "");
683 /* Realize so we can safely get screen later on. */
684 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
685 gtk_widget_realize (x->ttip_lbl);
687 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
688 G_CALLBACK (hierarchy_ch_cb), f);
690 return FALSE;
693 #endif /* USE_GTK_TOOLTIP */
695 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
696 Return true if a system tooltip is available. */
698 bool
699 xg_prepare_tooltip (struct frame *f,
700 Lisp_Object string,
701 int *width,
702 int *height)
704 #ifndef USE_GTK_TOOLTIP
705 return 0;
706 #else
707 struct x_output *x = f->output_data.x;
708 GtkWidget *widget;
709 GdkWindow *gwin;
710 GdkScreen *screen;
711 GtkSettings *settings;
712 gboolean tt_enabled = TRUE;
713 GtkRequisition req;
714 Lisp_Object encoded_string;
716 if (!x->ttip_lbl) return 0;
718 block_input ();
719 encoded_string = ENCODE_UTF_8 (string);
720 widget = GTK_WIDGET (x->ttip_lbl);
721 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
722 screen = gdk_window_get_screen (gwin);
723 settings = gtk_settings_get_for_screen (screen);
724 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
725 if (tt_enabled)
727 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
728 /* Record that we disabled it so it can be enabled again. */
729 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
730 (gpointer)f);
733 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
734 g_object_set_data (G_OBJECT
735 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
736 "gdk-display-current-tooltip", NULL);
738 /* Put our dummy widget in so we can get callbacks for unrealize and
739 hierarchy-changed. */
740 gtk_tooltip_set_custom (x->ttip_widget, widget);
741 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
742 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
743 if (width) *width = req.width;
744 if (height) *height = req.height;
746 unblock_input ();
748 return 1;
749 #endif /* USE_GTK_TOOLTIP */
752 /* Show the tooltip at ROOT_X and ROOT_Y.
753 xg_prepare_tooltip must have been called before this function. */
755 void
756 xg_show_tooltip (struct frame *f, int root_x, int root_y)
758 #ifdef USE_GTK_TOOLTIP
759 struct x_output *x = f->output_data.x;
760 if (x->ttip_window)
762 block_input ();
763 gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
764 root_y / xg_get_scale (f));
765 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
766 unblock_input ();
768 #endif
771 /* Hide tooltip if shown. Do nothing if not shown.
772 Return true if tip was hidden, false if not (i.e. not using
773 system tooltips). */
775 bool
776 xg_hide_tooltip (struct frame *f)
778 bool ret = 0;
779 #ifdef USE_GTK_TOOLTIP
780 if (f->output_data.x->ttip_window)
782 GtkWindow *win = f->output_data.x->ttip_window;
783 block_input ();
784 gtk_widget_hide (GTK_WIDGET (win));
786 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
788 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
789 GdkScreen *screen = gdk_window_get_screen (gwin);
790 GtkSettings *settings = gtk_settings_get_for_screen (screen);
791 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
793 unblock_input ();
795 ret = 1;
797 #endif
798 return ret;
802 /***********************************************************************
803 General functions for creating widgets, resizing, events, e.t.c.
804 ***********************************************************************/
806 #if ! GTK_CHECK_VERSION (3, 22, 0)
807 static void
808 my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
809 const gchar *msg, gpointer user_data)
811 if (!strstr (msg, "visible children"))
812 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
814 #endif
816 /* Make a geometry string and pass that to GTK. It seems this is the
817 only way to get geometry position right if the user explicitly
818 asked for a position when starting Emacs.
819 F is the frame we shall set geometry for. */
821 static void
822 xg_set_geometry (struct frame *f)
824 if (f->size_hint_flags & (USPosition | PPosition))
826 int scale = xg_get_scale (f);
827 #if ! GTK_CHECK_VERSION (3, 22, 0)
828 if (x_gtk_use_window_move)
830 #endif
831 /* Handle negative positions without consulting
832 gtk_window_parse_geometry (Bug#25851). The position will
833 be off by scrollbar width + window manager decorations. */
834 if (f->size_hint_flags & XNegative)
835 f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f))
836 - FRAME_PIXEL_WIDTH (f) + f->left_pos);
838 if (f->size_hint_flags & YNegative)
839 f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f))
840 - FRAME_PIXEL_HEIGHT (f) + f->top_pos);
842 /* GTK works in scaled pixels, so convert from X pixels. */
843 gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
844 f->left_pos / scale, f->top_pos / scale);
846 /* Reset size hint flags. */
847 f->size_hint_flags &= ~ (XNegative | YNegative);
848 # if ! GTK_CHECK_VERSION (3, 22, 0)
850 else
852 /* GTK works in scaled pixels, so convert from X pixels. */
853 int left = f->left_pos / scale;
854 int xneg = f->size_hint_flags & XNegative;
855 int top = f->top_pos / scale;
856 int yneg = f->size_hint_flags & YNegative;
857 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
858 guint id;
860 if (xneg)
861 left = -left;
862 if (yneg)
863 top = -top;
865 sprintf (geom_str, "=%dx%d%c%d%c%d",
866 FRAME_PIXEL_WIDTH (f),
867 FRAME_PIXEL_HEIGHT (f),
868 (xneg ? '-' : '+'), left,
869 (yneg ? '-' : '+'), top);
871 /* Silence warning about visible children. */
872 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
873 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
875 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
876 geom_str))
877 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
879 g_log_remove_handler ("Gtk", id);
881 #endif
885 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
886 and a Gtk+ menu bar, we get resize events for the edit part of the
887 frame only. We let Gtk+ deal with the Gtk+ parts.
888 F is the frame to resize.
889 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
891 void
892 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
894 int width, height;
896 if (pixelwidth == -1 && pixelheight == -1)
898 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
899 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
900 0, 0, &pixelwidth, &pixelheight);
901 else
902 return;
905 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
906 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
908 frame_size_history_add
909 (f, Qxg_frame_resized, width, height, Qnil);
911 if (width != FRAME_TEXT_WIDTH (f)
912 || height != FRAME_TEXT_HEIGHT (f)
913 || pixelwidth != FRAME_PIXEL_WIDTH (f)
914 || pixelheight != FRAME_PIXEL_HEIGHT (f))
916 x_clear_under_internal_border (f);
917 change_frame_size (f, width, height, 0, 1, 0, 1);
918 SET_FRAME_GARBAGED (f);
919 cancel_mouse_face (f);
923 /* Resize the outer window of frame F after changing the height.
924 COLUMNS/ROWS is the size the edit area shall have after the resize. */
926 void
927 xg_frame_set_char_size (struct frame *f, int width, int height)
929 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
930 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
931 Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
932 gint gwidth, gheight;
933 int totalheight
934 = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
935 int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
937 if (FRAME_PIXEL_HEIGHT (f) == 0)
938 return;
940 gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
941 &gwidth, &gheight);
943 /* Do this before resize, as we don't know yet if we will be resized. */
944 x_clear_under_internal_border (f);
946 totalheight /= xg_get_scale (f);
947 totalwidth /= xg_get_scale (f);
949 x_wm_set_size_hint (f, 0, 0);
951 /* Resize the top level widget so rows and columns remain constant.
953 When the frame is fullheight and we only want to change the width
954 or it is fullwidth and we only want to change the height we should
955 be able to preserve the fullscreen property. However, due to the
956 fact that we have to send a resize request anyway, the window
957 manager will abolish it. At least the respective size should
958 remain unchanged but giving the frame back its normal size will
959 be broken ... */
960 if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
962 frame_size_history_add
963 (f, Qxg_frame_set_char_size_1, width, height,
964 list2 (make_number (gheight), make_number (totalheight)));
966 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
967 gwidth, totalheight);
969 else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
971 frame_size_history_add
972 (f, Qxg_frame_set_char_size_2, width, height,
973 list2 (make_number (gwidth), make_number (totalwidth)));
975 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
976 totalwidth, gheight);
978 else
980 frame_size_history_add
981 (f, Qxg_frame_set_char_size_3, width, height,
982 list2 (make_number (totalwidth), make_number (totalheight)));
984 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
985 totalwidth, totalheight);
986 fullscreen = Qnil;
989 SET_FRAME_GARBAGED (f);
990 cancel_mouse_face (f);
992 /* We can not call change_frame_size for a mapped frame,
993 we can not set pixel width/height either. The window manager may
994 override our resize request, XMonad does this all the time.
995 The best we can do is try to sync, so lisp code sees the updated
996 size as fast as possible.
997 For unmapped windows, we can set rows/cols. When
998 the frame is mapped again we will (hopefully) get the correct size. */
999 if (FRAME_VISIBLE_P (f))
1001 /* Must call this to flush out events */
1002 (void)gtk_events_pending ();
1003 gdk_flush ();
1004 x_wait_for_event (f, ConfigureNotify);
1006 if (!NILP (fullscreen))
1007 /* Try to restore fullscreen state. */
1009 store_frame_param (f, Qfullscreen, fullscreen);
1010 x_set_fullscreen (f, fullscreen, fullscreen);
1013 else
1014 adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
1018 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
1019 The policy is to keep the number of editable lines. */
1021 #if 0
1022 static void
1023 xg_height_or_width_changed (struct frame *f)
1025 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1026 FRAME_TOTAL_PIXEL_WIDTH (f),
1027 FRAME_TOTAL_PIXEL_HEIGHT (f));
1028 f->output_data.x->hint_flags = 0;
1029 x_wm_set_size_hint (f, 0, 0);
1031 #endif
1033 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1034 Must be done like this, because GtkWidget:s can have "hidden"
1035 X Window that aren't accessible.
1037 Return 0 if no widget match WDESC. */
1039 GtkWidget *
1040 xg_win_to_widget (Display *dpy, Window wdesc)
1042 gpointer gdkwin;
1043 GtkWidget *gwdesc = 0;
1045 block_input ();
1047 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1048 wdesc);
1049 if (gdkwin)
1051 GdkEvent event;
1052 event.any.window = gdkwin;
1053 event.any.type = GDK_NOTHING;
1054 gwdesc = gtk_get_event_widget (&event);
1057 unblock_input ();
1058 return gwdesc;
1061 /* Set the background of widget W to PIXEL. */
1063 static void
1064 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
1066 #ifdef HAVE_GTK3
1067 GdkRGBA bg;
1068 XColor xbg;
1069 xbg.pixel = pixel;
1070 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1072 bg.red = (double)xbg.red/65535.0;
1073 bg.green = (double)xbg.green/65535.0;
1074 bg.blue = (double)xbg.blue/65535.0;
1075 bg.alpha = 1.0;
1076 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1078 #else
1079 GdkColor bg;
1080 GdkColormap *map = gtk_widget_get_colormap (w);
1081 gdk_colormap_query_color (map, pixel, &bg);
1082 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1083 #endif
1086 /* Callback called when the gtk theme changes.
1087 We notify lisp code so it can fix faces used for region for example. */
1089 static void
1090 style_changed_cb (GObject *go,
1091 GParamSpec *spec,
1092 gpointer user_data)
1094 struct input_event event;
1095 GdkDisplay *gdpy = user_data;
1096 const char *display_name = gdk_display_get_name (gdpy);
1097 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1099 EVENT_INIT (event);
1100 event.kind = CONFIG_CHANGED_EVENT;
1101 event.frame_or_window = build_string (display_name);
1102 /* Theme doesn't change often, so intern is called seldom. */
1103 event.arg = intern ("theme-name");
1104 kbd_buffer_store_event (&event);
1106 update_theme_scrollbar_width ();
1107 update_theme_scrollbar_height ();
1109 /* If scroll bar width changed, we need set the new size on all frames
1110 on this display. */
1111 if (dpy)
1113 Lisp_Object rest, frame;
1114 FOR_EACH_FRAME (rest, frame)
1116 struct frame *f = XFRAME (frame);
1117 if (FRAME_LIVE_P (f)
1118 && FRAME_X_P (f)
1119 && FRAME_X_DISPLAY (f) == dpy)
1121 x_set_scroll_bar_default_width (f);
1122 x_set_scroll_bar_default_height (f);
1123 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
1129 /* Called when a delete-event occurs on WIDGET. */
1131 static gboolean
1132 delete_cb (GtkWidget *widget,
1133 GdkEvent *event,
1134 gpointer user_data)
1136 return TRUE;
1139 /* Create and set up the GTK widgets for frame F.
1140 Return true if creation succeeded. */
1142 bool
1143 xg_create_frame_widgets (struct frame *f)
1145 GtkWidget *wtop;
1146 GtkWidget *wvbox, *whbox;
1147 GtkWidget *wfixed;
1148 #ifndef HAVE_GTK3
1149 GtkRcStyle *style;
1150 #endif
1151 char *title = 0;
1153 block_input ();
1155 if (FRAME_X_EMBEDDED_P (f))
1157 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1158 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1160 else
1161 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1163 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1164 has backported it to Gtk+ 2.0 and they add the resize grip for
1165 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1166 forever, so disable the grip. */
1167 #if (! GTK_CHECK_VERSION (3, 0, 0) \
1168 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1169 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1170 #endif
1172 xg_set_screen (wtop, f);
1174 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1175 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1176 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1177 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1179 #ifdef HAVE_GTK3
1180 wfixed = emacs_fixed_new (f);
1181 #else
1182 wfixed = gtk_fixed_new ();
1183 #endif
1185 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1187 if (wtop) gtk_widget_destroy (wtop);
1188 if (wvbox) gtk_widget_destroy (wvbox);
1189 if (whbox) gtk_widget_destroy (whbox);
1190 if (wfixed) gtk_widget_destroy (wfixed);
1192 unblock_input ();
1193 return 0;
1196 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1197 gtk_widget_set_name (wtop, EMACS_CLASS);
1198 gtk_widget_set_name (wvbox, "pane");
1199 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1201 /* If this frame has a title or name, set it in the title bar. */
1202 if (! NILP (f->title))
1203 title = SSDATA (ENCODE_UTF_8 (f->title));
1204 else if (! NILP (f->name))
1205 title = SSDATA (ENCODE_UTF_8 (f->name));
1207 if (title)
1208 gtk_window_set_title (GTK_WINDOW (wtop), title);
1210 if (FRAME_UNDECORATED (f))
1212 gtk_window_set_decorated (GTK_WINDOW (wtop), FALSE);
1213 store_frame_param (f, Qundecorated, Qt);
1216 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1217 FRAME_GTK_WIDGET (f) = wfixed;
1218 f->output_data.x->vbox_widget = wvbox;
1219 f->output_data.x->hbox_widget = whbox;
1221 gtk_widget_set_has_window (wfixed, TRUE);
1223 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1224 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1225 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1227 if (FRAME_EXTERNAL_TOOL_BAR (f))
1228 update_frame_tool_bar (f);
1230 /* We don't want this widget double buffered, because we draw on it
1231 with regular X drawing primitives, so from a GTK/GDK point of
1232 view, the widget is totally blank. When an expose comes, this
1233 will make the widget blank, and then Emacs redraws it. This flickers
1234 a lot, so we turn off double buffering.
1235 FIXME: gtk_widget_set_double_buffered is deprecated and might stop
1236 working in the future. We need to migrate away from combining
1237 X and GTK+ drawing to a pure GTK+ build. */
1238 gtk_widget_set_double_buffered (wfixed, FALSE);
1240 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1241 SSDATA (Vx_resource_name),
1242 SSDATA (Vx_resource_class));
1244 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1245 GTK is to destroy the widget. We want Emacs to do that instead. */
1246 g_signal_connect (G_OBJECT (wtop), "delete-event",
1247 G_CALLBACK (delete_cb), f);
1249 /* Convert our geometry parameters into a geometry string
1250 and specify it.
1251 GTK will itself handle calculating the real position this way. */
1252 xg_set_geometry (f);
1253 f->win_gravity
1254 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1256 gtk_widget_add_events (wfixed,
1257 GDK_POINTER_MOTION_MASK
1258 | GDK_EXPOSURE_MASK
1259 | GDK_BUTTON_PRESS_MASK
1260 | GDK_BUTTON_RELEASE_MASK
1261 | GDK_KEY_PRESS_MASK
1262 | GDK_ENTER_NOTIFY_MASK
1263 | GDK_LEAVE_NOTIFY_MASK
1264 | GDK_FOCUS_CHANGE_MASK
1265 | GDK_STRUCTURE_MASK
1266 | GDK_VISIBILITY_NOTIFY_MASK);
1268 /* Must realize the windows so the X window gets created. It is used
1269 by callers of this function. */
1270 gtk_widget_realize (wfixed);
1271 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1272 initial_set_up_x_back_buffer (f);
1274 /* Since GTK clears its window by filling with the background color,
1275 we must keep X and GTK background in sync. */
1276 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1278 #ifndef HAVE_GTK3
1279 /* Also, do not let any background pixmap to be set, this looks very
1280 bad as Emacs overwrites the background pixmap with its own idea
1281 of background color. */
1282 style = gtk_widget_get_modifier_style (wfixed);
1284 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1285 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1286 gtk_widget_modify_style (wfixed, style);
1287 #else
1288 gtk_widget_set_can_focus (wfixed, TRUE);
1289 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1290 #endif
1292 if (FRAME_OVERRIDE_REDIRECT (f))
1294 GdkWindow *gwin = gtk_widget_get_window (wtop);
1296 if (gwin)
1297 gdk_window_set_override_redirect (gwin, TRUE);
1300 #ifdef USE_GTK_TOOLTIP
1301 /* Steal a tool tip window we can move ourselves. */
1302 f->output_data.x->ttip_widget = 0;
1303 f->output_data.x->ttip_lbl = 0;
1304 f->output_data.x->ttip_window = 0;
1305 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1306 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1307 #endif
1310 GdkScreen *screen = gtk_widget_get_screen (wtop);
1311 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1312 /* Only connect this signal once per screen. */
1313 if (! g_signal_handler_find (G_OBJECT (gs),
1314 G_SIGNAL_MATCH_FUNC,
1315 0, 0, 0,
1316 (gpointer) G_CALLBACK (style_changed_cb),
1319 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1320 G_CALLBACK (style_changed_cb),
1321 gdk_screen_get_display (screen));
1325 unblock_input ();
1327 return 1;
1330 void
1331 xg_free_frame_widgets (struct frame *f)
1333 if (FRAME_GTK_OUTER_WIDGET (f))
1335 #ifdef USE_GTK_TOOLTIP
1336 struct x_output *x = f->output_data.x;
1337 #endif
1338 struct xg_frame_tb_info *tbinfo
1339 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1340 TB_INFO_KEY);
1341 if (tbinfo)
1342 xfree (tbinfo);
1344 /* x_free_frame_resources should have taken care of it */
1345 eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
1346 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1347 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1348 FRAME_X_RAW_DRAWABLE (f) = 0;
1349 FRAME_GTK_OUTER_WIDGET (f) = 0;
1350 #ifdef USE_GTK_TOOLTIP
1351 if (x->ttip_lbl)
1352 gtk_widget_destroy (x->ttip_lbl);
1353 if (x->ttip_widget)
1354 g_object_unref (G_OBJECT (x->ttip_widget));
1355 #endif
1359 /* Set the normal size hints for the window manager, for frame F.
1360 FLAGS is the flags word to use--or 0 meaning preserve the flags
1361 that the window now has.
1362 If USER_POSITION, set the User Position
1363 flag (this is useful when FLAGS is 0). */
1365 void
1366 x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
1368 /* Must use GTK routines here, otherwise GTK resets the size hints
1369 to its own defaults. */
1370 GdkGeometry size_hints;
1371 gint hint_flags = 0;
1372 int base_width, base_height;
1373 int min_rows = 0, min_cols = 0;
1374 int win_gravity = f->win_gravity;
1375 Lisp_Object fs_state, frame;
1376 int scale = xg_get_scale (f);
1378 /* Don't set size hints during initialization; that apparently leads
1379 to a race condition. See the thread at
1380 https://lists.gnu.org/r/emacs-devel/2008-10/msg00033.html */
1381 if (NILP (Vafter_init_time)
1382 || !FRAME_GTK_OUTER_WIDGET (f)
1383 || FRAME_PARENT_FRAME (f))
1384 return;
1386 XSETFRAME (frame, f);
1387 fs_state = Fframe_parameter (frame, Qfullscreen);
1388 if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
1389 (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
1390 x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
1392 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1393 Gtk3 don't get along and the frame shrinks (!).
1395 return;
1398 if (flags)
1400 memset (&size_hints, 0, sizeof (size_hints));
1401 f->output_data.x->size_hints = size_hints;
1402 f->output_data.x->hint_flags = hint_flags;
1404 else
1405 flags = f->size_hint_flags;
1407 size_hints = f->output_data.x->size_hints;
1408 hint_flags = f->output_data.x->hint_flags;
1410 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1411 size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
1412 size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
1414 hint_flags |= GDK_HINT_BASE_SIZE;
1415 /* Use one row/col here so base_height/width does not become zero.
1416 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
1417 Obviously this makes the row/col value displayed off by 1. */
1418 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1419 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1420 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1422 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1423 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1425 size_hints.base_width = base_width;
1426 size_hints.base_height = base_height;
1427 size_hints.min_width = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
1428 size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
1430 /* These currently have a one to one mapping with the X values, but I
1431 don't think we should rely on that. */
1432 hint_flags |= GDK_HINT_WIN_GRAVITY;
1433 size_hints.win_gravity = 0;
1434 if (win_gravity == NorthWestGravity)
1435 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1436 else if (win_gravity == NorthGravity)
1437 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1438 else if (win_gravity == NorthEastGravity)
1439 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1440 else if (win_gravity == WestGravity)
1441 size_hints.win_gravity = GDK_GRAVITY_WEST;
1442 else if (win_gravity == CenterGravity)
1443 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1444 else if (win_gravity == EastGravity)
1445 size_hints.win_gravity = GDK_GRAVITY_EAST;
1446 else if (win_gravity == SouthWestGravity)
1447 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1448 else if (win_gravity == SouthGravity)
1449 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1450 else if (win_gravity == SouthEastGravity)
1451 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1452 else if (win_gravity == StaticGravity)
1453 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1455 if (x_gtk_use_window_move)
1457 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
1458 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
1459 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
1462 if (user_position)
1464 hint_flags &= ~GDK_HINT_POS;
1465 hint_flags |= GDK_HINT_USER_POS;
1468 size_hints.base_width /= scale;
1469 size_hints.base_height /= scale;
1470 size_hints.width_inc /= scale;
1471 size_hints.height_inc /= scale;
1473 if (hint_flags != f->output_data.x->hint_flags
1474 || memcmp (&size_hints,
1475 &f->output_data.x->size_hints,
1476 sizeof (size_hints)) != 0)
1478 block_input ();
1479 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1480 NULL, &size_hints, hint_flags);
1481 f->output_data.x->size_hints = size_hints;
1482 f->output_data.x->hint_flags = hint_flags;
1483 unblock_input ();
1487 /* Change background color of a frame.
1488 Since GTK uses the background color to clear the window, we must
1489 keep the GTK and X colors in sync.
1490 F is the frame to change,
1491 BG is the pixel value to change to. */
1493 void
1494 xg_set_background_color (struct frame *f, unsigned long bg)
1496 if (FRAME_GTK_WIDGET (f))
1498 block_input ();
1499 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1501 Lisp_Object bar;
1502 for (bar = FRAME_SCROLL_BARS (f);
1503 !NILP (bar);
1504 bar = XSCROLL_BAR (bar)->next)
1506 GtkWidget *scrollbar =
1507 xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window);
1508 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
1509 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
1512 unblock_input ();
1516 /* Change the frame's decoration (title bar + resize borders). This
1517 might not work with all window managers. */
1518 void
1519 xg_set_undecorated (struct frame *f, Lisp_Object undecorated)
1521 if (FRAME_GTK_WIDGET (f))
1523 block_input ();
1524 gtk_window_set_decorated (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1525 NILP (undecorated) ? TRUE : FALSE);
1526 unblock_input ();
1531 /* Restack F1 below F2, above if ABOVE_FLAG is true. This might not
1532 work with all window managers. */
1533 void
1534 xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag)
1536 #if GTK_CHECK_VERSION (2, 18, 0)
1537 block_input ();
1538 if (FRAME_GTK_OUTER_WIDGET (f1) && FRAME_GTK_OUTER_WIDGET (f2))
1540 GdkWindow *gwin1 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f1));
1541 GdkWindow *gwin2 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f2));
1542 Lisp_Object frame1, frame2;
1544 XSETFRAME (frame1, f1);
1545 XSETFRAME (frame2, f2);
1547 gdk_window_restack (gwin1, gwin2, above_flag);
1548 x_sync (f1);
1550 unblock_input ();
1551 #endif
1555 /* Don't show frame in taskbar, don't ALT-TAB to it. */
1556 void
1557 xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
1559 block_input ();
1560 if (FRAME_GTK_WIDGET (f))
1561 gdk_window_set_skip_taskbar_hint
1562 (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
1563 NILP (skip_taskbar) ? FALSE : TRUE);
1564 unblock_input ();
1568 /* Don't give frame focus. */
1569 void
1570 xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
1572 block_input ();
1573 if (FRAME_GTK_WIDGET (f))
1575 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1576 gboolean g_no_focus_on_map = NILP (no_focus_on_map) ? TRUE : FALSE;
1578 gtk_window_set_focus_on_map (gwin, g_no_focus_on_map);
1580 unblock_input ();
1584 void
1585 xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus)
1587 block_input ();
1588 if (FRAME_GTK_WIDGET (f))
1590 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1591 gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
1593 gtk_window_set_accept_focus (gwin, g_no_accept_focus);
1595 unblock_input ();
1598 void
1599 xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect)
1601 block_input ();
1603 if (FRAME_GTK_OUTER_WIDGET (f))
1605 GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f));
1607 gdk_window_set_override_redirect (gwin, NILP (override_redirect) ? FALSE : TRUE);
1610 unblock_input ();
1613 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1614 functions so GTK does not overwrite the icon. */
1616 void
1617 xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1619 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1620 icon_pixmap,
1621 icon_mask);
1622 if (gp)
1623 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1628 /***********************************************************************
1629 Dialog functions
1630 ***********************************************************************/
1631 /* Return the dialog title to use for a dialog of type KEY.
1632 This is the encoding used by lwlib. We use the same for GTK. */
1634 static const char *
1635 get_dialog_title (char key)
1637 const char *title = "";
1639 switch (key) {
1640 case 'E': case 'e':
1641 title = "Error";
1642 break;
1644 case 'I': case 'i':
1645 title = "Information";
1646 break;
1648 case 'L': case 'l':
1649 title = "Prompt";
1650 break;
1652 case 'P': case 'p':
1653 title = "Prompt";
1654 break;
1656 case 'Q': case 'q':
1657 title = "Question";
1658 break;
1661 return title;
1664 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1665 the dialog, but return TRUE so the event does not propagate further
1666 in GTK. This prevents GTK from destroying the dialog widget automatically
1667 and we can always destroy the widget manually, regardless of how
1668 it was popped down (button press or WM_DELETE_WINDOW).
1669 W is the dialog widget.
1670 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1671 user_data is NULL (not used).
1673 Returns TRUE to end propagation of event. */
1675 static gboolean
1676 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1678 gtk_widget_unmap (w);
1679 return TRUE;
1682 /* Create a popup dialog window. See also xg_create_widget below.
1683 WV is a widget_value describing the dialog.
1684 SELECT_CB is the callback to use when a button has been pressed.
1685 DEACTIVATE_CB is the callback to use when the dialog pops down.
1687 Returns the GTK dialog widget. */
1689 static GtkWidget *
1690 create_dialog (widget_value *wv,
1691 GCallback select_cb,
1692 GCallback deactivate_cb)
1694 const char *title = get_dialog_title (wv->name[0]);
1695 int total_buttons = wv->name[1] - '0';
1696 int right_buttons = wv->name[4] - '0';
1697 int left_buttons;
1698 int button_nr = 0;
1699 int button_spacing = 10;
1700 GtkWidget *wdialog = gtk_dialog_new ();
1701 GtkDialog *wd = GTK_DIALOG (wdialog);
1702 widget_value *item;
1703 GtkWidget *whbox_down;
1705 /* If the number of buttons is greater than 4, make two rows of buttons
1706 instead. This looks better. */
1707 bool make_two_rows = total_buttons > 4;
1709 #if GTK_CHECK_VERSION (3, 12, 0)
1710 GtkBuilder *gbld = gtk_builder_new ();
1711 GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
1712 gbld,
1713 "action_area");
1714 GtkBox *cur_box = GTK_BOX (go);
1715 g_object_unref (G_OBJECT (gbld));
1716 #else
1717 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1718 #endif
1720 if (right_buttons == 0) right_buttons = total_buttons/2;
1721 left_buttons = total_buttons - right_buttons;
1723 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1724 gtk_widget_set_name (wdialog, "emacs-dialog");
1727 if (make_two_rows)
1729 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1730 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1731 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1732 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1733 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1734 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1736 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1737 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1738 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1740 cur_box = GTK_BOX (whbox_up);
1743 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1744 G_CALLBACK (dialog_delete_callback), 0);
1746 if (deactivate_cb)
1748 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1749 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1752 for (item = wv->contents; item; item = item->next)
1754 char *utf8_label = get_utf8_string (item->value);
1755 GtkWidget *w;
1756 GtkRequisition req;
1758 if (item->name && strcmp (item->name, "message") == 0)
1760 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1761 /* This is the text part of the dialog. */
1762 w = gtk_label_new (utf8_label);
1763 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1764 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1765 #if GTK_CHECK_VERSION (3, 14, 0)
1766 gtk_widget_set_halign (w, GTK_ALIGN_START);
1767 gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
1768 #else
1769 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1770 #endif
1771 /* Try to make dialog look better. Must realize first so
1772 the widget can calculate the size it needs. */
1773 gtk_widget_realize (w);
1774 gtk_widget_get_preferred_size (w, NULL, &req);
1775 gtk_box_set_spacing (wvbox, req.height);
1776 if (item->value && strlen (item->value) > 0)
1777 button_spacing = 2*req.width/strlen (item->value);
1778 if (button_spacing < 10) button_spacing = 10;
1780 else
1782 /* This is one button to add to the dialog. */
1783 w = gtk_button_new_with_label (utf8_label);
1784 if (! item->enabled)
1785 gtk_widget_set_sensitive (w, FALSE);
1786 if (select_cb)
1787 g_signal_connect (G_OBJECT (w), "clicked",
1788 select_cb, item->call_data);
1790 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1791 if (++button_nr == left_buttons)
1793 if (make_two_rows)
1794 cur_box = GTK_BOX (whbox_down);
1798 if (utf8_label)
1799 g_free (utf8_label);
1802 return wdialog;
1805 struct xg_dialog_data
1807 GMainLoop *loop;
1808 int response;
1809 GtkWidget *w;
1810 guint timerid;
1813 /* Function that is called when the file or font dialogs pop down.
1814 W is the dialog widget, RESPONSE is the response code.
1815 USER_DATA is what we passed in to g_signal_connect. */
1817 static void
1818 xg_dialog_response_cb (GtkDialog *w,
1819 gint response,
1820 gpointer user_data)
1822 struct xg_dialog_data *dd = user_data;
1823 dd->response = response;
1824 g_main_loop_quit (dd->loop);
1828 /* Destroy the dialog. This makes it pop down. */
1830 static void
1831 pop_down_dialog (void *arg)
1833 struct xg_dialog_data *dd = arg;
1835 block_input ();
1836 if (dd->w) gtk_widget_destroy (dd->w);
1837 if (dd->timerid != 0) g_source_remove (dd->timerid);
1839 g_main_loop_quit (dd->loop);
1840 g_main_loop_unref (dd->loop);
1842 unblock_input ();
1845 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1846 Pass DATA as gpointer so we can use this as a callback. */
1848 static gboolean
1849 xg_maybe_add_timer (gpointer data)
1851 struct xg_dialog_data *dd = data;
1852 struct timespec next_time = timer_check ();
1854 dd->timerid = 0;
1856 if (timespec_valid_p (next_time))
1858 time_t s = next_time.tv_sec;
1859 int per_ms = TIMESPEC_RESOLUTION / 1000;
1860 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
1861 if (s <= ((guint) -1 - ms) / 1000)
1862 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1864 return FALSE;
1868 /* Pops up a modal dialog W and waits for response.
1869 We don't use gtk_dialog_run because we want to process emacs timers.
1870 The dialog W is not destroyed when this function returns. */
1872 static int
1873 xg_dialog_run (struct frame *f, GtkWidget *w)
1875 ptrdiff_t count = SPECPDL_INDEX ();
1876 struct xg_dialog_data dd;
1878 xg_set_screen (w, f);
1879 gtk_window_set_transient_for (GTK_WINDOW (w),
1880 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1881 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1882 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1884 dd.loop = g_main_loop_new (NULL, FALSE);
1885 dd.response = GTK_RESPONSE_CANCEL;
1886 dd.w = w;
1887 dd.timerid = 0;
1889 g_signal_connect (G_OBJECT (w),
1890 "response",
1891 G_CALLBACK (xg_dialog_response_cb),
1892 &dd);
1893 /* Don't destroy the widget if closed by the window manager close button. */
1894 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1895 gtk_widget_show (w);
1897 record_unwind_protect_ptr (pop_down_dialog, &dd);
1899 (void) xg_maybe_add_timer (&dd);
1900 g_main_loop_run (dd.loop);
1902 dd.w = 0;
1903 unbind_to (count, Qnil);
1905 return dd.response;
1909 /***********************************************************************
1910 File dialog functions
1911 ***********************************************************************/
1912 /* Return true if the old file selection dialog is being used. */
1914 bool
1915 xg_uses_old_file_dialog (void)
1917 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1918 return x_gtk_use_old_file_dialog;
1919 #else
1920 return 0;
1921 #endif
1925 typedef char * (*xg_get_file_func) (GtkWidget *);
1927 /* Return the selected file for file chooser dialog W.
1928 The returned string must be free:d. */
1930 static char *
1931 xg_get_file_name_from_chooser (GtkWidget *w)
1933 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1936 /* Callback called when the "Show hidden files" toggle is pressed.
1937 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1939 static void
1940 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1942 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1943 gboolean visible;
1944 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1945 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1949 /* Callback called when a property changes in a file chooser.
1950 GOBJECT is the file chooser dialog, ARG1 describes the property.
1951 USER_DATA is the toggle widget in the file chooser dialog.
1952 We use this to update the "Show hidden files" toggle when the user
1953 changes that property by right clicking in the file list. */
1955 static void
1956 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1958 if (strcmp (arg1->name, "show-hidden") == 0)
1960 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1961 gboolean visible, toggle_on;
1963 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1964 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1966 if (!!visible != !!toggle_on)
1968 gpointer cb = (gpointer) G_CALLBACK (xg_toggle_visibility_cb);
1969 g_signal_handlers_block_by_func (G_OBJECT (wtoggle), cb, gobject);
1970 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1971 g_signal_handlers_unblock_by_func (G_OBJECT (wtoggle), cb, gobject);
1973 x_gtk_show_hidden_files = visible;
1977 /* Read a file name from the user using a file chooser dialog.
1978 F is the current frame.
1979 PROMPT is a prompt to show to the user. May not be NULL.
1980 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1981 If MUSTMATCH_P, the returned file name must be an existing
1982 file. (Actually, this only has cosmetic effects, the user can
1983 still enter a non-existing file.) *FUNC is set to a function that
1984 can be used to retrieve the selected file name from the returned widget.
1986 Returns the created widget. */
1988 static GtkWidget *
1989 xg_get_file_with_chooser (struct frame *f,
1990 char *prompt,
1991 char *default_filename,
1992 bool mustmatch_p, bool only_dir_p,
1993 xg_get_file_func *func)
1995 char msgbuf[1024];
1997 GtkWidget *filewin, *wtoggle, *wbox;
1998 GtkWidget *wmessage UNINIT;
1999 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
2000 GtkFileChooserAction action = (mustmatch_p ?
2001 GTK_FILE_CHOOSER_ACTION_OPEN :
2002 GTK_FILE_CHOOSER_ACTION_SAVE);
2004 if (only_dir_p)
2005 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
2007 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
2008 XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
2009 (mustmatch_p || only_dir_p ?
2010 XG_TEXT_OPEN : XG_TEXT_OK),
2011 GTK_RESPONSE_OK,
2012 NULL);
2013 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
2015 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2016 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2017 gtk_widget_show (wbox);
2018 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
2020 if (x_gtk_show_hidden_files)
2022 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
2023 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
2025 gtk_widget_show (wtoggle);
2026 g_signal_connect (G_OBJECT (wtoggle), "clicked",
2027 G_CALLBACK (xg_toggle_visibility_cb), filewin);
2028 g_signal_connect (G_OBJECT (filewin), "notify",
2029 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
2031 if (x_gtk_file_dialog_help_text)
2033 char *z = msgbuf;
2034 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
2035 Show the C-l help text only for versions < 2.10. */
2036 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
2037 z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
2038 strcpy (z, "\nIf you don't like this file selector, use the "
2039 "corresponding\nkey binding or customize "
2040 "use-file-dialog to turn it off.");
2042 wmessage = gtk_label_new (msgbuf);
2043 gtk_widget_show (wmessage);
2046 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
2047 if (x_gtk_file_dialog_help_text)
2048 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
2049 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
2051 if (default_filename)
2053 Lisp_Object file;
2054 char *utf8_filename;
2056 file = build_string (default_filename);
2058 /* File chooser does not understand ~/... in the file name. It must be
2059 an absolute name starting with /. */
2060 if (default_filename[0] != '/')
2061 file = Fexpand_file_name (file, Qnil);
2063 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
2064 if (! NILP (Ffile_directory_p (file)))
2065 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
2066 utf8_filename);
2067 else
2069 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
2070 utf8_filename);
2071 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
2073 char *cp = strrchr (utf8_filename, '/');
2074 if (cp) ++cp;
2075 else cp = utf8_filename;
2076 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
2081 *func = xg_get_file_name_from_chooser;
2082 return filewin;
2085 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2087 /* Return the selected file for file selector dialog W.
2088 The returned string must be free:d. */
2090 static char *
2091 xg_get_file_name_from_selector (GtkWidget *w)
2093 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
2094 return xstrdup (gtk_file_selection_get_filename (filesel));
2097 /* Create a file selection dialog.
2098 F is the current frame.
2099 PROMPT is a prompt to show to the user. May not be NULL.
2100 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2101 If MUSTMATCH_P, the returned file name must be an existing
2102 file. *FUNC is set to a function that can be used to retrieve the
2103 selected file name from the returned widget.
2105 Returns the created widget. */
2107 static GtkWidget *
2108 xg_get_file_with_selection (struct frame *f,
2109 char *prompt,
2110 char *default_filename,
2111 bool mustmatch_p, bool only_dir_p,
2112 xg_get_file_func *func)
2114 GtkWidget *filewin;
2115 GtkFileSelection *filesel;
2117 filewin = gtk_file_selection_new (prompt);
2118 filesel = GTK_FILE_SELECTION (filewin);
2120 if (default_filename)
2121 gtk_file_selection_set_filename (filesel, default_filename);
2123 if (mustmatch_p)
2125 /* The selection_entry part of filesel is not documented. */
2126 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
2127 gtk_file_selection_hide_fileop_buttons (filesel);
2130 *func = xg_get_file_name_from_selector;
2132 return filewin;
2134 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
2136 /* Read a file name from the user using a file dialog, either the old
2137 file selection dialog, or the new file chooser dialog. Which to use
2138 depends on what the GTK version used has, and what the value of
2139 gtk-use-old-file-dialog.
2140 F is the current frame.
2141 PROMPT is a prompt to show to the user. May not be NULL.
2142 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2143 If MUSTMATCH_P, the returned file name must be an existing
2144 file.
2146 Returns a file name or NULL if no file was selected.
2147 The returned string must be freed by the caller. */
2149 char *
2150 xg_get_file_name (struct frame *f,
2151 char *prompt,
2152 char *default_filename,
2153 bool mustmatch_p,
2154 bool only_dir_p)
2156 GtkWidget *w = 0;
2157 char *fn = 0;
2158 int filesel_done = 0;
2159 xg_get_file_func func;
2161 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2163 if (xg_uses_old_file_dialog ())
2164 w = xg_get_file_with_selection (f, prompt, default_filename,
2165 mustmatch_p, only_dir_p, &func);
2166 else
2167 w = xg_get_file_with_chooser (f, prompt, default_filename,
2168 mustmatch_p, only_dir_p, &func);
2170 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
2171 w = xg_get_file_with_chooser (f, prompt, default_filename,
2172 mustmatch_p, only_dir_p, &func);
2173 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
2175 gtk_widget_set_name (w, "emacs-filedialog");
2177 filesel_done = xg_dialog_run (f, w);
2178 if (filesel_done == GTK_RESPONSE_OK)
2179 fn = (*func) (w);
2181 gtk_widget_destroy (w);
2182 return fn;
2185 /***********************************************************************
2186 GTK font chooser
2187 ***********************************************************************/
2189 #ifdef HAVE_FREETYPE
2191 #if USE_NEW_GTK_FONT_CHOOSER
2193 #define XG_WEIGHT_TO_SYMBOL(w) \
2194 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2195 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2196 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2197 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2198 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2199 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2200 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2201 : Qultra_bold)
2203 #define XG_STYLE_TO_SYMBOL(s) \
2204 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2205 : s == PANGO_STYLE_ITALIC ? Qitalic \
2206 : Qnormal)
2208 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2211 static char *x_last_font_name;
2213 /* Pop up a GTK font selector and return the name of the font the user
2214 selects, as a C string. The returned font name follows GTK's own
2215 format:
2217 `FAMILY [VALUE1 VALUE2] SIZE'
2219 This can be parsed using font_parse_fcname in font.c.
2220 DEFAULT_NAME, if non-zero, is the default font name. */
2222 Lisp_Object
2223 xg_get_font (struct frame *f, const char *default_name)
2225 GtkWidget *w;
2226 int done = 0;
2227 Lisp_Object font = Qnil;
2229 w = gtk_font_chooser_dialog_new
2230 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2232 if (default_name)
2234 /* Convert fontconfig names to Gtk names, i.e. remove - before
2235 number */
2236 char *p = strrchr (default_name, '-');
2237 if (p)
2239 char *ep = p+1;
2240 while (c_isdigit (*ep))
2241 ++ep;
2242 if (*ep == '\0') *p = ' ';
2245 else if (x_last_font_name)
2246 default_name = x_last_font_name;
2248 if (default_name)
2249 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2251 gtk_widget_set_name (w, "emacs-fontdialog");
2252 done = xg_dialog_run (f, w);
2253 if (done == GTK_RESPONSE_OK)
2255 #if USE_NEW_GTK_FONT_CHOOSER
2256 /* Use the GTK3 font chooser. */
2257 PangoFontDescription *desc
2258 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2260 if (desc)
2262 const char *name = pango_font_description_get_family (desc);
2263 gint size = pango_font_description_get_size (desc);
2264 PangoWeight weight = pango_font_description_get_weight (desc);
2265 PangoStyle style = pango_font_description_get_style (desc);
2267 #ifdef USE_CAIRO
2268 #define FONT_TYPE_WANTED (Qftcr)
2269 #else
2270 #define FONT_TYPE_WANTED (Qxft)
2271 #endif
2272 font = CALLN (Ffont_spec,
2273 QCname, build_string (name),
2274 QCsize, make_float (pango_units_to_double (size)),
2275 QCweight, XG_WEIGHT_TO_SYMBOL (weight),
2276 QCslant, XG_STYLE_TO_SYMBOL (style),
2277 QCtype,
2278 FONT_TYPE_WANTED);
2280 pango_font_description_free (desc);
2281 dupstring (&x_last_font_name, name);
2284 #else /* Use old font selector, which just returns the font name. */
2286 char *font_name
2287 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2289 if (font_name)
2291 font = build_string (font_name);
2292 g_free (x_last_font_name);
2293 x_last_font_name = font_name;
2295 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2298 gtk_widget_destroy (w);
2299 return font;
2301 #endif /* HAVE_FREETYPE */
2305 /***********************************************************************
2306 Menu functions.
2307 ***********************************************************************/
2309 /* The name of menu items that can be used for customization. Since GTK
2310 RC files are very crude and primitive, we have to set this on all
2311 menu item names so a user can easily customize menu items. */
2313 #define MENU_ITEM_NAME "emacs-menuitem"
2316 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2317 during GC. The next member points to the items. */
2318 static xg_list_node xg_menu_cb_list;
2320 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2321 during GC. The next member points to the items. */
2322 static xg_list_node xg_menu_item_cb_list;
2324 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2325 F is the frame CL_DATA will be initialized for.
2326 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2328 The menu bar and all sub menus under the menu bar in a frame
2329 share the same structure, hence the reference count.
2331 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2332 allocated xg_menu_cb_data if CL_DATA is NULL. */
2334 static xg_menu_cb_data *
2335 make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
2337 if (! cl_data)
2339 cl_data = xmalloc (sizeof *cl_data);
2340 cl_data->f = f;
2341 cl_data->menu_bar_vector = f->menu_bar_vector;
2342 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2343 cl_data->highlight_cb = highlight_cb;
2344 cl_data->ref_count = 0;
2346 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2349 cl_data->ref_count++;
2351 return cl_data;
2354 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2355 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2357 When the menu bar is updated, menu items may have been added and/or
2358 removed, so menu_bar_vector and menu_bar_items_used change. We must
2359 then update CL_DATA since it is used to determine which menu
2360 item that is invoked in the menu.
2361 HIGHLIGHT_CB could change, there is no check that the same
2362 function is given when modifying a menu bar as was given when
2363 creating the menu bar. */
2365 static void
2366 update_cl_data (xg_menu_cb_data *cl_data,
2367 struct frame *f,
2368 GCallback highlight_cb)
2370 if (cl_data)
2372 cl_data->f = f;
2373 cl_data->menu_bar_vector = f->menu_bar_vector;
2374 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2375 cl_data->highlight_cb = highlight_cb;
2379 /* Decrease reference count for CL_DATA.
2380 If reference count is zero, free CL_DATA. */
2382 static void
2383 unref_cl_data (xg_menu_cb_data *cl_data)
2385 if (cl_data && cl_data->ref_count > 0)
2387 cl_data->ref_count--;
2388 if (cl_data->ref_count == 0)
2390 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2391 xfree (cl_data);
2396 /* Function that marks all lisp data during GC. */
2398 void
2399 xg_mark_data (void)
2401 xg_list_node *iter;
2402 Lisp_Object rest, frame;
2404 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2405 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2407 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2409 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2411 if (! NILP (cb_data->help))
2412 mark_object (cb_data->help);
2415 FOR_EACH_FRAME (rest, frame)
2417 struct frame *f = XFRAME (frame);
2419 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2421 struct xg_frame_tb_info *tbinfo
2422 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2423 TB_INFO_KEY);
2424 if (tbinfo)
2426 mark_object (tbinfo->last_tool_bar);
2427 mark_object (tbinfo->style);
2433 /* Callback called when a menu item is destroyed. Used to free data.
2434 W is the widget that is being destroyed (not used).
2435 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2437 static void
2438 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2440 if (client_data)
2442 xg_menu_item_cb_data *data = client_data;
2443 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2444 xfree (data);
2448 /* Callback called when the pointer enters/leaves a menu item.
2449 W is the parent of the menu item.
2450 EVENT is either an enter event or leave event.
2451 CLIENT_DATA is not used.
2453 Returns FALSE to tell GTK to keep processing this event. */
2455 static gboolean
2456 menuitem_highlight_callback (GtkWidget *w,
2457 GdkEventCrossing *event,
2458 gpointer client_data)
2460 GdkEvent ev;
2461 GtkWidget *subwidget;
2462 xg_menu_item_cb_data *data;
2464 ev.crossing = *event;
2465 subwidget = gtk_get_event_widget (&ev);
2466 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
2467 if (data)
2469 if (! NILP (data->help) && data->cl_data->highlight_cb)
2471 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2472 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2473 (*func) (subwidget, call_data);
2477 return FALSE;
2480 /* Callback called when a menu is destroyed. Used to free data.
2481 W is the widget that is being destroyed (not used).
2482 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2484 static void
2485 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2487 unref_cl_data (client_data);
2490 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2491 must be non-NULL) and can be inserted into a menu item.
2493 Returns the GtkHBox. */
2495 static GtkWidget *
2496 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2498 GtkWidget *wlbl;
2499 GtkWidget *wkey;
2500 GtkWidget *wbox;
2502 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2503 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2504 wlbl = gtk_label_new (utf8_label);
2505 wkey = gtk_label_new (utf8_key);
2507 #if GTK_CHECK_VERSION (3, 14, 0)
2508 gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
2509 gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
2510 gtk_widget_set_halign (wkey, GTK_ALIGN_START);
2511 gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
2512 #else
2513 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2514 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2515 #endif
2516 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2517 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2519 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2520 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2521 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2523 return wbox;
2526 /* Make and return a menu item widget with the key to the right.
2527 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2528 UTF8_KEY is the text representing the key binding.
2529 ITEM is the widget_value describing the menu item.
2531 GROUP is an in/out parameter. If the menu item to be created is not
2532 part of any radio menu group, *GROUP contains NULL on entry and exit.
2533 If the menu item to be created is part of a radio menu group, on entry
2534 *GROUP contains the group to use, or NULL if this is the first item
2535 in the group. On exit, *GROUP contains the radio item group.
2537 Unfortunately, keys don't line up as nicely as in Motif,
2538 but the macOS version doesn't either, so I guess that is OK. */
2540 static GtkWidget *
2541 make_menu_item (const char *utf8_label,
2542 const char *utf8_key,
2543 widget_value *item,
2544 GSList **group)
2546 GtkWidget *w;
2547 GtkWidget *wtoadd = 0;
2549 /* It has been observed that some menu items have a NULL name field.
2550 This will lead to this function being called with a NULL utf8_label.
2551 GTK crashes on that so we set a blank label. Why there is a NULL
2552 name remains to be investigated. */
2553 if (! utf8_label) utf8_label = " ";
2555 if (utf8_key)
2556 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2558 if (item->button_type == BUTTON_TYPE_TOGGLE)
2560 *group = NULL;
2561 if (utf8_key) w = gtk_check_menu_item_new ();
2562 else w = gtk_check_menu_item_new_with_label (utf8_label);
2563 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2565 else if (item->button_type == BUTTON_TYPE_RADIO)
2567 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2568 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2569 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2570 if (item->selected)
2571 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2573 else
2575 *group = NULL;
2576 if (utf8_key) w = gtk_menu_item_new ();
2577 else w = gtk_menu_item_new_with_label (utf8_label);
2580 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2581 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2583 return w;
2586 /* Create a menu item widget, and connect the callbacks.
2587 ITEM describes the menu item.
2588 F is the frame the created menu belongs to.
2589 SELECT_CB is the callback to use when a menu item is selected.
2590 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2591 CL_DATA points to the callback data to be used for this menu.
2592 GROUP is an in/out parameter. If the menu item to be created is not
2593 part of any radio menu group, *GROUP contains NULL on entry and exit.
2594 If the menu item to be created is part of a radio menu group, on entry
2595 *GROUP contains the group to use, or NULL if this is the first item
2596 in the group. On exit, *GROUP contains the radio item group.
2598 Returns the created GtkWidget. */
2600 static GtkWidget *
2601 xg_create_one_menuitem (widget_value *item,
2602 struct frame *f,
2603 GCallback select_cb,
2604 GCallback highlight_cb,
2605 xg_menu_cb_data *cl_data,
2606 GSList **group)
2608 char *utf8_label;
2609 char *utf8_key;
2610 GtkWidget *w;
2611 xg_menu_item_cb_data *cb_data;
2613 utf8_label = get_utf8_string (item->name);
2614 utf8_key = get_utf8_string (item->key);
2616 w = make_menu_item (utf8_label, utf8_key, item, group);
2618 if (utf8_label) g_free (utf8_label);
2619 if (utf8_key) g_free (utf8_key);
2621 cb_data = xmalloc (sizeof *cb_data);
2623 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2625 cb_data->select_id = 0;
2626 cb_data->help = item->help;
2627 cb_data->cl_data = cl_data;
2628 cb_data->call_data = item->call_data;
2630 g_signal_connect (G_OBJECT (w),
2631 "destroy",
2632 G_CALLBACK (menuitem_destroy_callback),
2633 cb_data);
2635 /* Put cb_data in widget, so we can get at it when modifying menubar */
2636 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2638 /* final item, not a submenu */
2639 if (item->call_data && ! item->contents)
2641 if (select_cb)
2642 cb_data->select_id
2643 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2646 return w;
2649 /* Create a full menu tree specified by DATA.
2650 F is the frame the created menu belongs to.
2651 SELECT_CB is the callback to use when a menu item is selected.
2652 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2653 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2654 If POP_UP_P, create a popup menu.
2655 If MENU_BAR_P, create a menu bar.
2656 TOPMENU is the topmost GtkWidget that others shall be placed under.
2657 It may be NULL, in that case we create the appropriate widget
2658 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2659 CL_DATA is the callback data we shall use for this menu, or NULL
2660 if we haven't set the first callback yet.
2661 NAME is the name to give to the top level menu if this function
2662 creates it. May be NULL to not set any name.
2664 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2665 not NULL.
2667 This function calls itself to create submenus. */
2669 static GtkWidget *
2670 create_menus (widget_value *data,
2671 struct frame *f,
2672 GCallback select_cb,
2673 GCallback deactivate_cb,
2674 GCallback highlight_cb,
2675 bool pop_up_p,
2676 bool menu_bar_p,
2677 GtkWidget *topmenu,
2678 xg_menu_cb_data *cl_data,
2679 const char *name)
2681 widget_value *item;
2682 GtkWidget *wmenu = topmenu;
2683 GSList *group = NULL;
2685 if (! topmenu)
2687 if (! menu_bar_p)
2689 wmenu = gtk_menu_new ();
2690 xg_set_screen (wmenu, f);
2691 /* Connect this to the menu instead of items so we get enter/leave for
2692 disabled items also. TODO: Still does not get enter/leave for
2693 disabled items in detached menus. */
2694 g_signal_connect (G_OBJECT (wmenu),
2695 "enter-notify-event",
2696 G_CALLBACK (menuitem_highlight_callback),
2697 NULL);
2698 g_signal_connect (G_OBJECT (wmenu),
2699 "leave-notify-event",
2700 G_CALLBACK (menuitem_highlight_callback),
2701 NULL);
2703 else
2705 wmenu = gtk_menu_bar_new ();
2706 /* Set width of menu bar to a small value so it doesn't enlarge
2707 a small initial frame size. The width will be set to the
2708 width of the frame later on when it is added to a container.
2709 height -1: Natural height. */
2710 gtk_widget_set_size_request (wmenu, 1, -1);
2713 /* Put cl_data on the top menu for easier access. */
2714 cl_data = make_cl_data (cl_data, f, highlight_cb);
2715 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2716 g_signal_connect (G_OBJECT (wmenu), "destroy",
2717 G_CALLBACK (menu_destroy_callback), cl_data);
2719 if (name)
2720 gtk_widget_set_name (wmenu, name);
2722 if (deactivate_cb)
2723 g_signal_connect (G_OBJECT (wmenu),
2724 "selection-done", deactivate_cb, 0);
2727 for (item = data; item; item = item->next)
2729 GtkWidget *w;
2731 if (pop_up_p && !item->contents && !item->call_data
2732 && !menu_separator_name_p (item->name))
2734 char *utf8_label;
2735 /* A title for a popup. We do the same as GTK does when
2736 creating titles, but it does not look good. */
2737 group = NULL;
2738 utf8_label = get_utf8_string (item->name);
2740 w = gtk_menu_item_new_with_label (utf8_label);
2741 gtk_widget_set_sensitive (w, FALSE);
2742 if (utf8_label) g_free (utf8_label);
2744 else if (menu_separator_name_p (item->name))
2746 group = NULL;
2747 /* GTK only have one separator type. */
2748 w = gtk_separator_menu_item_new ();
2750 else
2752 w = xg_create_one_menuitem (item,
2754 item->contents ? 0 : select_cb,
2755 highlight_cb,
2756 cl_data,
2757 &group);
2759 /* Create a possibly empty submenu for menu bar items, since some
2760 themes don't highlight items correctly without it. */
2761 if (item->contents || menu_bar_p)
2763 GtkWidget *submenu = create_menus (item->contents,
2765 select_cb,
2766 deactivate_cb,
2767 highlight_cb,
2771 cl_data,
2773 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2777 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2778 gtk_widget_set_name (w, MENU_ITEM_NAME);
2781 return wmenu;
2784 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2785 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2786 with some text and buttons.
2787 F is the frame the created item belongs to.
2788 NAME is the name to use for the top widget.
2789 VAL is a widget_value structure describing items to be created.
2790 SELECT_CB is the callback to use when a menu item is selected or
2791 a dialog button is pressed.
2792 DEACTIVATE_CB is the callback to use when an item is deactivated.
2793 For a menu, when a sub menu is not shown anymore, for a dialog it is
2794 called when the dialog is popped down.
2795 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2797 Returns the widget created. */
2799 GtkWidget *
2800 xg_create_widget (const char *type, const char *name, struct frame *f,
2801 widget_value *val, GCallback select_cb,
2802 GCallback deactivate_cb, GCallback highlight_cb)
2804 GtkWidget *w = 0;
2805 bool menu_bar_p = strcmp (type, "menubar") == 0;
2806 bool pop_up_p = strcmp (type, "popup") == 0;
2808 if (strcmp (type, "dialog") == 0)
2810 w = create_dialog (val, select_cb, deactivate_cb);
2811 xg_set_screen (w, f);
2812 gtk_window_set_transient_for (GTK_WINDOW (w),
2813 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2814 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2815 gtk_widget_set_name (w, "emacs-dialog");
2816 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2818 else if (menu_bar_p || pop_up_p)
2820 w = create_menus (val->contents,
2822 select_cb,
2823 deactivate_cb,
2824 highlight_cb,
2825 pop_up_p,
2826 menu_bar_p,
2829 name);
2831 /* Set the cursor to an arrow for popup menus when they are mapped.
2832 This is done by default for menu bar menus. */
2833 if (pop_up_p)
2835 /* Must realize so the GdkWindow inside the widget is created. */
2836 gtk_widget_realize (w);
2837 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
2840 else
2842 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2843 type);
2846 return w;
2849 /* Return the label for menu item WITEM. */
2851 static const char *
2852 xg_get_menu_item_label (GtkMenuItem *witem)
2854 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2855 return gtk_label_get_label (wlabel);
2858 /* Return true if the menu item WITEM has the text LABEL. */
2860 static bool
2861 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2863 bool is_same = 0;
2864 char *utf8_label = get_utf8_string (label);
2865 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2867 if (! old_label && ! utf8_label)
2868 is_same = 1;
2869 else if (old_label && utf8_label)
2870 is_same = strcmp (utf8_label, old_label) == 0;
2872 if (utf8_label) g_free (utf8_label);
2874 return is_same;
2877 /* Destroy widgets in LIST. */
2879 static void
2880 xg_destroy_widgets (GList *list)
2882 GList *iter;
2884 for (iter = list; iter; iter = g_list_next (iter))
2886 GtkWidget *w = GTK_WIDGET (iter->data);
2888 /* Destroying the widget will remove it from the container it is in. */
2889 gtk_widget_destroy (w);
2893 /* Update the top level names in MENUBAR (i.e. not submenus).
2894 F is the frame the menu bar belongs to.
2895 *LIST is a list with the current menu bar names (menu item widgets).
2896 ITER is the item within *LIST that shall be updated.
2897 POS is the numerical position, starting at 0, of ITER in *LIST.
2898 VAL describes what the menu bar shall look like after the update.
2899 SELECT_CB is the callback to use when a menu item is selected.
2900 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2901 CL_DATA points to the callback data to be used for this menu bar.
2903 This function calls itself to walk through the menu bar names. */
2905 static void
2906 xg_update_menubar (GtkWidget *menubar,
2907 struct frame *f,
2908 GList **list,
2909 GList *iter,
2910 int pos,
2911 widget_value *val,
2912 GCallback select_cb,
2913 GCallback deactivate_cb,
2914 GCallback highlight_cb,
2915 xg_menu_cb_data *cl_data)
2917 if (! iter && ! val)
2918 return;
2919 else if (iter && ! val)
2921 /* Item(s) have been removed. Remove all remaining items. */
2922 xg_destroy_widgets (iter);
2924 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2925 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2926 gtk_menu_item_new_with_label (""),
2928 /* All updated. */
2929 val = 0;
2930 iter = 0;
2932 else if (! iter && val)
2934 /* Item(s) added. Add all new items in one call. */
2935 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2936 0, 1, menubar, cl_data, 0);
2938 /* All updated. */
2939 val = 0;
2940 iter = 0;
2942 /* Below this neither iter or val is NULL */
2943 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2945 /* This item is still the same, check next item. */
2946 val = val->next;
2947 iter = g_list_next (iter);
2948 ++pos;
2950 else /* This item is changed. */
2952 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2953 GtkMenuItem *witem2 = 0;
2954 bool val_in_menubar = 0;
2955 bool iter_in_new_menubar = 0;
2956 GList *iter2;
2957 widget_value *cur;
2959 /* See if the changed entry (val) is present later in the menu bar */
2960 for (iter2 = iter;
2961 iter2 && ! val_in_menubar;
2962 iter2 = g_list_next (iter2))
2964 witem2 = GTK_MENU_ITEM (iter2->data);
2965 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2968 /* See if the current entry (iter) is present later in the
2969 specification for the new menu bar. */
2970 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2971 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2973 if (val_in_menubar && ! iter_in_new_menubar)
2975 int nr = pos;
2977 /* This corresponds to:
2978 Current: A B C
2979 New: A C
2980 Remove B. */
2982 g_object_ref (G_OBJECT (witem));
2983 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2984 gtk_widget_destroy (GTK_WIDGET (witem));
2986 /* Must get new list since the old changed. */
2987 g_list_free (*list);
2988 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2989 while (nr-- > 0) iter = g_list_next (iter);
2991 else if (! val_in_menubar && ! iter_in_new_menubar)
2993 /* This corresponds to:
2994 Current: A B C
2995 New: A X C
2996 Rename B to X. This might seem to be a strange thing to do,
2997 since if there is a menu under B it will be totally wrong for X.
2998 But consider editing a C file. Then there is a C-mode menu
2999 (corresponds to B above).
3000 If then doing C-x C-f the minibuf menu (X above) replaces the
3001 C-mode menu. When returning from the minibuffer, we get
3002 back the C-mode menu. Thus we do:
3003 Rename B to X (C-mode to minibuf menu)
3004 Rename X to B (minibuf to C-mode menu).
3005 If the X menu hasn't been invoked, the menu under B
3006 is up to date when leaving the minibuffer. */
3007 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
3008 char *utf8_label = get_utf8_string (val->name);
3010 /* GTK menu items don't notice when their labels have been
3011 changed from underneath them, so we have to explicitly
3012 use g_object_notify to tell listeners (e.g., a GMenuModel
3013 bridge that might be loaded) that the item's label has
3014 changed. */
3015 gtk_label_set_text (wlabel, utf8_label);
3016 #if GTK_CHECK_VERSION (2, 16, 0)
3017 g_object_notify (G_OBJECT (witem), "label");
3018 #endif
3019 if (utf8_label) g_free (utf8_label);
3020 iter = g_list_next (iter);
3021 val = val->next;
3022 ++pos;
3024 else if (! val_in_menubar && iter_in_new_menubar)
3026 /* This corresponds to:
3027 Current: A B C
3028 New: A X B C
3029 Insert X. */
3031 int nr = pos;
3032 GSList *group = 0;
3033 GtkWidget *w = xg_create_one_menuitem (val,
3035 select_cb,
3036 highlight_cb,
3037 cl_data,
3038 &group);
3040 /* Create a possibly empty submenu for menu bar items, since some
3041 themes don't highlight items correctly without it. */
3042 GtkWidget *submenu = create_menus (NULL, f,
3043 select_cb, deactivate_cb,
3044 highlight_cb,
3045 0, 0, 0, cl_data, 0);
3047 gtk_widget_set_name (w, MENU_ITEM_NAME);
3048 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
3049 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
3051 g_list_free (*list);
3052 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
3053 while (nr-- > 0) iter = g_list_next (iter);
3054 iter = g_list_next (iter);
3055 val = val->next;
3056 ++pos;
3058 else /* if (val_in_menubar && iter_in_new_menubar) */
3060 int nr = pos;
3061 /* This corresponds to:
3062 Current: A B C
3063 New: A C B
3064 Move C before B */
3066 g_object_ref (G_OBJECT (witem2));
3067 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
3068 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
3069 GTK_WIDGET (witem2), pos);
3070 g_object_unref (G_OBJECT (witem2));
3072 g_list_free (*list);
3073 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
3074 while (nr-- > 0) iter = g_list_next (iter);
3075 if (iter) iter = g_list_next (iter);
3076 val = val->next;
3077 ++pos;
3081 /* Update the rest of the menu bar. */
3082 xg_update_menubar (menubar, f, list, iter, pos, val,
3083 select_cb, deactivate_cb, highlight_cb, cl_data);
3086 /* Update the menu item W so it corresponds to VAL.
3087 SELECT_CB is the callback to use when a menu item is selected.
3088 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3089 CL_DATA is the data to set in the widget for menu invocation. */
3091 static void
3092 xg_update_menu_item (widget_value *val,
3093 GtkWidget *w,
3094 GCallback select_cb,
3095 GCallback highlight_cb,
3096 xg_menu_cb_data *cl_data)
3098 GtkWidget *wchild;
3099 GtkLabel *wlbl = 0;
3100 GtkLabel *wkey = 0;
3101 char *utf8_label;
3102 char *utf8_key;
3103 const char *old_label = 0;
3104 const char *old_key = 0;
3105 xg_menu_item_cb_data *cb_data;
3106 bool label_changed = false;
3108 wchild = XG_BIN_CHILD (w);
3109 utf8_label = get_utf8_string (val->name);
3110 utf8_key = get_utf8_string (val->key);
3112 /* See if W is a menu item with a key. See make_menu_item above. */
3113 if (GTK_IS_BOX (wchild))
3115 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
3117 wlbl = GTK_LABEL (list->data);
3118 wkey = GTK_LABEL (list->next->data);
3119 g_list_free (list);
3121 if (! utf8_key)
3123 /* Remove the key and keep just the label. */
3124 g_object_ref (G_OBJECT (wlbl));
3125 gtk_container_remove (GTK_CONTAINER (w), wchild);
3126 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
3127 g_object_unref (G_OBJECT (wlbl));
3128 wkey = 0;
3132 else /* Just a label. */
3134 wlbl = GTK_LABEL (wchild);
3136 /* Check if there is now a key. */
3137 if (utf8_key)
3139 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3140 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
3142 wlbl = GTK_LABEL (list->data);
3143 wkey = GTK_LABEL (list->next->data);
3144 g_list_free (list);
3146 gtk_container_remove (GTK_CONTAINER (w), wchild);
3147 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3151 if (wkey) old_key = gtk_label_get_label (wkey);
3152 if (wlbl) old_label = gtk_label_get_label (wlbl);
3154 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3156 label_changed = true;
3157 gtk_label_set_text (wkey, utf8_key);
3160 if (! old_label || strcmp (utf8_label, old_label) != 0)
3162 label_changed = true;
3163 gtk_label_set_text (wlbl, utf8_label);
3166 if (utf8_key) g_free (utf8_key);
3167 if (utf8_label) g_free (utf8_label);
3169 if (! val->enabled && gtk_widget_get_sensitive (w))
3170 gtk_widget_set_sensitive (w, FALSE);
3171 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3172 gtk_widget_set_sensitive (w, TRUE);
3174 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
3175 if (cb_data)
3177 cb_data->call_data = val->call_data;
3178 cb_data->help = val->help;
3179 cb_data->cl_data = cl_data;
3181 /* We assume the callback functions don't change. */
3182 if (val->call_data && ! val->contents)
3184 /* This item shall have a select callback. */
3185 if (! cb_data->select_id)
3186 cb_data->select_id
3187 = g_signal_connect (G_OBJECT (w), "activate",
3188 select_cb, cb_data);
3190 else if (cb_data->select_id)
3192 g_signal_handler_disconnect (w, cb_data->select_id);
3193 cb_data->select_id = 0;
3197 #if GTK_CHECK_VERSION (2, 16, 0)
3198 if (label_changed) /* See comment in xg_update_menubar. */
3199 g_object_notify (G_OBJECT (w), "label");
3200 #endif
3203 /* Update the toggle menu item W so it corresponds to VAL. */
3205 static void
3206 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3208 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3211 /* Update the radio menu item W so it corresponds to VAL. */
3213 static void
3214 xg_update_radio_item (widget_value *val, GtkWidget *w)
3216 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3219 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3220 SUBMENU may be NULL, in that case a new menu is created.
3221 F is the frame the menu bar belongs to.
3222 VAL describes the contents of the menu bar.
3223 SELECT_CB is the callback to use when a menu item is selected.
3224 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3225 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3226 CL_DATA is the call back data to use for any newly created items.
3228 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3229 was NULL. */
3231 static GtkWidget *
3232 xg_update_submenu (GtkWidget *submenu,
3233 struct frame *f,
3234 widget_value *val,
3235 GCallback select_cb,
3236 GCallback deactivate_cb,
3237 GCallback highlight_cb,
3238 xg_menu_cb_data *cl_data)
3240 GtkWidget *newsub = submenu;
3241 GList *list = 0;
3242 GList *iter;
3243 widget_value *cur;
3244 GList *first_radio = 0;
3246 if (submenu)
3247 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3249 for (cur = val, iter = list;
3250 cur && iter;
3251 iter = g_list_next (iter), cur = cur->next)
3253 GtkWidget *w = GTK_WIDGET (iter->data);
3255 /* Remember first radio button in a group. If we get a mismatch in
3256 a radio group we must rebuild the whole group so that the connections
3257 in GTK becomes correct. */
3258 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3259 first_radio = iter;
3260 else if (cur->button_type != BUTTON_TYPE_RADIO
3261 && ! GTK_IS_RADIO_MENU_ITEM (w))
3262 first_radio = 0;
3264 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3266 if (! menu_separator_name_p (cur->name))
3267 break;
3269 else if (GTK_IS_CHECK_MENU_ITEM (w))
3271 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3272 break;
3273 xg_update_toggle_item (cur, w);
3274 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3276 else if (GTK_IS_RADIO_MENU_ITEM (w))
3278 if (cur->button_type != BUTTON_TYPE_RADIO)
3279 break;
3280 xg_update_radio_item (cur, w);
3281 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3283 else if (GTK_IS_MENU_ITEM (w))
3285 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3286 GtkWidget *sub;
3288 if (cur->button_type != BUTTON_TYPE_NONE ||
3289 menu_separator_name_p (cur->name))
3290 break;
3292 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3294 sub = gtk_menu_item_get_submenu (witem);
3295 if (sub && ! cur->contents)
3297 /* Not a submenu anymore. */
3298 g_object_ref (G_OBJECT (sub));
3299 remove_submenu (witem);
3300 gtk_widget_destroy (sub);
3302 else if (cur->contents)
3304 GtkWidget *nsub;
3306 nsub = xg_update_submenu (sub, f, cur->contents,
3307 select_cb, deactivate_cb,
3308 highlight_cb, cl_data);
3310 /* If this item just became a submenu, we must set it. */
3311 if (nsub != sub)
3312 gtk_menu_item_set_submenu (witem, nsub);
3315 else
3317 /* Structural difference. Remove everything from here and down
3318 in SUBMENU. */
3319 break;
3323 /* Remove widgets from first structural change. */
3324 if (iter)
3326 /* If we are adding new menu items below, we must remove from
3327 first radio button so that radio groups become correct. */
3328 if (cur && first_radio) xg_destroy_widgets (first_radio);
3329 else xg_destroy_widgets (iter);
3332 if (cur)
3334 /* More items added. Create them. */
3335 newsub = create_menus (cur,
3337 select_cb,
3338 deactivate_cb,
3339 highlight_cb,
3342 submenu,
3343 cl_data,
3347 if (list) g_list_free (list);
3349 return newsub;
3352 /* Update the MENUBAR.
3353 F is the frame the menu bar belongs to.
3354 VAL describes the contents of the menu bar.
3355 If DEEP_P, rebuild all but the top level menu names in
3356 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3357 SELECT_CB is the callback to use when a menu item is selected.
3358 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3359 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3361 void
3362 xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3363 widget_value *val, bool deep_p,
3364 GCallback select_cb, GCallback deactivate_cb,
3365 GCallback highlight_cb)
3367 xg_menu_cb_data *cl_data;
3368 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3370 if (! list) return;
3372 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
3374 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3375 select_cb, deactivate_cb, highlight_cb, cl_data);
3377 if (deep_p)
3379 widget_value *cur;
3381 /* Update all sub menus.
3382 We must keep the submenus (GTK menu item widgets) since the
3383 X Window in the XEvent that activates the menu are those widgets. */
3385 /* Update cl_data, menu_item things in F may have changed. */
3386 update_cl_data (cl_data, f, highlight_cb);
3388 for (cur = val->contents; cur; cur = cur->next)
3390 GList *iter;
3391 GtkWidget *sub = 0;
3392 GtkWidget *newsub;
3393 GtkMenuItem *witem = 0;
3395 /* Find sub menu that corresponds to val and update it. */
3396 for (iter = list ; iter; iter = g_list_next (iter))
3398 witem = GTK_MENU_ITEM (iter->data);
3399 if (xg_item_label_same_p (witem, cur->name))
3401 sub = gtk_menu_item_get_submenu (witem);
3402 break;
3406 newsub = xg_update_submenu (sub,
3408 cur->contents,
3409 select_cb,
3410 deactivate_cb,
3411 highlight_cb,
3412 cl_data);
3413 /* sub may still be NULL. If we just updated non deep and added
3414 a new menu bar item, it has no sub menu yet. So we set the
3415 newly created sub menu under witem. */
3416 if (newsub != sub && witem != 0)
3418 xg_set_screen (newsub, f);
3419 gtk_menu_item_set_submenu (witem, newsub);
3424 g_list_free (list);
3425 gtk_widget_show_all (menubar);
3428 /* Callback called when the menu bar W is mapped.
3429 Used to find the height of the menu bar if we didn't get it
3430 after showing the widget. */
3432 static void
3433 menubar_map_cb (GtkWidget *w, gpointer user_data)
3435 GtkRequisition req;
3436 struct frame *f = user_data;
3437 gtk_widget_get_preferred_size (w, NULL, &req);
3438 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3440 FRAME_MENUBAR_HEIGHT (f) = req.height;
3441 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3445 /* Recompute all the widgets of frame F, when the menu bar has been
3446 changed. */
3448 void
3449 xg_update_frame_menubar (struct frame *f)
3451 struct x_output *x = f->output_data.x;
3452 GtkRequisition req;
3454 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3455 return;
3457 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3458 return; /* Already done this, happens for frames created invisible. */
3460 block_input ();
3462 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3463 FALSE, FALSE, 0);
3464 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3466 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3467 gtk_widget_show_all (x->menubar_widget);
3468 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3470 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3472 FRAME_MENUBAR_HEIGHT (f) = req.height;
3473 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3475 unblock_input ();
3478 /* Get rid of the menu bar of frame F, and free its storage.
3479 This is used when deleting a frame, and when turning off the menu bar. */
3481 void
3482 free_frame_menubar (struct frame *f)
3484 struct x_output *x = f->output_data.x;
3486 if (x->menubar_widget)
3488 block_input ();
3490 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3491 /* The menubar and its children shall be deleted when removed from
3492 the container. */
3493 x->menubar_widget = 0;
3494 FRAME_MENUBAR_HEIGHT (f) = 0;
3495 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3496 unblock_input ();
3500 bool
3501 xg_event_is_for_menubar (struct frame *f, const XEvent *event)
3503 struct x_output *x = f->output_data.x;
3504 GList *iter;
3505 GdkRectangle rec;
3506 GList *list;
3507 GdkDisplay *gdpy;
3508 GdkWindow *gw;
3509 GdkEvent gevent;
3510 GtkWidget *gwdesc;
3512 if (! x->menubar_widget) return 0;
3514 if (! (event->xbutton.x >= 0
3515 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3516 && event->xbutton.y >= 0
3517 && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
3518 && event->xbutton.same_screen))
3519 return 0;
3521 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3522 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3523 if (! gw) return 0;
3524 gevent.any.window = gw;
3525 gevent.any.type = GDK_NOTHING;
3526 gwdesc = gtk_get_event_widget (&gevent);
3527 if (! gwdesc) return 0;
3528 if (! GTK_IS_MENU_BAR (gwdesc)
3529 && ! GTK_IS_MENU_ITEM (gwdesc)
3530 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3531 return 0;
3533 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3534 if (! list) return 0;
3535 rec.x = event->xbutton.x;
3536 rec.y = event->xbutton.y;
3537 rec.width = 1;
3538 rec.height = 1;
3540 for (iter = list ; iter; iter = g_list_next (iter))
3542 GtkWidget *w = GTK_WIDGET (iter->data);
3543 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3544 break;
3546 g_list_free (list);
3547 return iter != 0;
3552 /***********************************************************************
3553 Scroll bar functions
3554 ***********************************************************************/
3557 /* Setting scroll bar values invokes the callback. Use this variable
3558 to indicate that callback should do nothing. */
3560 bool xg_ignore_gtk_scrollbar;
3562 /* Width and height of scroll bars for the current theme. */
3563 static int scroll_bar_width_for_theme;
3564 static int scroll_bar_height_for_theme;
3566 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3567 may be larger than 32 bits. Keep a mapping from integer index to widget
3568 pointers to get around the 32 bit limitation. */
3570 static struct
3572 GtkWidget **widgets;
3573 ptrdiff_t max_size;
3574 ptrdiff_t used;
3575 } id_to_widget;
3577 /* Grow this much every time we need to allocate more */
3579 #define ID_TO_WIDGET_INCR 32
3581 /* Store the widget pointer W in id_to_widget and return the integer index. */
3583 static ptrdiff_t
3584 xg_store_widget_in_map (GtkWidget *w)
3586 ptrdiff_t i;
3588 if (id_to_widget.max_size == id_to_widget.used)
3590 ptrdiff_t new_size;
3591 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3592 memory_full (SIZE_MAX);
3594 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3595 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3596 new_size, sizeof (GtkWidget *));
3598 for (i = id_to_widget.max_size; i < new_size; ++i)
3599 id_to_widget.widgets[i] = 0;
3600 id_to_widget.max_size = new_size;
3603 /* Just loop over the array and find a free place. After all,
3604 how many scroll bars are we creating? Should be a small number.
3605 The check above guarantees we will find a free place. */
3606 for (i = 0; i < id_to_widget.max_size; ++i)
3608 if (! id_to_widget.widgets[i])
3610 id_to_widget.widgets[i] = w;
3611 ++id_to_widget.used;
3613 return i;
3617 /* Should never end up here */
3618 emacs_abort ();
3621 /* Remove pointer at IDX from id_to_widget.
3622 Called when scroll bar is destroyed. */
3624 static void
3625 xg_remove_widget_from_map (ptrdiff_t idx)
3627 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3629 id_to_widget.widgets[idx] = 0;
3630 --id_to_widget.used;
3634 /* Get the widget pointer at IDX from id_to_widget. */
3636 static GtkWidget *
3637 xg_get_widget_from_map (ptrdiff_t idx)
3639 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3640 return id_to_widget.widgets[idx];
3642 return 0;
3645 static void
3646 update_theme_scrollbar_width (void)
3648 #ifdef HAVE_GTK3
3649 GtkAdjustment *vadj;
3650 #else
3651 GtkObject *vadj;
3652 #endif
3653 GtkWidget *wscroll;
3654 int w = 0, b = 0;
3656 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3657 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3658 g_object_ref_sink (G_OBJECT (wscroll));
3659 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3660 gtk_widget_destroy (wscroll);
3661 g_object_unref (G_OBJECT (wscroll));
3662 w += 2*b;
3663 #ifndef HAVE_GTK3
3664 if (w < 16) w = 16;
3665 #endif
3666 scroll_bar_width_for_theme = w;
3669 static void
3670 update_theme_scrollbar_height (void)
3672 #ifdef HAVE_GTK3
3673 GtkAdjustment *hadj;
3674 #else
3675 GtkObject *hadj;
3676 #endif
3677 GtkWidget *wscroll;
3678 int w = 0, b = 0;
3680 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
3681 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3682 g_object_ref_sink (G_OBJECT (wscroll));
3683 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3684 gtk_widget_destroy (wscroll);
3685 g_object_unref (G_OBJECT (wscroll));
3686 w += 2*b;
3687 if (w < 12) w = 12;
3688 scroll_bar_height_for_theme = w;
3692 xg_get_default_scrollbar_width (struct frame *f)
3694 return scroll_bar_width_for_theme * xg_get_scale (f);
3698 xg_get_default_scrollbar_height (struct frame *f)
3700 /* Apparently there's no default height for themes. */
3701 return scroll_bar_width_for_theme * xg_get_scale (f);
3704 /* Return the scrollbar id for X Window WID on display DPY.
3705 Return -1 if WID not in id_to_widget. */
3707 ptrdiff_t
3708 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3710 ptrdiff_t idx;
3711 GtkWidget *w;
3713 w = xg_win_to_widget (dpy, wid);
3715 if (w)
3717 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3718 if (id_to_widget.widgets[idx] == w)
3719 return idx;
3722 return -1;
3725 /* Callback invoked when scroll bar WIDGET is destroyed.
3726 DATA is the index into id_to_widget for WIDGET.
3727 We free pointer to last scroll bar values here and remove the index. */
3729 static void
3730 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3732 intptr_t id = (intptr_t) data;
3733 xg_remove_widget_from_map (id);
3736 static void
3737 xg_finish_scroll_bar_creation (struct frame *f,
3738 GtkWidget *wscroll,
3739 struct scroll_bar *bar,
3740 GCallback scroll_callback,
3741 GCallback end_callback,
3742 const char *scroll_bar_name)
3744 GtkWidget *webox = gtk_event_box_new ();
3746 gtk_widget_set_name (wscroll, scroll_bar_name);
3747 #ifndef HAVE_GTK3
3748 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3749 #endif
3750 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3752 ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll);
3754 g_signal_connect (G_OBJECT (wscroll),
3755 "destroy",
3756 G_CALLBACK (xg_gtk_scroll_destroy),
3757 (gpointer) scroll_id);
3758 g_signal_connect (G_OBJECT (wscroll),
3759 "change-value",
3760 scroll_callback,
3761 (gpointer) bar);
3762 g_signal_connect (G_OBJECT (wscroll),
3763 "button-release-event",
3764 end_callback,
3765 (gpointer) bar);
3767 /* The scroll bar widget does not draw on a window of its own. Instead
3768 it draws on the parent window, in this case the edit widget. So
3769 whenever the edit widget is cleared, the scroll bar needs to redraw
3770 also, which causes flicker. Put an event box between the edit widget
3771 and the scroll bar, so the scroll bar instead draws itself on the
3772 event box window. */
3773 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3774 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3776 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
3778 /* N.B. The event box doesn't become a real X11 window until we ask
3779 for its XID via GTK_WIDGET_TO_X_WIN. If the event box is not a
3780 real X window, it and its scroll-bar child try to draw on the
3781 Emacs main window, which we draw over using Xlib. */
3782 gtk_widget_realize (webox);
3783 GTK_WIDGET_TO_X_WIN (webox);
3785 /* Set the cursor to an arrow. */
3786 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3788 bar->x_window = scroll_id;
3791 /* Create a scroll bar widget for frame F. Store the scroll bar
3792 in BAR.
3793 SCROLL_CALLBACK is the callback to invoke when the value of the
3794 bar changes.
3795 END_CALLBACK is the callback to invoke when scrolling ends.
3796 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3797 to set resources for the widget. */
3799 void
3800 xg_create_scroll_bar (struct frame *f,
3801 struct scroll_bar *bar,
3802 GCallback scroll_callback,
3803 GCallback end_callback,
3804 const char *scroll_bar_name)
3806 GtkWidget *wscroll;
3807 #ifdef HAVE_GTK3
3808 GtkAdjustment *vadj;
3809 #else
3810 GtkObject *vadj;
3811 #endif
3813 /* Page, step increment values are not so important here, they
3814 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3815 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3816 0.1, 0.1, 0.1);
3818 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3820 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3821 end_callback, scroll_bar_name);
3822 bar->horizontal = 0;
3825 /* Create a horizontal scroll bar widget for frame F. Store the scroll
3826 bar in BAR. SCROLL_CALLBACK is the callback to invoke when the value
3827 of the bar changes. END_CALLBACK is the callback to invoke when
3828 scrolling ends. SCROLL_BAR_NAME is the name we use for the scroll
3829 bar. Can be used to set resources for the widget. */
3831 void
3832 xg_create_horizontal_scroll_bar (struct frame *f,
3833 struct scroll_bar *bar,
3834 GCallback scroll_callback,
3835 GCallback end_callback,
3836 const char *scroll_bar_name)
3838 GtkWidget *wscroll;
3839 #ifdef HAVE_GTK3
3840 GtkAdjustment *hadj;
3841 #else
3842 GtkObject *hadj;
3843 #endif
3845 /* Page, step increment values are not so important here, they
3846 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3847 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
3848 0.1, 0.1, 0.1);
3850 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3852 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3853 end_callback, scroll_bar_name);
3854 bar->horizontal = 1;
3857 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3859 void
3860 xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
3862 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3863 if (w)
3865 GtkWidget *wparent = gtk_widget_get_parent (w);
3866 gtk_widget_destroy (w);
3867 gtk_widget_destroy (wparent);
3868 SET_FRAME_GARBAGED (f);
3872 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3873 in frame F.
3874 TOP/LEFT are the new pixel positions where the bar shall appear.
3875 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3877 void
3878 xg_update_scrollbar_pos (struct frame *f,
3879 ptrdiff_t scrollbar_id,
3880 int top,
3881 int left,
3882 int width,
3883 int height)
3885 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3886 if (wscroll)
3888 GtkWidget *wfixed = f->output_data.x->edit_widget;
3889 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3890 gint msl;
3891 int scale = xg_get_scale (f);
3893 top /= scale;
3894 left /= scale;
3895 height /= scale;
3896 width /= scale;
3898 /* Clear out old position. */
3899 int oldx = -1, oldy = -1, oldw, oldh;
3900 if (gtk_widget_get_parent (wparent) == wfixed)
3902 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3903 "x", &oldx, "y", &oldy, NULL);
3904 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3907 /* Move and resize to new values. */
3908 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3909 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3910 bool hidden = height < msl;
3911 if (hidden)
3913 /* No room. Hide scroll bar as some themes output a warning if
3914 the height is less than the min size. */
3915 gtk_widget_hide (wparent);
3916 gtk_widget_hide (wscroll);
3918 else
3920 gtk_widget_show_all (wparent);
3921 gtk_widget_set_size_request (wscroll, width, height);
3923 if (oldx != -1 && oldw > 0 && oldh > 0)
3925 /* Clear under old scroll bar position. */
3926 oldw += (scale - 1) * oldw;
3927 oldx -= (scale - 1) * oldw;
3928 x_clear_area (f, oldx, oldy, oldw, oldh);
3931 if (!hidden)
3933 GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id);
3934 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
3936 /* Don't obscure any child frames. */
3937 XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
3940 /* GTK does not redraw until the main loop is entered again, but
3941 if there are no X events pending we will not enter it. So we sync
3942 here to get some events. */
3944 x_sync (f);
3945 SET_FRAME_GARBAGED (f);
3946 cancel_mouse_face (f);
3951 /* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
3952 in frame F.
3953 TOP/LEFT are the new pixel positions where the bar shall appear.
3954 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3956 void
3957 xg_update_horizontal_scrollbar_pos (struct frame *f,
3958 ptrdiff_t scrollbar_id,
3959 int top,
3960 int left,
3961 int width,
3962 int height)
3965 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3967 if (wscroll)
3969 GtkWidget *wfixed = f->output_data.x->edit_widget;
3970 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3971 gint msl;
3972 int scale = xg_get_scale (f);
3974 top /= scale;
3975 left /= scale;
3976 height /= scale;
3977 width /= scale;
3979 /* Clear out old position. */
3980 int oldx = -1, oldy = -1, oldw, oldh;
3981 if (gtk_widget_get_parent (wparent) == wfixed)
3983 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3984 "x", &oldx, "y", &oldy, NULL);
3985 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3988 /* Move and resize to new values. */
3989 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3990 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3991 if (msl > width)
3993 /* No room. Hide scroll bar as some themes output a warning if
3994 the width is less than the min size. */
3995 gtk_widget_hide (wparent);
3996 gtk_widget_hide (wscroll);
3998 else
4000 gtk_widget_show_all (wparent);
4001 gtk_widget_set_size_request (wscroll, width, height);
4003 if (oldx != -1 && oldw > 0 && oldh > 0)
4004 /* Clear under old scroll bar position. */
4005 x_clear_area (f, oldx, oldy, oldw, oldh);
4007 /* GTK does not redraw until the main loop is entered again, but
4008 if there are no X events pending we will not enter it. So we sync
4009 here to get some events. */
4012 GtkWidget *scrollbar =
4013 xg_get_widget_from_map (scrollbar_id);
4014 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
4016 /* Don't obscure any child frames. */
4017 XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
4020 x_sync (f);
4021 SET_FRAME_GARBAGED (f);
4022 cancel_mouse_face (f);
4027 /* Get the current value of the range, truncated to an integer. */
4029 static int
4030 int_gtk_range_get_value (GtkRange *range)
4032 return gtk_range_get_value (range);
4036 /* Set the thumb size and position of scroll bar BAR. We are currently
4037 displaying PORTION out of a whole WHOLE, and our position POSITION. */
4039 void
4040 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
4041 int portion,
4042 int position,
4043 int whole)
4045 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
4047 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
4049 if (wscroll && bar->dragging == -1)
4051 GtkAdjustment *adj;
4052 gdouble shown;
4053 gdouble top;
4054 int size, value;
4055 int old_size;
4056 int new_step;
4057 bool changed = 0;
4059 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4061 if (scroll_bar_adjust_thumb_portion_p)
4063 /* We do the same as for MOTIF in xterm.c, use 30 chars per
4064 line rather than the real portion value. This makes the
4065 thumb less likely to resize and that looks better. */
4066 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
4068 /* When the thumb is at the bottom, position == whole.
4069 So we need to increase `whole' to make space for the thumb. */
4070 whole += portion;
4073 if (whole <= 0)
4074 top = 0, shown = 1;
4075 else
4077 top = (gdouble) position / whole;
4078 shown = (gdouble) portion / whole;
4081 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
4082 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
4084 /* Assume all lines are of equal size. */
4085 new_step = size / max (1, FRAME_LINES (f));
4087 old_size = gtk_adjustment_get_page_size (adj);
4088 if (old_size != size)
4090 int old_step = gtk_adjustment_get_step_increment (adj);
4091 if (old_step != new_step)
4093 gtk_adjustment_set_page_size (adj, size);
4094 gtk_adjustment_set_step_increment (adj, new_step);
4095 /* Assume a page increment is about 95% of the page size */
4096 gtk_adjustment_set_page_increment (adj, size - size / 20);
4097 changed = 1;
4101 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
4103 block_input ();
4105 /* gtk_range_set_value invokes the callback. Set
4106 ignore_gtk_scrollbar to make the callback do nothing */
4107 xg_ignore_gtk_scrollbar = 1;
4109 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
4110 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
4111 else if (changed)
4112 gtk_adjustment_changed (adj);
4114 xg_ignore_gtk_scrollbar = 0;
4116 unblock_input ();
4121 /* Set the thumb size and position of horizontal scroll bar BAR. We are
4122 currently displaying PORTION out of a whole WHOLE, and our position
4123 POSITION. */
4124 void
4125 xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
4126 int portion,
4127 int position,
4128 int whole)
4130 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
4132 if (wscroll && bar->dragging == -1)
4134 GtkAdjustment *adj;
4135 int lower = 0;
4136 int upper = max (whole - 1, 0);
4137 int pagesize = min (upper, max (portion, 0));
4138 int value = max (0, min (position, upper - pagesize));
4139 /* These should be set to something more <portion, whole>
4140 related. */
4141 int page_increment = 4;
4142 int step_increment = 1;
4144 block_input ();
4145 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4146 gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
4147 (gdouble) upper, (gdouble) step_increment,
4148 (gdouble) page_increment, (gdouble) pagesize);
4149 gtk_adjustment_changed (adj);
4150 unblock_input ();
4154 /* Return true if EVENT is for a scroll bar in frame F.
4155 When the same X window is used for several Gtk+ widgets, we cannot
4156 say for sure based on the X window alone if an event is for the
4157 frame. This function does additional checks. */
4159 bool
4160 xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
4162 bool retval = 0;
4164 if (f && event->type == ButtonPress && event->xbutton.button < 4)
4166 /* Check if press occurred outside the edit widget. */
4167 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
4168 GdkWindow *gwin;
4169 #ifdef HAVE_GTK3
4170 #if GTK_CHECK_VERSION (3, 20, 0)
4171 GdkDevice *gdev
4172 = gdk_seat_get_pointer (gdk_display_get_default_seat (gdpy));
4173 #else
4174 GdkDevice *gdev = gdk_device_manager_get_client_pointer
4175 (gdk_display_get_device_manager (gdpy));
4176 #endif
4177 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
4178 #else
4179 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
4180 #endif
4181 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
4183 else if (f
4184 && ((event->type == ButtonRelease && event->xbutton.button < 4)
4185 || event->type == MotionNotify))
4187 /* If we are releasing or moving the scroll bar, it has the grab. */
4188 GtkWidget *w = gtk_grab_get_current ();
4189 retval = w != 0 && GTK_IS_SCROLLBAR (w);
4192 return retval;
4196 /***********************************************************************
4197 Printing
4198 ***********************************************************************/
4199 #ifdef USE_CAIRO
4200 static GtkPrintSettings *print_settings = NULL;
4201 static GtkPageSetup *page_setup = NULL;
4203 void
4204 xg_page_setup_dialog (void)
4206 GtkPageSetup *new_page_setup = NULL;
4208 if (print_settings == NULL)
4209 print_settings = gtk_print_settings_new ();
4210 new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
4211 print_settings);
4212 if (page_setup)
4213 g_object_unref (page_setup);
4214 page_setup = new_page_setup;
4217 Lisp_Object
4218 xg_get_page_setup (void)
4220 Lisp_Object orientation_symbol;
4222 if (page_setup == NULL)
4223 page_setup = gtk_page_setup_new ();
4225 switch (gtk_page_setup_get_orientation (page_setup))
4227 case GTK_PAGE_ORIENTATION_PORTRAIT:
4228 orientation_symbol = Qportrait;
4229 break;
4230 case GTK_PAGE_ORIENTATION_LANDSCAPE:
4231 orientation_symbol = Qlandscape;
4232 break;
4233 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4234 orientation_symbol = Qreverse_portrait;
4235 break;
4236 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4237 orientation_symbol = Qreverse_landscape;
4238 break;
4239 default:
4240 eassume (false);
4243 return listn (CONSTYPE_HEAP, 7,
4244 Fcons (Qorientation, orientation_symbol),
4245 #define MAKE_FLOAT_PAGE_SETUP(f) make_float (f (page_setup, GTK_UNIT_POINTS))
4246 Fcons (Qwidth,
4247 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_width)),
4248 Fcons (Qheight,
4249 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_height)),
4250 Fcons (Qleft_margin,
4251 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_left_margin)),
4252 Fcons (Qright_margin,
4253 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_right_margin)),
4254 Fcons (Qtop_margin,
4255 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_top_margin)),
4256 Fcons (Qbottom_margin,
4257 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_bottom_margin))
4258 #undef MAKE_FLOAT_PAGE_SETUP
4262 static void
4263 draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
4264 gint page_nr, gpointer user_data)
4266 Lisp_Object frames = *((Lisp_Object *) user_data);
4267 struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
4268 cairo_t *cr = gtk_print_context_get_cairo_context (context);
4270 x_cr_draw_frame (cr, f);
4273 void
4274 xg_print_frames_dialog (Lisp_Object frames)
4276 GtkPrintOperation *print;
4277 GtkPrintOperationResult res;
4279 print = gtk_print_operation_new ();
4280 if (print_settings != NULL)
4281 gtk_print_operation_set_print_settings (print, print_settings);
4282 if (page_setup != NULL)
4283 gtk_print_operation_set_default_page_setup (print, page_setup);
4284 gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
4285 g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
4286 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
4287 NULL, NULL);
4288 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
4290 if (print_settings != NULL)
4291 g_object_unref (print_settings);
4292 print_settings =
4293 g_object_ref (gtk_print_operation_get_print_settings (print));
4295 g_object_unref (print);
4298 #endif /* USE_CAIRO */
4302 /***********************************************************************
4303 Tool bar functions
4304 ***********************************************************************/
4305 /* The key for the data we put in the GtkImage widgets. The data is
4306 the image used by Emacs. We use this to see if we need to update
4307 the GtkImage with a new image. */
4308 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
4310 /* The key for storing the latest modifiers so the activate callback can
4311 get them. */
4312 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
4314 /* The key for the data we put in the GtkImage widgets. The data is
4315 the stock name used by Emacs. We use this to see if we need to update
4316 the GtkImage with a new image. */
4317 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
4319 /* As above, but this is used for named theme widgets, as opposed to
4320 stock items. */
4321 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
4323 /* Callback function invoked when a tool bar item is pressed.
4324 W is the button widget in the tool bar that got pressed,
4325 CLIENT_DATA is an integer that is the index of the button in the
4326 tool bar. 0 is the first button. */
4328 static gboolean
4329 xg_tool_bar_button_cb (GtkWidget *widget,
4330 GdkEventButton *event,
4331 gpointer user_data)
4333 intptr_t state = event->state;
4334 gpointer ptr = (gpointer) state;
4335 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
4336 return FALSE;
4340 /* Callback function invoked when a tool bar item is pressed.
4341 W is the button widget in the tool bar that got pressed,
4342 CLIENT_DATA is an integer that is the index of the button in the
4343 tool bar. 0 is the first button. */
4345 static void
4346 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
4348 intptr_t idx = (intptr_t) client_data;
4349 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
4350 intptr_t mod = (intptr_t) gmod;
4352 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4353 Lisp_Object key, frame;
4354 struct input_event event;
4355 EVENT_INIT (event);
4357 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4358 return;
4360 idx *= TOOL_BAR_ITEM_NSLOTS;
4362 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
4363 XSETFRAME (frame, f);
4365 /* We generate two events here. The first one is to set the prefix
4366 to `(tool_bar)', see keyboard.c. */
4367 event.kind = TOOL_BAR_EVENT;
4368 event.frame_or_window = frame;
4369 event.arg = frame;
4370 kbd_buffer_store_event (&event);
4372 event.kind = TOOL_BAR_EVENT;
4373 event.frame_or_window = frame;
4374 event.arg = key;
4375 /* Convert between the modifier bits GDK uses and the modifier bits
4376 Emacs uses. This assumes GDK and X masks are the same, which they are when
4377 this is written. */
4378 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
4379 kbd_buffer_store_event (&event);
4381 /* Return focus to the frame after we have clicked on a detached
4382 tool bar button. */
4383 x_focus_frame (f, false);
4386 static GtkWidget *
4387 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4389 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
4390 GtkWidget *c1 = clist->data;
4391 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
4393 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4394 g_list_free (clist);
4395 return GTK_IS_LABEL (c1) ? c1 : c2;
4399 /* This callback is called when the mouse enters or leaves a tool bar item.
4400 It is used for displaying and hiding the help text.
4401 W is the tool bar item, a button.
4402 EVENT is either an enter event or leave event.
4403 CLIENT_DATA is an integer that is the index of the button in the
4404 tool bar. 0 is the first button.
4406 Returns FALSE to tell GTK to keep processing this event. */
4408 static gboolean
4409 xg_tool_bar_help_callback (GtkWidget *w,
4410 GdkEventCrossing *event,
4411 gpointer client_data)
4413 intptr_t idx = (intptr_t) client_data;
4414 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4415 Lisp_Object help, frame;
4417 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4418 return FALSE;
4420 if (event->type == GDK_ENTER_NOTIFY)
4422 idx *= TOOL_BAR_ITEM_NSLOTS;
4423 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4425 if (NILP (help))
4426 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4428 else
4429 help = Qnil;
4431 XSETFRAME (frame, f);
4432 kbd_buffer_store_help_event (frame, help);
4434 return FALSE;
4438 /* This callback is called when a tool bar item shall be redrawn.
4439 It modifies the expose event so that the GtkImage widget redraws the
4440 whole image. This to overcome a bug that makes GtkImage draw the image
4441 in the wrong place when it tries to redraw just a part of the image.
4442 W is the GtkImage to be redrawn.
4443 EVENT is the expose event for W.
4444 CLIENT_DATA is unused.
4446 Returns FALSE to tell GTK to keep processing this event. */
4448 #ifndef HAVE_GTK3
4449 static gboolean
4450 xg_tool_bar_item_expose_callback (GtkWidget *w,
4451 GdkEventExpose *event,
4452 gpointer client_data)
4454 gint width, height;
4456 gdk_drawable_get_size (event->window, &width, &height);
4457 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4458 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4460 event->area.x = max (0, event->area.x);
4461 event->area.y = max (0, event->area.y);
4463 event->area.width = max (width, event->area.width);
4464 event->area.height = max (height, event->area.height);
4466 return FALSE;
4468 #endif
4470 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4471 #define toolbar_set_orientation(w, o) \
4472 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4473 #else
4474 #define toolbar_set_orientation(w, o) \
4475 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4476 #endif
4478 /* Attach a tool bar to frame F. */
4480 static void
4481 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
4483 struct x_output *x = f->output_data.x;
4484 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4485 GtkWidget *top_widget = x->toolbar_widget;
4487 toolbar_set_orientation (x->toolbar_widget,
4488 into_hbox
4489 ? GTK_ORIENTATION_VERTICAL
4490 : GTK_ORIENTATION_HORIZONTAL);
4492 if (into_hbox)
4494 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
4495 FALSE, FALSE, 0);
4497 if (EQ (pos, Qleft))
4498 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4499 top_widget,
4501 x->toolbar_in_hbox = true;
4503 else
4505 bool vbox_pos = x->menubar_widget != 0;
4506 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
4507 FALSE, FALSE, 0);
4509 if (EQ (pos, Qtop))
4510 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4511 top_widget,
4512 vbox_pos);
4513 x->toolbar_in_hbox = false;
4515 x->toolbar_is_packed = true;
4518 static bool xg_update_tool_bar_sizes (struct frame *f);
4520 static void
4521 tb_size_cb (GtkWidget *widget,
4522 GdkRectangle *allocation,
4523 gpointer user_data)
4525 /* When tool bar is created it has one preferred size. But when size is
4526 allocated between widgets, it may get another. So we must update
4527 size hints if tool bar size changes. Seen on Fedora 18 at least. */
4528 struct frame *f = user_data;
4530 if (xg_update_tool_bar_sizes (f))
4532 frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
4533 adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
4537 /* Create a tool bar for frame F. */
4539 static void
4540 xg_create_tool_bar (struct frame *f)
4542 struct x_output *x = f->output_data.x;
4543 #if GTK_CHECK_VERSION (3, 3, 6)
4544 GtkStyleContext *gsty;
4545 #endif
4546 struct xg_frame_tb_info *tbinfo
4547 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4548 TB_INFO_KEY);
4549 if (! tbinfo)
4551 tbinfo = xmalloc (sizeof (*tbinfo));
4552 tbinfo->last_tool_bar = Qnil;
4553 tbinfo->style = Qnil;
4554 tbinfo->hmargin = tbinfo->vmargin = 0;
4555 tbinfo->dir = GTK_TEXT_DIR_NONE;
4556 tbinfo->n_last_items = 0;
4557 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4558 TB_INFO_KEY,
4559 tbinfo);
4562 x->toolbar_widget = gtk_toolbar_new ();
4564 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4566 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4567 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4568 g_signal_connect (x->toolbar_widget, "size-allocate",
4569 G_CALLBACK (tb_size_cb), f);
4570 #if GTK_CHECK_VERSION (3, 3, 6)
4571 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4572 gtk_style_context_add_class (gsty, "primary-toolbar");
4573 #endif
4577 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4579 /* Find the right-to-left image named by RTL in the tool bar images for F.
4580 Returns IMAGE if RTL is not found. */
4582 static Lisp_Object
4583 find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
4585 int i;
4586 Lisp_Object file, rtl_name;
4588 rtl_name = Ffile_name_nondirectory (rtl);
4590 for (i = 0; i < f->n_tool_bar_items; ++i)
4592 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4593 if (!NILP (file = file_for_image (rtl_image)))
4595 file = call1 (intern ("file-name-sans-extension"),
4596 Ffile_name_nondirectory (file));
4597 if (! NILP (Fequal (file, rtl_name)))
4599 image = rtl_image;
4600 break;
4605 return image;
4608 static GtkToolItem *
4609 xg_make_tool_item (struct frame *f,
4610 GtkWidget *wimage,
4611 GtkWidget **wbutton,
4612 const char *label,
4613 int i, bool horiz, bool text_image)
4615 GtkToolItem *ti = gtk_tool_item_new ();
4616 GtkWidget *vb = gtk_box_new (horiz
4617 ? GTK_ORIENTATION_HORIZONTAL
4618 : GTK_ORIENTATION_VERTICAL,
4620 GtkWidget *wb = gtk_button_new ();
4621 /* The eventbox is here so we can have tooltips on disabled items. */
4622 GtkWidget *weventbox = gtk_event_box_new ();
4623 #if GTK_CHECK_VERSION (3, 3, 6)
4624 GtkCssProvider *css_prov = gtk_css_provider_new ();
4625 GtkStyleContext *gsty;
4627 gtk_css_provider_load_from_data (css_prov,
4628 "GtkEventBox {"
4629 " background-color: transparent;"
4630 "}",
4631 -1, NULL);
4633 gsty = gtk_widget_get_style_context (weventbox);
4634 gtk_style_context_add_provider (gsty,
4635 GTK_STYLE_PROVIDER (css_prov),
4636 GTK_STYLE_PROVIDER_PRIORITY_USER);
4637 g_object_unref (css_prov);
4638 #endif
4640 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4642 if (wimage && !text_image)
4643 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4644 if (label)
4645 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4646 if (wimage && text_image)
4647 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4649 #if GTK_CHECK_VERSION (3, 20, 0)
4650 gtk_widget_set_focus_on_click (wb, FALSE);
4651 #else
4652 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4653 #endif
4654 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4655 gtk_container_add (GTK_CONTAINER (wb), vb);
4656 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4657 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4659 if (wimage || label)
4661 intptr_t ii = i;
4662 gpointer gi = (gpointer) ii;
4664 g_signal_connect (G_OBJECT (wb), "clicked",
4665 G_CALLBACK (xg_tool_bar_callback),
4666 gi);
4668 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4670 #ifndef HAVE_GTK3
4671 /* Catch expose events to overcome an annoying redraw bug, see
4672 comment for xg_tool_bar_item_expose_callback. */
4673 g_signal_connect (G_OBJECT (ti),
4674 "expose-event",
4675 G_CALLBACK (xg_tool_bar_item_expose_callback),
4677 #endif
4678 gtk_tool_item_set_homogeneous (ti, FALSE);
4680 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4681 no distinction based on modifiers in the activate callback,
4682 so we have to do it ourselves. */
4683 g_signal_connect (wb, "button-release-event",
4684 G_CALLBACK (xg_tool_bar_button_cb),
4685 NULL);
4687 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4689 /* Use enter/leave notify to show help. We use the events
4690 rather than the GtkButton specific signals "enter" and
4691 "leave", so we can have only one callback. The event
4692 will tell us what kind of event it is. */
4693 g_signal_connect (G_OBJECT (weventbox),
4694 "enter-notify-event",
4695 G_CALLBACK (xg_tool_bar_help_callback),
4696 gi);
4697 g_signal_connect (G_OBJECT (weventbox),
4698 "leave-notify-event",
4699 G_CALLBACK (xg_tool_bar_help_callback),
4700 gi);
4703 if (wbutton) *wbutton = wb;
4705 return ti;
4708 static bool
4709 is_box_type (GtkWidget *vb, bool is_horizontal)
4711 #ifdef HAVE_GTK3
4712 bool ret = 0;
4713 if (GTK_IS_BOX (vb))
4715 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4716 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4717 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4719 return ret;
4720 #else
4721 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4722 #endif
4726 static bool
4727 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4728 const char *icon_name, const struct image *img,
4729 const char *label, bool horiz)
4731 gpointer old;
4732 GtkWidget *wimage;
4733 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4734 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4736 /* Check if the tool icon matches. */
4737 if (stock_name && wimage)
4739 old = g_object_get_data (G_OBJECT (wimage),
4740 XG_TOOL_BAR_STOCK_NAME);
4741 if (!old || strcmp (old, stock_name))
4742 return 1;
4744 else if (icon_name && wimage)
4746 old = g_object_get_data (G_OBJECT (wimage),
4747 XG_TOOL_BAR_ICON_NAME);
4748 if (!old || strcmp (old, icon_name))
4749 return 1;
4751 else if (wimage)
4753 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4754 XG_TOOL_BAR_IMAGE_DATA);
4755 Pixmap old_img = (Pixmap) gold_img;
4756 if (old_img != img->pixmap)
4757 return 1;
4760 /* Check button configuration and label. */
4761 if (is_box_type (vb, horiz)
4762 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4763 return 1;
4765 /* Ensure label is correct. */
4766 if (label && wlbl)
4767 gtk_label_set_text (GTK_LABEL (wlbl), label);
4768 return 0;
4771 static bool
4772 xg_update_tool_bar_sizes (struct frame *f)
4774 struct x_output *x = f->output_data.x;
4775 GtkRequisition req;
4776 int nl = 0, nr = 0, nt = 0, nb = 0;
4777 GtkWidget *top_widget = x->toolbar_widget;
4779 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
4780 if (x->toolbar_in_hbox)
4782 int pos;
4783 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4784 top_widget,
4785 "position", &pos, NULL);
4786 if (pos == 0) nl = req.width;
4787 else nr = req.width;
4789 else
4791 int pos;
4792 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4793 top_widget,
4794 "position", &pos, NULL);
4795 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4796 else nb = req.height;
4799 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4800 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4801 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4802 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4804 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4805 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4806 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4807 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4808 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4809 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4811 return true;
4813 else
4814 return false;
4817 static char *
4818 find_icon_from_name (char *name,
4819 GtkIconTheme *icon_theme,
4820 char **icon_name)
4822 #if ! GTK_CHECK_VERSION (3, 10, 0)
4823 GtkStockItem stock_item;
4824 #endif
4826 if (name[0] == 'n' && name[1] == ':')
4828 *icon_name = name + 2;
4829 name = NULL;
4831 if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
4832 *icon_name = NULL;
4835 #if ! GTK_CHECK_VERSION (3, 10, 0)
4836 else if (gtk_stock_lookup (name, &stock_item))
4837 *icon_name = NULL;
4838 #endif
4839 else if (gtk_icon_theme_has_icon (icon_theme, name))
4841 *icon_name = name;
4842 name = NULL;
4844 else
4846 name = NULL;
4847 *icon_name = NULL;
4850 return name;
4854 /* Update the tool bar for frame F. Add new buttons and remove old. */
4856 void
4857 update_frame_tool_bar (struct frame *f)
4859 int i, j;
4860 struct x_output *x = f->output_data.x;
4861 int hmargin = 0, vmargin = 0;
4862 GtkToolbar *wtoolbar;
4863 GtkToolItem *ti;
4864 GtkTextDirection dir;
4865 Lisp_Object style;
4866 bool text_image, horiz;
4867 struct xg_frame_tb_info *tbinfo;
4868 GdkScreen *screen;
4869 GtkIconTheme *icon_theme;
4872 if (! FRAME_GTK_WIDGET (f))
4873 return;
4875 block_input ();
4877 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4879 hmargin = XFASTINT (Vtool_bar_button_margin);
4880 vmargin = XFASTINT (Vtool_bar_button_margin);
4882 else if (CONSP (Vtool_bar_button_margin))
4884 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4885 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4887 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4888 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4891 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4892 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4893 i.e. zero. This means that margins less than
4894 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4895 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4896 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4898 if (! x->toolbar_widget)
4899 xg_create_tool_bar (f);
4901 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4902 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4904 style = Ftool_bar_get_system_style ();
4905 screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4906 icon_theme = gtk_icon_theme_get_for_screen (screen);
4908 /* Are we up to date? */
4909 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4910 TB_INFO_KEY);
4912 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4913 && tbinfo->n_last_items == f->n_tool_bar_items
4914 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4915 && tbinfo->dir == dir
4916 && ! NILP (Fequal (tbinfo->style, style))
4917 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4919 unblock_input ();
4920 return;
4923 tbinfo->last_tool_bar = f->tool_bar_items;
4924 tbinfo->n_last_items = f->n_tool_bar_items;
4925 tbinfo->style = style;
4926 tbinfo->hmargin = hmargin;
4927 tbinfo->vmargin = vmargin;
4928 tbinfo->dir = dir;
4930 text_image = EQ (style, Qtext_image_horiz);
4931 horiz = EQ (style, Qboth_horiz) || text_image;
4933 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4935 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4936 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4937 int idx;
4938 ptrdiff_t img_id;
4939 int icon_size = 0;
4940 struct image *img = NULL;
4941 Lisp_Object image;
4942 Lisp_Object stock = Qnil;
4943 char *stock_name = NULL;
4944 char *icon_name = NULL;
4945 Lisp_Object rtl;
4946 GtkWidget *wbutton = NULL;
4947 Lisp_Object specified_file;
4948 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4949 const char *label
4950 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4951 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4952 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4953 : "";
4955 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4957 /* If this is a separator, use a gtk separator item. */
4958 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4960 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4962 if (ti)
4963 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4964 GTK_WIDGET (ti));
4965 ti = gtk_separator_tool_item_new ();
4966 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4968 j++;
4969 continue;
4972 /* Otherwise, the tool-bar item is an ordinary button. */
4974 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4976 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4977 ti = NULL;
4980 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4982 /* Ignore invalid image specifications. */
4983 image = PROP (TOOL_BAR_ITEM_IMAGES);
4984 if (!valid_image_p (image))
4986 if (ti)
4987 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4988 GTK_WIDGET (ti));
4989 continue;
4992 specified_file = file_for_image (image);
4993 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4994 stock = call1 (Qx_gtk_map_stock, specified_file);
4996 if (CONSP (stock))
4998 Lisp_Object tem;
4999 for (tem = stock; CONSP (tem); tem = XCDR (tem))
5000 if (! NILP (tem) && STRINGP (XCAR (tem)))
5002 stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
5003 icon_theme,
5004 &icon_name);
5005 if (stock_name || icon_name) break;
5008 else if (STRINGP (stock))
5010 stock_name = find_icon_from_name (SSDATA (stock),
5011 icon_theme,
5012 &icon_name);
5015 if (stock_name || icon_name)
5016 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
5018 if (stock_name == NULL && icon_name == NULL)
5020 /* No stock image, or stock item not known. Try regular
5021 image. If image is a vector, choose it according to the
5022 button state. */
5023 if (dir == GTK_TEXT_DIR_RTL
5024 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
5025 && STRINGP (rtl))
5026 image = find_rtl_image (f, image, rtl);
5028 if (VECTORP (image))
5030 if (enabled_p)
5031 idx = (selected_p
5032 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
5033 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
5034 else
5035 idx = (selected_p
5036 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
5037 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
5039 eassert (ASIZE (image) >= idx);
5040 image = AREF (image, idx);
5042 else
5043 idx = -1;
5045 img_id = lookup_image (f, image);
5046 img = IMAGE_FROM_ID (f, img_id);
5047 prepare_image_for_display (f, img);
5049 if (img->load_failed_p || img->pixmap == None)
5051 if (ti)
5052 gtk_container_remove (GTK_CONTAINER (wtoolbar),
5053 GTK_WIDGET (ti));
5054 continue;
5058 /* If there is an existing widget, check if it's stale; if so,
5059 remove it and make a new tool item from scratch. */
5060 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
5061 img, label, horiz))
5063 gtk_container_remove (GTK_CONTAINER (wtoolbar),
5064 GTK_WIDGET (ti));
5065 ti = NULL;
5068 if (ti == NULL)
5070 GtkWidget *w;
5072 /* Save the image so we can see if an update is needed the
5073 next time we call xg_tool_item_match_p. */
5074 if (EQ (style, Qtext))
5075 w = NULL;
5076 else if (stock_name)
5079 #if GTK_CHECK_VERSION (3, 10, 0)
5080 w = gtk_image_new_from_icon_name (stock_name, icon_size);
5081 #else
5082 w = gtk_image_new_from_stock (stock_name, icon_size);
5083 #endif
5084 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
5085 (gpointer) xstrdup (stock_name),
5086 (GDestroyNotify) xfree);
5088 else if (icon_name)
5090 w = gtk_image_new_from_icon_name (icon_name, icon_size);
5091 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
5092 (gpointer) xstrdup (icon_name),
5093 (GDestroyNotify) xfree);
5095 else
5097 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
5098 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
5099 (gpointer)img->pixmap);
5102 #if GTK_CHECK_VERSION (3, 14, 0)
5103 if (w)
5105 gtk_widget_set_margin_start (w, hmargin);
5106 gtk_widget_set_margin_end (w, hmargin);
5107 gtk_widget_set_margin_top (w, vmargin);
5108 gtk_widget_set_margin_bottom (w, vmargin);
5110 #else
5111 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
5112 #endif
5113 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
5114 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
5117 #undef PROP
5119 gtk_widget_set_sensitive (wbutton, enabled_p);
5120 j++;
5123 /* Remove buttons not longer needed. */
5126 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
5127 if (ti)
5128 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
5129 } while (ti != NULL);
5131 if (f->n_tool_bar_items != 0)
5133 if (! x->toolbar_is_packed)
5134 xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
5135 gtk_widget_show_all (x->toolbar_widget);
5136 if (xg_update_tool_bar_sizes (f))
5138 int inhibit
5139 = ((f->after_make_frame
5140 && !f->tool_bar_resized
5141 && (EQ (frame_inhibit_implied_resize, Qt)
5142 || (CONSP (frame_inhibit_implied_resize)
5143 && !NILP (Fmemq (Qtool_bar_lines,
5144 frame_inhibit_implied_resize))))
5145 /* This will probably fail to DTRT in the
5146 fullheight/-width cases. */
5147 && NILP (get_frame_param (f, Qfullscreen)))
5149 : 2);
5151 frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
5152 adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
5154 f->tool_bar_resized = f->tool_bar_redisplayed;
5157 unblock_input ();
5160 /* Deallocate all resources for the tool bar on frame F.
5161 Remove the tool bar. */
5163 void
5164 free_frame_tool_bar (struct frame *f)
5166 struct x_output *x = f->output_data.x;
5168 if (x->toolbar_widget)
5170 struct xg_frame_tb_info *tbinfo;
5171 GtkWidget *top_widget = x->toolbar_widget;
5173 block_input ();
5174 if (x->toolbar_is_packed)
5176 if (x->toolbar_in_hbox)
5177 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5178 top_widget);
5179 else
5180 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5181 top_widget);
5183 else
5184 gtk_widget_destroy (x->toolbar_widget);
5186 x->toolbar_widget = 0;
5187 x->toolbar_widget = 0;
5188 x->toolbar_is_packed = false;
5189 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
5190 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
5192 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5193 TB_INFO_KEY);
5194 if (tbinfo)
5196 xfree (tbinfo);
5197 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5198 TB_INFO_KEY,
5199 NULL);
5202 frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
5203 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5205 unblock_input ();
5209 void
5210 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
5212 struct x_output *x = f->output_data.x;
5213 GtkWidget *top_widget = x->toolbar_widget;
5215 if (! x->toolbar_widget || ! top_widget)
5216 return;
5218 block_input ();
5219 g_object_ref (top_widget);
5220 if (x->toolbar_is_packed)
5222 if (x->toolbar_in_hbox)
5223 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5224 top_widget);
5225 else
5226 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5227 top_widget);
5230 xg_pack_tool_bar (f, pos);
5231 g_object_unref (top_widget);
5233 if (xg_update_tool_bar_sizes (f))
5235 frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
5236 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5240 unblock_input ();
5245 /***********************************************************************
5246 Initializing
5247 ***********************************************************************/
5248 void
5249 xg_initialize (void)
5251 GtkBindingSet *binding_set;
5252 GtkSettings *settings;
5254 #if HAVE_XFT
5255 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5256 we keep it permanently linked in. */
5257 XftInit (0);
5258 #endif
5260 gdpy_def = NULL;
5261 xg_ignore_gtk_scrollbar = 0;
5262 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5263 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5265 id_to_widget.max_size = id_to_widget.used = 0;
5266 id_to_widget.widgets = 0;
5268 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5269 (gdk_display_get_default ()));
5270 #if ! GTK_CHECK_VERSION (3, 10, 0)
5271 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5272 bindings. It doesn't seem to be any way to remove properties,
5273 so we set it to "" which in means "no key". */
5274 gtk_settings_set_string_property (settings,
5275 "gtk-menu-bar-accel",
5277 EMACS_CLASS);
5278 #endif
5280 /* Make GTK text input widgets use Emacs style keybindings. This is
5281 Emacs after all. */
5282 #if GTK_CHECK_VERSION (3, 16, 0)
5283 g_object_set (settings, "gtk-key-theme-name", "Emacs", NULL);
5284 #else
5285 gtk_settings_set_string_property (settings,
5286 "gtk-key-theme-name",
5287 "Emacs",
5288 EMACS_CLASS);
5289 #endif
5291 /* Make dialogs close on C-g. Since file dialog inherits from
5292 dialog, this works for them also. */
5293 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
5294 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5295 "close", 0);
5297 /* Make menus close on C-g. */
5298 binding_set = gtk_binding_set_by_class (g_type_class_ref
5299 (GTK_TYPE_MENU_SHELL));
5300 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5301 "cancel", 0);
5302 update_theme_scrollbar_width ();
5303 update_theme_scrollbar_height ();
5305 #ifdef HAVE_FREETYPE
5306 x_last_font_name = NULL;
5307 #endif
5310 #endif /* USE_GTK */