; Add further traces to tramp-tests.el
[emacs.git] / src / gtkutil.c
blob0c8395efe9ba7adad242e6ccf8d16fc069778f78
1 /* Functions for creating and updating GTK widgets.
3 Copyright (C) 2003-2017 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 <http://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
580 gtk_style_context_get_background_color (gsty, state, &col);
582 unsigned short
583 r = col.red * 65535,
584 g = col.green * 65535,
585 b = col.blue * 65535;
586 sprintf (buf, "rgb:%04x/%04x/%04x", r, g, b);
587 success_p = x_parse_color (f, buf, color) != 0;
588 #else
589 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
590 GdkColor *grgb = get_bg
591 ? &gsty->bg[GTK_STATE_SELECTED]
592 : &gsty->fg[GTK_STATE_SELECTED];
594 color->red = grgb->red;
595 color->green = grgb->green;
596 color->blue = grgb->blue;
597 color->pixel = grgb->pixel;
598 success_p = 1;
599 #endif
602 unblock_input ();
603 return success_p;
608 /***********************************************************************
609 Tooltips
610 ***********************************************************************/
611 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
612 We use that to pop down the tooltip. This happens if Gtk+ for some
613 reason wants to change or hide the tooltip. */
615 #ifdef USE_GTK_TOOLTIP
617 static void
618 hierarchy_ch_cb (GtkWidget *widget,
619 GtkWidget *previous_toplevel,
620 gpointer user_data)
622 struct frame *f = user_data;
623 struct x_output *x = f->output_data.x;
624 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
626 if (! top || ! GTK_IS_WINDOW (top))
627 gtk_widget_hide (previous_toplevel);
630 /* Callback called when Gtk+ thinks a tooltip should be displayed.
631 We use it to get the tooltip window and the tooltip widget so
632 we can manipulate the ourselves.
634 Return FALSE ensures that the tooltip is not shown. */
636 static gboolean
637 qttip_cb (GtkWidget *widget,
638 gint xpos,
639 gint ypos,
640 gboolean keyboard_mode,
641 GtkTooltip *tooltip,
642 gpointer user_data)
644 struct frame *f = user_data;
645 struct x_output *x = f->output_data.x;
646 if (x->ttip_widget == NULL)
648 GtkWidget *p;
649 GList *list, *iter;
651 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
652 x->ttip_widget = tooltip;
653 g_object_ref (G_OBJECT (tooltip));
654 x->ttip_lbl = gtk_label_new ("");
655 g_object_ref (G_OBJECT (x->ttip_lbl));
656 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
657 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
659 /* Change stupid Gtk+ default line wrapping. */
660 p = gtk_widget_get_parent (x->ttip_lbl);
661 list = gtk_container_get_children (GTK_CONTAINER (p));
662 for (iter = list; iter; iter = g_list_next (iter))
664 GtkWidget *w = GTK_WIDGET (iter->data);
665 if (GTK_IS_LABEL (w))
666 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
668 g_list_free (list);
670 /* ATK needs an empty title for some reason. */
671 gtk_window_set_title (x->ttip_window, "");
672 /* Realize so we can safely get screen later on. */
673 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
674 gtk_widget_realize (x->ttip_lbl);
676 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
677 G_CALLBACK (hierarchy_ch_cb), f);
679 return FALSE;
682 #endif /* USE_GTK_TOOLTIP */
684 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
685 Return true if a system tooltip is available. */
687 bool
688 xg_prepare_tooltip (struct frame *f,
689 Lisp_Object string,
690 int *width,
691 int *height)
693 #ifndef USE_GTK_TOOLTIP
694 return 0;
695 #else
696 struct x_output *x = f->output_data.x;
697 GtkWidget *widget;
698 GdkWindow *gwin;
699 GdkScreen *screen;
700 GtkSettings *settings;
701 gboolean tt_enabled = TRUE;
702 GtkRequisition req;
703 Lisp_Object encoded_string;
705 if (!x->ttip_lbl) return 0;
707 block_input ();
708 encoded_string = ENCODE_UTF_8 (string);
709 widget = GTK_WIDGET (x->ttip_lbl);
710 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
711 screen = gdk_window_get_screen (gwin);
712 settings = gtk_settings_get_for_screen (screen);
713 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
714 if (tt_enabled)
716 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
717 /* Record that we disabled it so it can be enabled again. */
718 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
719 (gpointer)f);
722 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
723 g_object_set_data (G_OBJECT
724 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
725 "gdk-display-current-tooltip", NULL);
727 /* Put our dummy widget in so we can get callbacks for unrealize and
728 hierarchy-changed. */
729 gtk_tooltip_set_custom (x->ttip_widget, widget);
730 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
731 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
732 if (width) *width = req.width;
733 if (height) *height = req.height;
735 unblock_input ();
737 return 1;
738 #endif /* USE_GTK_TOOLTIP */
741 /* Show the tooltip at ROOT_X and ROOT_Y.
742 xg_prepare_tooltip must have been called before this function. */
744 void
745 xg_show_tooltip (struct frame *f, int root_x, int root_y)
747 #ifdef USE_GTK_TOOLTIP
748 struct x_output *x = f->output_data.x;
749 if (x->ttip_window)
751 block_input ();
752 gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
753 root_y / xg_get_scale (f));
754 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
755 unblock_input ();
757 #endif
760 /* Hide tooltip if shown. Do nothing if not shown.
761 Return true if tip was hidden, false if not (i.e. not using
762 system tooltips). */
764 bool
765 xg_hide_tooltip (struct frame *f)
767 bool ret = 0;
768 #ifdef USE_GTK_TOOLTIP
769 if (f->output_data.x->ttip_window)
771 GtkWindow *win = f->output_data.x->ttip_window;
772 block_input ();
773 gtk_widget_hide (GTK_WIDGET (win));
775 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
777 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
778 GdkScreen *screen = gdk_window_get_screen (gwin);
779 GtkSettings *settings = gtk_settings_get_for_screen (screen);
780 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
782 unblock_input ();
784 ret = 1;
786 #endif
787 return ret;
791 /***********************************************************************
792 General functions for creating widgets, resizing, events, e.t.c.
793 ***********************************************************************/
795 static void
796 my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
797 const gchar *msg, gpointer user_data)
799 if (!strstr (msg, "visible children"))
800 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
803 /* Make a geometry string and pass that to GTK. It seems this is the
804 only way to get geometry position right if the user explicitly
805 asked for a position when starting Emacs.
806 F is the frame we shall set geometry for. */
808 static void
809 xg_set_geometry (struct frame *f)
811 if (f->size_hint_flags & (USPosition | PPosition))
813 if (x_gtk_use_window_move)
815 /* Handle negative positions without consulting
816 gtk_window_parse_geometry (Bug#25851). The position will
817 be off by scrollbar width + window manager decorations. */
818 if (f->size_hint_flags & XNegative)
819 f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f))
820 - FRAME_PIXEL_WIDTH (f) + f->left_pos);
822 if (f->size_hint_flags & YNegative)
823 f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f))
824 - FRAME_PIXEL_HEIGHT (f) + f->top_pos);
826 gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
827 f->left_pos, f->top_pos);
829 /* Reset size hint flags. */
830 f->size_hint_flags &= ~ (XNegative | YNegative);
832 else
834 int left = f->left_pos;
835 int xneg = f->size_hint_flags & XNegative;
836 int top = f->top_pos;
837 int yneg = f->size_hint_flags & YNegative;
838 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
839 guint id;
841 if (xneg)
842 left = -left;
843 if (yneg)
844 top = -top;
846 sprintf (geom_str, "=%dx%d%c%d%c%d",
847 FRAME_PIXEL_WIDTH (f),
848 FRAME_PIXEL_HEIGHT (f),
849 (xneg ? '-' : '+'), left,
850 (yneg ? '-' : '+'), top);
852 /* Silence warning about visible children. */
853 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
854 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
856 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
857 geom_str))
858 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
860 g_log_remove_handler ("Gtk", id);
865 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
866 and a Gtk+ menu bar, we get resize events for the edit part of the
867 frame only. We let Gtk+ deal with the Gtk+ parts.
868 F is the frame to resize.
869 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
871 void
872 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
874 int width, height;
876 if (pixelwidth == -1 && pixelheight == -1)
878 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
879 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
880 0, 0, &pixelwidth, &pixelheight);
881 else
882 return;
885 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
886 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
888 frame_size_history_add
889 (f, Qxg_frame_resized, width, height, Qnil);
891 if (width != FRAME_TEXT_WIDTH (f)
892 || height != FRAME_TEXT_HEIGHT (f)
893 || pixelwidth != FRAME_PIXEL_WIDTH (f)
894 || pixelheight != FRAME_PIXEL_HEIGHT (f))
896 x_clear_under_internal_border (f);
897 change_frame_size (f, width, height, 0, 1, 0, 1);
898 SET_FRAME_GARBAGED (f);
899 cancel_mouse_face (f);
903 /* Resize the outer window of frame F after changing the height.
904 COLUMNS/ROWS is the size the edit area shall have after the resize. */
906 void
907 xg_frame_set_char_size (struct frame *f, int width, int height)
909 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
910 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
911 Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
912 gint gwidth, gheight;
913 int totalheight
914 = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
915 int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
917 if (FRAME_PIXEL_HEIGHT (f) == 0)
918 return;
920 gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
921 &gwidth, &gheight);
923 /* Do this before resize, as we don't know yet if we will be resized. */
924 x_clear_under_internal_border (f);
926 totalheight /= xg_get_scale (f);
927 totalwidth /= xg_get_scale (f);
929 x_wm_set_size_hint (f, 0, 0);
931 /* Resize the top level widget so rows and columns remain constant.
933 When the frame is fullheight and we only want to change the width
934 or it is fullwidth and we only want to change the height we should
935 be able to preserve the fullscreen property. However, due to the
936 fact that we have to send a resize request anyway, the window
937 manager will abolish it. At least the respective size should
938 remain unchanged but giving the frame back its normal size will
939 be broken ... */
940 if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
942 frame_size_history_add
943 (f, Qxg_frame_set_char_size_1, width, height,
944 list2 (make_number (gheight), make_number (totalheight)));
946 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
947 gwidth, totalheight);
949 else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
951 frame_size_history_add
952 (f, Qxg_frame_set_char_size_2, width, height,
953 list2 (make_number (gwidth), make_number (totalwidth)));
955 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
956 totalwidth, gheight);
958 else
960 frame_size_history_add
961 (f, Qxg_frame_set_char_size_3, width, height,
962 list2 (make_number (totalwidth), make_number (totalheight)));
964 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
965 totalwidth, totalheight);
966 fullscreen = Qnil;
969 SET_FRAME_GARBAGED (f);
970 cancel_mouse_face (f);
972 /* We can not call change_frame_size for a mapped frame,
973 we can not set pixel width/height either. The window manager may
974 override our resize request, XMonad does this all the time.
975 The best we can do is try to sync, so lisp code sees the updated
976 size as fast as possible.
977 For unmapped windows, we can set rows/cols. When
978 the frame is mapped again we will (hopefully) get the correct size. */
979 if (FRAME_VISIBLE_P (f))
981 /* Must call this to flush out events */
982 (void)gtk_events_pending ();
983 gdk_flush ();
984 x_wait_for_event (f, ConfigureNotify);
986 if (!NILP (fullscreen))
987 /* Try to restore fullscreen state. */
989 store_frame_param (f, Qfullscreen, fullscreen);
990 x_set_fullscreen (f, fullscreen, fullscreen);
993 else
994 adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
998 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
999 The policy is to keep the number of editable lines. */
1001 #if 0
1002 static void
1003 xg_height_or_width_changed (struct frame *f)
1005 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1006 FRAME_TOTAL_PIXEL_WIDTH (f),
1007 FRAME_TOTAL_PIXEL_HEIGHT (f));
1008 f->output_data.x->hint_flags = 0;
1009 x_wm_set_size_hint (f, 0, 0);
1011 #endif
1013 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1014 Must be done like this, because GtkWidget:s can have "hidden"
1015 X Window that aren't accessible.
1017 Return 0 if no widget match WDESC. */
1019 GtkWidget *
1020 xg_win_to_widget (Display *dpy, Window wdesc)
1022 gpointer gdkwin;
1023 GtkWidget *gwdesc = 0;
1025 block_input ();
1027 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1028 wdesc);
1029 if (gdkwin)
1031 GdkEvent event;
1032 event.any.window = gdkwin;
1033 event.any.type = GDK_NOTHING;
1034 gwdesc = gtk_get_event_widget (&event);
1037 unblock_input ();
1038 return gwdesc;
1041 /* Set the background of widget W to PIXEL. */
1043 static void
1044 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
1046 #ifdef HAVE_GTK3
1047 GdkRGBA bg;
1048 XColor xbg;
1049 xbg.pixel = pixel;
1050 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1052 bg.red = (double)xbg.red/65535.0;
1053 bg.green = (double)xbg.green/65535.0;
1054 bg.blue = (double)xbg.blue/65535.0;
1055 bg.alpha = 1.0;
1056 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1058 #else
1059 GdkColor bg;
1060 GdkColormap *map = gtk_widget_get_colormap (w);
1061 gdk_colormap_query_color (map, pixel, &bg);
1062 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1063 #endif
1066 /* Callback called when the gtk theme changes.
1067 We notify lisp code so it can fix faces used for region for example. */
1069 static void
1070 style_changed_cb (GObject *go,
1071 GParamSpec *spec,
1072 gpointer user_data)
1074 struct input_event event;
1075 GdkDisplay *gdpy = user_data;
1076 const char *display_name = gdk_display_get_name (gdpy);
1077 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1079 EVENT_INIT (event);
1080 event.kind = CONFIG_CHANGED_EVENT;
1081 event.frame_or_window = build_string (display_name);
1082 /* Theme doesn't change often, so intern is called seldom. */
1083 event.arg = intern ("theme-name");
1084 kbd_buffer_store_event (&event);
1086 update_theme_scrollbar_width ();
1087 update_theme_scrollbar_height ();
1089 /* If scroll bar width changed, we need set the new size on all frames
1090 on this display. */
1091 if (dpy)
1093 Lisp_Object rest, frame;
1094 FOR_EACH_FRAME (rest, frame)
1096 struct frame *f = XFRAME (frame);
1097 if (FRAME_LIVE_P (f)
1098 && FRAME_X_P (f)
1099 && FRAME_X_DISPLAY (f) == dpy)
1101 x_set_scroll_bar_default_width (f);
1102 x_set_scroll_bar_default_height (f);
1103 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
1109 /* Called when a delete-event occurs on WIDGET. */
1111 static gboolean
1112 delete_cb (GtkWidget *widget,
1113 GdkEvent *event,
1114 gpointer user_data)
1116 return TRUE;
1119 /* Create and set up the GTK widgets for frame F.
1120 Return true if creation succeeded. */
1122 bool
1123 xg_create_frame_widgets (struct frame *f)
1125 GtkWidget *wtop;
1126 GtkWidget *wvbox, *whbox;
1127 GtkWidget *wfixed;
1128 #ifndef HAVE_GTK3
1129 GtkRcStyle *style;
1130 #endif
1131 char *title = 0;
1133 block_input ();
1135 if (FRAME_X_EMBEDDED_P (f))
1137 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1138 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1140 else
1141 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1143 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1144 has backported it to Gtk+ 2.0 and they add the resize grip for
1145 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1146 forever, so disable the grip. */
1147 #if (! GTK_CHECK_VERSION (3, 0, 0) \
1148 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1149 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1150 #endif
1152 xg_set_screen (wtop, f);
1154 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1155 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1156 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1157 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1159 #ifdef HAVE_GTK3
1160 wfixed = emacs_fixed_new (f);
1161 #else
1162 wfixed = gtk_fixed_new ();
1163 #endif
1165 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1167 if (wtop) gtk_widget_destroy (wtop);
1168 if (wvbox) gtk_widget_destroy (wvbox);
1169 if (whbox) gtk_widget_destroy (whbox);
1170 if (wfixed) gtk_widget_destroy (wfixed);
1172 unblock_input ();
1173 return 0;
1176 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1177 gtk_widget_set_name (wtop, EMACS_CLASS);
1178 gtk_widget_set_name (wvbox, "pane");
1179 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1181 /* If this frame has a title or name, set it in the title bar. */
1182 if (! NILP (f->title))
1183 title = SSDATA (ENCODE_UTF_8 (f->title));
1184 else if (! NILP (f->name))
1185 title = SSDATA (ENCODE_UTF_8 (f->name));
1187 if (title)
1188 gtk_window_set_title (GTK_WINDOW (wtop), title);
1190 if (FRAME_UNDECORATED (f))
1192 gtk_window_set_decorated (GTK_WINDOW (wtop), FALSE);
1193 store_frame_param (f, Qundecorated, Qt);
1196 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1197 FRAME_GTK_WIDGET (f) = wfixed;
1198 f->output_data.x->vbox_widget = wvbox;
1199 f->output_data.x->hbox_widget = whbox;
1201 gtk_widget_set_has_window (wfixed, TRUE);
1203 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1204 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1205 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1207 if (FRAME_EXTERNAL_TOOL_BAR (f))
1208 update_frame_tool_bar (f);
1210 /* We don't want this widget double buffered, because we draw on it
1211 with regular X drawing primitives, so from a GTK/GDK point of
1212 view, the widget is totally blank. When an expose comes, this
1213 will make the widget blank, and then Emacs redraws it. This flickers
1214 a lot, so we turn off double buffering. */
1215 gtk_widget_set_double_buffered (wfixed, FALSE);
1217 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1218 SSDATA (Vx_resource_name),
1219 SSDATA (Vx_resource_class));
1221 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1222 GTK is to destroy the widget. We want Emacs to do that instead. */
1223 g_signal_connect (G_OBJECT (wtop), "delete-event",
1224 G_CALLBACK (delete_cb), f);
1226 /* Convert our geometry parameters into a geometry string
1227 and specify it.
1228 GTK will itself handle calculating the real position this way. */
1229 xg_set_geometry (f);
1230 f->win_gravity
1231 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1233 gtk_widget_add_events (wfixed,
1234 GDK_POINTER_MOTION_MASK
1235 | GDK_EXPOSURE_MASK
1236 | GDK_BUTTON_PRESS_MASK
1237 | GDK_BUTTON_RELEASE_MASK
1238 | GDK_KEY_PRESS_MASK
1239 | GDK_ENTER_NOTIFY_MASK
1240 | GDK_LEAVE_NOTIFY_MASK
1241 | GDK_FOCUS_CHANGE_MASK
1242 | GDK_STRUCTURE_MASK
1243 | GDK_VISIBILITY_NOTIFY_MASK);
1245 /* Must realize the windows so the X window gets created. It is used
1246 by callers of this function. */
1247 gtk_widget_realize (wfixed);
1248 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1249 initial_set_up_x_back_buffer (f);
1251 /* Since GTK clears its window by filling with the background color,
1252 we must keep X and GTK background in sync. */
1253 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1255 #ifndef HAVE_GTK3
1256 /* Also, do not let any background pixmap to be set, this looks very
1257 bad as Emacs overwrites the background pixmap with its own idea
1258 of background color. */
1259 style = gtk_widget_get_modifier_style (wfixed);
1261 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1262 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1263 gtk_widget_modify_style (wfixed, style);
1264 #else
1265 gtk_widget_set_can_focus (wfixed, TRUE);
1266 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1267 #endif
1269 if (FRAME_OVERRIDE_REDIRECT (f))
1271 GdkWindow *gwin = gtk_widget_get_window (wtop);
1273 if (gwin)
1274 gdk_window_set_override_redirect (gwin, TRUE);
1277 #ifdef USE_GTK_TOOLTIP
1278 /* Steal a tool tip window we can move ourselves. */
1279 f->output_data.x->ttip_widget = 0;
1280 f->output_data.x->ttip_lbl = 0;
1281 f->output_data.x->ttip_window = 0;
1282 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1283 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1284 #endif
1287 GdkScreen *screen = gtk_widget_get_screen (wtop);
1288 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1289 /* Only connect this signal once per screen. */
1290 if (! g_signal_handler_find (G_OBJECT (gs),
1291 G_SIGNAL_MATCH_FUNC,
1292 0, 0, 0,
1293 (gpointer) G_CALLBACK (style_changed_cb),
1296 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1297 G_CALLBACK (style_changed_cb),
1298 gdk_screen_get_display (screen));
1302 unblock_input ();
1304 return 1;
1307 void
1308 xg_free_frame_widgets (struct frame *f)
1310 if (FRAME_GTK_OUTER_WIDGET (f))
1312 #ifdef USE_GTK_TOOLTIP
1313 struct x_output *x = f->output_data.x;
1314 #endif
1315 struct xg_frame_tb_info *tbinfo
1316 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1317 TB_INFO_KEY);
1318 if (tbinfo)
1319 xfree (tbinfo);
1321 /* x_free_frame_resources should have taken care of it */
1322 eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
1323 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1324 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1325 FRAME_X_RAW_DRAWABLE (f) = 0;
1326 FRAME_GTK_OUTER_WIDGET (f) = 0;
1327 #ifdef USE_GTK_TOOLTIP
1328 if (x->ttip_lbl)
1329 gtk_widget_destroy (x->ttip_lbl);
1330 if (x->ttip_widget)
1331 g_object_unref (G_OBJECT (x->ttip_widget));
1332 #endif
1336 /* Set the normal size hints for the window manager, for frame F.
1337 FLAGS is the flags word to use--or 0 meaning preserve the flags
1338 that the window now has.
1339 If USER_POSITION, set the User Position
1340 flag (this is useful when FLAGS is 0). */
1342 void
1343 x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
1345 /* Must use GTK routines here, otherwise GTK resets the size hints
1346 to its own defaults. */
1347 GdkGeometry size_hints;
1348 gint hint_flags = 0;
1349 int base_width, base_height;
1350 int min_rows = 0, min_cols = 0;
1351 int win_gravity = f->win_gravity;
1352 Lisp_Object fs_state, frame;
1353 int scale = xg_get_scale (f);
1355 /* Don't set size hints during initialization; that apparently leads
1356 to a race condition. See the thread at
1357 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1358 if (NILP (Vafter_init_time)
1359 || !FRAME_GTK_OUTER_WIDGET (f)
1360 || FRAME_PARENT_FRAME (f))
1361 return;
1363 XSETFRAME (frame, f);
1364 fs_state = Fframe_parameter (frame, Qfullscreen);
1365 if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
1366 (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
1367 x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
1369 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1370 Gtk3 don't get along and the frame shrinks (!).
1372 return;
1375 if (flags)
1377 memset (&size_hints, 0, sizeof (size_hints));
1378 f->output_data.x->size_hints = size_hints;
1379 f->output_data.x->hint_flags = hint_flags;
1381 else
1382 flags = f->size_hint_flags;
1384 size_hints = f->output_data.x->size_hints;
1385 hint_flags = f->output_data.x->hint_flags;
1387 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1388 size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
1389 size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
1391 hint_flags |= GDK_HINT_BASE_SIZE;
1392 /* Use one row/col here so base_height/width does not become zero.
1393 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
1394 Obviously this makes the row/col value displayed off by 1. */
1395 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1396 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1397 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1399 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1400 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1402 size_hints.base_width = base_width;
1403 size_hints.base_height = base_height;
1404 size_hints.min_width = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
1405 size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
1407 /* These currently have a one to one mapping with the X values, but I
1408 don't think we should rely on that. */
1409 hint_flags |= GDK_HINT_WIN_GRAVITY;
1410 size_hints.win_gravity = 0;
1411 if (win_gravity == NorthWestGravity)
1412 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1413 else if (win_gravity == NorthGravity)
1414 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1415 else if (win_gravity == NorthEastGravity)
1416 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1417 else if (win_gravity == WestGravity)
1418 size_hints.win_gravity = GDK_GRAVITY_WEST;
1419 else if (win_gravity == CenterGravity)
1420 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1421 else if (win_gravity == EastGravity)
1422 size_hints.win_gravity = GDK_GRAVITY_EAST;
1423 else if (win_gravity == SouthWestGravity)
1424 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1425 else if (win_gravity == SouthGravity)
1426 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1427 else if (win_gravity == SouthEastGravity)
1428 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1429 else if (win_gravity == StaticGravity)
1430 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1432 if (x_gtk_use_window_move)
1434 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
1435 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
1436 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
1439 if (user_position)
1441 hint_flags &= ~GDK_HINT_POS;
1442 hint_flags |= GDK_HINT_USER_POS;
1445 size_hints.base_width /= scale;
1446 size_hints.base_height /= scale;
1447 size_hints.width_inc /= scale;
1448 size_hints.height_inc /= scale;
1450 if (hint_flags != f->output_data.x->hint_flags
1451 || memcmp (&size_hints,
1452 &f->output_data.x->size_hints,
1453 sizeof (size_hints)) != 0)
1455 block_input ();
1456 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1457 NULL, &size_hints, hint_flags);
1458 f->output_data.x->size_hints = size_hints;
1459 f->output_data.x->hint_flags = hint_flags;
1460 unblock_input ();
1464 /* Change background color of a frame.
1465 Since GTK uses the background color to clear the window, we must
1466 keep the GTK and X colors in sync.
1467 F is the frame to change,
1468 BG is the pixel value to change to. */
1470 void
1471 xg_set_background_color (struct frame *f, unsigned long bg)
1473 if (FRAME_GTK_WIDGET (f))
1475 block_input ();
1476 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1478 Lisp_Object bar;
1479 for (bar = FRAME_SCROLL_BARS (f);
1480 !NILP (bar);
1481 bar = XSCROLL_BAR (bar)->next)
1483 GtkWidget *scrollbar =
1484 xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window);
1485 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
1486 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
1489 unblock_input ();
1493 /* Change the frame's decoration (title bar + resize borders). This
1494 might not work with all window managers. */
1495 void
1496 xg_set_undecorated (struct frame *f, Lisp_Object undecorated)
1498 if (FRAME_GTK_WIDGET (f))
1500 block_input ();
1501 gtk_window_set_decorated (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1502 NILP (undecorated) ? TRUE : FALSE);
1503 unblock_input ();
1508 /* Restack F1 below F2, above if ABOVE_FLAG is true. This might not
1509 work with all window managers. */
1510 void
1511 xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag)
1513 #if GTK_CHECK_VERSION (2, 18, 0)
1514 block_input ();
1515 if (FRAME_GTK_OUTER_WIDGET (f1) && FRAME_GTK_OUTER_WIDGET (f2))
1517 GdkWindow *gwin1 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f1));
1518 GdkWindow *gwin2 = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f2));
1519 Lisp_Object frame1, frame2;
1521 XSETFRAME (frame1, f1);
1522 XSETFRAME (frame2, f2);
1524 gdk_window_restack (gwin1, gwin2, above_flag);
1525 x_sync (f1);
1527 unblock_input ();
1528 #endif
1532 /* Don't show frame in taskbar, don't ALT-TAB to it. */
1533 void
1534 xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
1536 block_input ();
1537 if (FRAME_GTK_WIDGET (f))
1538 gdk_window_set_skip_taskbar_hint
1539 (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
1540 NILP (skip_taskbar) ? FALSE : TRUE);
1541 unblock_input ();
1545 /* Don't give frame focus. */
1546 void
1547 xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
1549 block_input ();
1550 if (FRAME_GTK_WIDGET (f))
1552 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1553 gboolean g_no_focus_on_map = NILP (no_focus_on_map) ? TRUE : FALSE;
1555 gtk_window_set_focus_on_map (gwin, g_no_focus_on_map);
1557 unblock_input ();
1561 void
1562 xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus)
1564 block_input ();
1565 if (FRAME_GTK_WIDGET (f))
1567 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1568 gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
1570 gtk_window_set_accept_focus (gwin, g_no_accept_focus);
1572 unblock_input ();
1575 void
1576 xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect)
1578 block_input ();
1580 if (FRAME_GTK_OUTER_WIDGET (f))
1582 GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f));
1584 gdk_window_set_override_redirect (gwin, NILP (override_redirect) ? FALSE : TRUE);
1587 unblock_input ();
1590 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1591 functions so GTK does not overwrite the icon. */
1593 void
1594 xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1596 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1597 icon_pixmap,
1598 icon_mask);
1599 if (gp)
1600 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1605 /***********************************************************************
1606 Dialog functions
1607 ***********************************************************************/
1608 /* Return the dialog title to use for a dialog of type KEY.
1609 This is the encoding used by lwlib. We use the same for GTK. */
1611 static const char *
1612 get_dialog_title (char key)
1614 const char *title = "";
1616 switch (key) {
1617 case 'E': case 'e':
1618 title = "Error";
1619 break;
1621 case 'I': case 'i':
1622 title = "Information";
1623 break;
1625 case 'L': case 'l':
1626 title = "Prompt";
1627 break;
1629 case 'P': case 'p':
1630 title = "Prompt";
1631 break;
1633 case 'Q': case 'q':
1634 title = "Question";
1635 break;
1638 return title;
1641 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1642 the dialog, but return TRUE so the event does not propagate further
1643 in GTK. This prevents GTK from destroying the dialog widget automatically
1644 and we can always destroy the widget manually, regardless of how
1645 it was popped down (button press or WM_DELETE_WINDOW).
1646 W is the dialog widget.
1647 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1648 user_data is NULL (not used).
1650 Returns TRUE to end propagation of event. */
1652 static gboolean
1653 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1655 gtk_widget_unmap (w);
1656 return TRUE;
1659 /* Create a popup dialog window. See also xg_create_widget below.
1660 WV is a widget_value describing the dialog.
1661 SELECT_CB is the callback to use when a button has been pressed.
1662 DEACTIVATE_CB is the callback to use when the dialog pops down.
1664 Returns the GTK dialog widget. */
1666 static GtkWidget *
1667 create_dialog (widget_value *wv,
1668 GCallback select_cb,
1669 GCallback deactivate_cb)
1671 const char *title = get_dialog_title (wv->name[0]);
1672 int total_buttons = wv->name[1] - '0';
1673 int right_buttons = wv->name[4] - '0';
1674 int left_buttons;
1675 int button_nr = 0;
1676 int button_spacing = 10;
1677 GtkWidget *wdialog = gtk_dialog_new ();
1678 GtkDialog *wd = GTK_DIALOG (wdialog);
1679 widget_value *item;
1680 GtkWidget *whbox_down;
1682 /* If the number of buttons is greater than 4, make two rows of buttons
1683 instead. This looks better. */
1684 bool make_two_rows = total_buttons > 4;
1686 #if GTK_CHECK_VERSION (3, 12, 0)
1687 GtkBuilder *gbld = gtk_builder_new ();
1688 GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
1689 gbld,
1690 "action_area");
1691 GtkBox *cur_box = GTK_BOX (go);
1692 g_object_unref (G_OBJECT (gbld));
1693 #else
1694 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1695 #endif
1697 if (right_buttons == 0) right_buttons = total_buttons/2;
1698 left_buttons = total_buttons - right_buttons;
1700 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1701 gtk_widget_set_name (wdialog, "emacs-dialog");
1704 if (make_two_rows)
1706 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1707 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1708 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1709 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1710 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1711 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1713 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1714 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1715 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1717 cur_box = GTK_BOX (whbox_up);
1720 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1721 G_CALLBACK (dialog_delete_callback), 0);
1723 if (deactivate_cb)
1725 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1726 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1729 for (item = wv->contents; item; item = item->next)
1731 char *utf8_label = get_utf8_string (item->value);
1732 GtkWidget *w;
1733 GtkRequisition req;
1735 if (item->name && strcmp (item->name, "message") == 0)
1737 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1738 /* This is the text part of the dialog. */
1739 w = gtk_label_new (utf8_label);
1740 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1741 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1742 #if GTK_CHECK_VERSION (3, 14, 0)
1743 gtk_widget_set_halign (w, GTK_ALIGN_START);
1744 gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
1745 #else
1746 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1747 #endif
1748 /* Try to make dialog look better. Must realize first so
1749 the widget can calculate the size it needs. */
1750 gtk_widget_realize (w);
1751 gtk_widget_get_preferred_size (w, NULL, &req);
1752 gtk_box_set_spacing (wvbox, req.height);
1753 if (item->value && strlen (item->value) > 0)
1754 button_spacing = 2*req.width/strlen (item->value);
1755 if (button_spacing < 10) button_spacing = 10;
1757 else
1759 /* This is one button to add to the dialog. */
1760 w = gtk_button_new_with_label (utf8_label);
1761 if (! item->enabled)
1762 gtk_widget_set_sensitive (w, FALSE);
1763 if (select_cb)
1764 g_signal_connect (G_OBJECT (w), "clicked",
1765 select_cb, item->call_data);
1767 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1768 if (++button_nr == left_buttons)
1770 if (make_two_rows)
1771 cur_box = GTK_BOX (whbox_down);
1775 if (utf8_label)
1776 g_free (utf8_label);
1779 return wdialog;
1782 struct xg_dialog_data
1784 GMainLoop *loop;
1785 int response;
1786 GtkWidget *w;
1787 guint timerid;
1790 /* Function that is called when the file or font dialogs pop down.
1791 W is the dialog widget, RESPONSE is the response code.
1792 USER_DATA is what we passed in to g_signal_connect. */
1794 static void
1795 xg_dialog_response_cb (GtkDialog *w,
1796 gint response,
1797 gpointer user_data)
1799 struct xg_dialog_data *dd = user_data;
1800 dd->response = response;
1801 g_main_loop_quit (dd->loop);
1805 /* Destroy the dialog. This makes it pop down. */
1807 static void
1808 pop_down_dialog (void *arg)
1810 struct xg_dialog_data *dd = arg;
1812 block_input ();
1813 if (dd->w) gtk_widget_destroy (dd->w);
1814 if (dd->timerid != 0) g_source_remove (dd->timerid);
1816 g_main_loop_quit (dd->loop);
1817 g_main_loop_unref (dd->loop);
1819 unblock_input ();
1822 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1823 Pass DATA as gpointer so we can use this as a callback. */
1825 static gboolean
1826 xg_maybe_add_timer (gpointer data)
1828 struct xg_dialog_data *dd = data;
1829 struct timespec next_time = timer_check ();
1831 dd->timerid = 0;
1833 if (timespec_valid_p (next_time))
1835 time_t s = next_time.tv_sec;
1836 int per_ms = TIMESPEC_RESOLUTION / 1000;
1837 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
1838 if (s <= ((guint) -1 - ms) / 1000)
1839 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1841 return FALSE;
1845 /* Pops up a modal dialog W and waits for response.
1846 We don't use gtk_dialog_run because we want to process emacs timers.
1847 The dialog W is not destroyed when this function returns. */
1849 static int
1850 xg_dialog_run (struct frame *f, GtkWidget *w)
1852 ptrdiff_t count = SPECPDL_INDEX ();
1853 struct xg_dialog_data dd;
1855 xg_set_screen (w, f);
1856 gtk_window_set_transient_for (GTK_WINDOW (w),
1857 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1858 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1859 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1861 dd.loop = g_main_loop_new (NULL, FALSE);
1862 dd.response = GTK_RESPONSE_CANCEL;
1863 dd.w = w;
1864 dd.timerid = 0;
1866 g_signal_connect (G_OBJECT (w),
1867 "response",
1868 G_CALLBACK (xg_dialog_response_cb),
1869 &dd);
1870 /* Don't destroy the widget if closed by the window manager close button. */
1871 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1872 gtk_widget_show (w);
1874 record_unwind_protect_ptr (pop_down_dialog, &dd);
1876 (void) xg_maybe_add_timer (&dd);
1877 g_main_loop_run (dd.loop);
1879 dd.w = 0;
1880 unbind_to (count, Qnil);
1882 return dd.response;
1886 /***********************************************************************
1887 File dialog functions
1888 ***********************************************************************/
1889 /* Return true if the old file selection dialog is being used. */
1891 bool
1892 xg_uses_old_file_dialog (void)
1894 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1895 return x_gtk_use_old_file_dialog;
1896 #else
1897 return 0;
1898 #endif
1902 typedef char * (*xg_get_file_func) (GtkWidget *);
1904 /* Return the selected file for file chooser dialog W.
1905 The returned string must be free:d. */
1907 static char *
1908 xg_get_file_name_from_chooser (GtkWidget *w)
1910 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1913 /* Callback called when the "Show hidden files" toggle is pressed.
1914 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1916 static void
1917 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1919 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1920 gboolean visible;
1921 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1922 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1926 /* Callback called when a property changes in a file chooser.
1927 GOBJECT is the file chooser dialog, ARG1 describes the property.
1928 USER_DATA is the toggle widget in the file chooser dialog.
1929 We use this to update the "Show hidden files" toggle when the user
1930 changes that property by right clicking in the file list. */
1932 static void
1933 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1935 if (strcmp (arg1->name, "show-hidden") == 0)
1937 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1938 gboolean visible, toggle_on;
1940 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1941 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1943 if (!!visible != !!toggle_on)
1945 gpointer cb = (gpointer) G_CALLBACK (xg_toggle_visibility_cb);
1946 g_signal_handlers_block_by_func (G_OBJECT (wtoggle), cb, gobject);
1947 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1948 g_signal_handlers_unblock_by_func (G_OBJECT (wtoggle), cb, gobject);
1950 x_gtk_show_hidden_files = visible;
1954 /* Read a file name from the user using a file chooser dialog.
1955 F is the current frame.
1956 PROMPT is a prompt to show to the user. May not be NULL.
1957 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1958 If MUSTMATCH_P, the returned file name must be an existing
1959 file. (Actually, this only has cosmetic effects, the user can
1960 still enter a non-existing file.) *FUNC is set to a function that
1961 can be used to retrieve the selected file name from the returned widget.
1963 Returns the created widget. */
1965 static GtkWidget *
1966 xg_get_file_with_chooser (struct frame *f,
1967 char *prompt,
1968 char *default_filename,
1969 bool mustmatch_p, bool only_dir_p,
1970 xg_get_file_func *func)
1972 char msgbuf[1024];
1974 GtkWidget *filewin, *wtoggle, *wbox;
1975 GtkWidget *wmessage UNINIT;
1976 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1977 GtkFileChooserAction action = (mustmatch_p ?
1978 GTK_FILE_CHOOSER_ACTION_OPEN :
1979 GTK_FILE_CHOOSER_ACTION_SAVE);
1981 if (only_dir_p)
1982 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1984 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1985 XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
1986 (mustmatch_p || only_dir_p ?
1987 XG_TEXT_OPEN : XG_TEXT_OK),
1988 GTK_RESPONSE_OK,
1989 NULL);
1990 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
1992 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1993 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
1994 gtk_widget_show (wbox);
1995 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
1997 if (x_gtk_show_hidden_files)
1999 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
2000 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
2002 gtk_widget_show (wtoggle);
2003 g_signal_connect (G_OBJECT (wtoggle), "clicked",
2004 G_CALLBACK (xg_toggle_visibility_cb), filewin);
2005 g_signal_connect (G_OBJECT (filewin), "notify",
2006 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
2008 if (x_gtk_file_dialog_help_text)
2010 char *z = msgbuf;
2011 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
2012 Show the C-l help text only for versions < 2.10. */
2013 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
2014 z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
2015 strcpy (z, "\nIf you don't like this file selector, use the "
2016 "corresponding\nkey binding or customize "
2017 "use-file-dialog to turn it off.");
2019 wmessage = gtk_label_new (msgbuf);
2020 gtk_widget_show (wmessage);
2023 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
2024 if (x_gtk_file_dialog_help_text)
2025 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
2026 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
2028 if (default_filename)
2030 Lisp_Object file;
2031 char *utf8_filename;
2033 file = build_string (default_filename);
2035 /* File chooser does not understand ~/... in the file name. It must be
2036 an absolute name starting with /. */
2037 if (default_filename[0] != '/')
2038 file = Fexpand_file_name (file, Qnil);
2040 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
2041 if (! NILP (Ffile_directory_p (file)))
2042 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
2043 utf8_filename);
2044 else
2046 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
2047 utf8_filename);
2048 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
2050 char *cp = strrchr (utf8_filename, '/');
2051 if (cp) ++cp;
2052 else cp = utf8_filename;
2053 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
2058 *func = xg_get_file_name_from_chooser;
2059 return filewin;
2062 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2064 /* Return the selected file for file selector dialog W.
2065 The returned string must be free:d. */
2067 static char *
2068 xg_get_file_name_from_selector (GtkWidget *w)
2070 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
2071 return xstrdup (gtk_file_selection_get_filename (filesel));
2074 /* Create a file selection dialog.
2075 F is the current frame.
2076 PROMPT is a prompt to show to the user. May not be NULL.
2077 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2078 If MUSTMATCH_P, the returned file name must be an existing
2079 file. *FUNC is set to a function that can be used to retrieve the
2080 selected file name from the returned widget.
2082 Returns the created widget. */
2084 static GtkWidget *
2085 xg_get_file_with_selection (struct frame *f,
2086 char *prompt,
2087 char *default_filename,
2088 bool mustmatch_p, bool only_dir_p,
2089 xg_get_file_func *func)
2091 GtkWidget *filewin;
2092 GtkFileSelection *filesel;
2094 filewin = gtk_file_selection_new (prompt);
2095 filesel = GTK_FILE_SELECTION (filewin);
2097 if (default_filename)
2098 gtk_file_selection_set_filename (filesel, default_filename);
2100 if (mustmatch_p)
2102 /* The selection_entry part of filesel is not documented. */
2103 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
2104 gtk_file_selection_hide_fileop_buttons (filesel);
2107 *func = xg_get_file_name_from_selector;
2109 return filewin;
2111 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
2113 /* Read a file name from the user using a file dialog, either the old
2114 file selection dialog, or the new file chooser dialog. Which to use
2115 depends on what the GTK version used has, and what the value of
2116 gtk-use-old-file-dialog.
2117 F is the current frame.
2118 PROMPT is a prompt to show to the user. May not be NULL.
2119 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2120 If MUSTMATCH_P, the returned file name must be an existing
2121 file.
2123 Returns a file name or NULL if no file was selected.
2124 The returned string must be freed by the caller. */
2126 char *
2127 xg_get_file_name (struct frame *f,
2128 char *prompt,
2129 char *default_filename,
2130 bool mustmatch_p,
2131 bool only_dir_p)
2133 GtkWidget *w = 0;
2134 char *fn = 0;
2135 int filesel_done = 0;
2136 xg_get_file_func func;
2138 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2140 if (xg_uses_old_file_dialog ())
2141 w = xg_get_file_with_selection (f, prompt, default_filename,
2142 mustmatch_p, only_dir_p, &func);
2143 else
2144 w = xg_get_file_with_chooser (f, prompt, default_filename,
2145 mustmatch_p, only_dir_p, &func);
2147 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
2148 w = xg_get_file_with_chooser (f, prompt, default_filename,
2149 mustmatch_p, only_dir_p, &func);
2150 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
2152 gtk_widget_set_name (w, "emacs-filedialog");
2154 filesel_done = xg_dialog_run (f, w);
2155 if (filesel_done == GTK_RESPONSE_OK)
2156 fn = (*func) (w);
2158 gtk_widget_destroy (w);
2159 return fn;
2162 /***********************************************************************
2163 GTK font chooser
2164 ***********************************************************************/
2166 #ifdef HAVE_FREETYPE
2168 #if USE_NEW_GTK_FONT_CHOOSER
2170 #define XG_WEIGHT_TO_SYMBOL(w) \
2171 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2172 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2173 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2174 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2175 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2176 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2177 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2178 : Qultra_bold)
2180 #define XG_STYLE_TO_SYMBOL(s) \
2181 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2182 : s == PANGO_STYLE_ITALIC ? Qitalic \
2183 : Qnormal)
2185 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2188 static char *x_last_font_name;
2190 /* Pop up a GTK font selector and return the name of the font the user
2191 selects, as a C string. The returned font name follows GTK's own
2192 format:
2194 `FAMILY [VALUE1 VALUE2] SIZE'
2196 This can be parsed using font_parse_fcname in font.c.
2197 DEFAULT_NAME, if non-zero, is the default font name. */
2199 Lisp_Object
2200 xg_get_font (struct frame *f, const char *default_name)
2202 GtkWidget *w;
2203 int done = 0;
2204 Lisp_Object font = Qnil;
2206 w = gtk_font_chooser_dialog_new
2207 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2209 if (default_name)
2211 /* Convert fontconfig names to Gtk names, i.e. remove - before
2212 number */
2213 char *p = strrchr (default_name, '-');
2214 if (p)
2216 char *ep = p+1;
2217 while (c_isdigit (*ep))
2218 ++ep;
2219 if (*ep == '\0') *p = ' ';
2222 else if (x_last_font_name)
2223 default_name = x_last_font_name;
2225 if (default_name)
2226 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2228 gtk_widget_set_name (w, "emacs-fontdialog");
2229 done = xg_dialog_run (f, w);
2230 if (done == GTK_RESPONSE_OK)
2232 #if USE_NEW_GTK_FONT_CHOOSER
2233 /* Use the GTK3 font chooser. */
2234 PangoFontDescription *desc
2235 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2237 if (desc)
2239 const char *name = pango_font_description_get_family (desc);
2240 gint size = pango_font_description_get_size (desc);
2241 PangoWeight weight = pango_font_description_get_weight (desc);
2242 PangoStyle style = pango_font_description_get_style (desc);
2244 #ifdef USE_CAIRO
2245 #define FONT_TYPE_WANTED (Qftcr)
2246 #else
2247 #define FONT_TYPE_WANTED (Qxft)
2248 #endif
2249 font = CALLN (Ffont_spec,
2250 QCname, build_string (name),
2251 QCsize, make_float (pango_units_to_double (size)),
2252 QCweight, XG_WEIGHT_TO_SYMBOL (weight),
2253 QCslant, XG_STYLE_TO_SYMBOL (style),
2254 QCtype,
2255 FONT_TYPE_WANTED);
2257 pango_font_description_free (desc);
2258 dupstring (&x_last_font_name, name);
2261 #else /* Use old font selector, which just returns the font name. */
2263 char *font_name
2264 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2266 if (font_name)
2268 font = build_string (font_name);
2269 g_free (x_last_font_name);
2270 x_last_font_name = font_name;
2272 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2275 gtk_widget_destroy (w);
2276 return font;
2278 #endif /* HAVE_FREETYPE */
2282 /***********************************************************************
2283 Menu functions.
2284 ***********************************************************************/
2286 /* The name of menu items that can be used for customization. Since GTK
2287 RC files are very crude and primitive, we have to set this on all
2288 menu item names so a user can easily customize menu items. */
2290 #define MENU_ITEM_NAME "emacs-menuitem"
2293 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2294 during GC. The next member points to the items. */
2295 static xg_list_node xg_menu_cb_list;
2297 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2298 during GC. The next member points to the items. */
2299 static xg_list_node xg_menu_item_cb_list;
2301 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2302 F is the frame CL_DATA will be initialized for.
2303 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2305 The menu bar and all sub menus under the menu bar in a frame
2306 share the same structure, hence the reference count.
2308 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2309 allocated xg_menu_cb_data if CL_DATA is NULL. */
2311 static xg_menu_cb_data *
2312 make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
2314 if (! cl_data)
2316 cl_data = xmalloc (sizeof *cl_data);
2317 cl_data->f = f;
2318 cl_data->menu_bar_vector = f->menu_bar_vector;
2319 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2320 cl_data->highlight_cb = highlight_cb;
2321 cl_data->ref_count = 0;
2323 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2326 cl_data->ref_count++;
2328 return cl_data;
2331 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2332 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2334 When the menu bar is updated, menu items may have been added and/or
2335 removed, so menu_bar_vector and menu_bar_items_used change. We must
2336 then update CL_DATA since it is used to determine which menu
2337 item that is invoked in the menu.
2338 HIGHLIGHT_CB could change, there is no check that the same
2339 function is given when modifying a menu bar as was given when
2340 creating the menu bar. */
2342 static void
2343 update_cl_data (xg_menu_cb_data *cl_data,
2344 struct frame *f,
2345 GCallback highlight_cb)
2347 if (cl_data)
2349 cl_data->f = f;
2350 cl_data->menu_bar_vector = f->menu_bar_vector;
2351 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2352 cl_data->highlight_cb = highlight_cb;
2356 /* Decrease reference count for CL_DATA.
2357 If reference count is zero, free CL_DATA. */
2359 static void
2360 unref_cl_data (xg_menu_cb_data *cl_data)
2362 if (cl_data && cl_data->ref_count > 0)
2364 cl_data->ref_count--;
2365 if (cl_data->ref_count == 0)
2367 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2368 xfree (cl_data);
2373 /* Function that marks all lisp data during GC. */
2375 void
2376 xg_mark_data (void)
2378 xg_list_node *iter;
2379 Lisp_Object rest, frame;
2381 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2382 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2384 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2386 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2388 if (! NILP (cb_data->help))
2389 mark_object (cb_data->help);
2392 FOR_EACH_FRAME (rest, frame)
2394 struct frame *f = XFRAME (frame);
2396 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2398 struct xg_frame_tb_info *tbinfo
2399 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2400 TB_INFO_KEY);
2401 if (tbinfo)
2403 mark_object (tbinfo->last_tool_bar);
2404 mark_object (tbinfo->style);
2410 /* Callback called when a menu item is destroyed. Used to free data.
2411 W is the widget that is being destroyed (not used).
2412 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2414 static void
2415 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2417 if (client_data)
2419 xg_menu_item_cb_data *data = client_data;
2420 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2421 xfree (data);
2425 /* Callback called when the pointer enters/leaves a menu item.
2426 W is the parent of the menu item.
2427 EVENT is either an enter event or leave event.
2428 CLIENT_DATA is not used.
2430 Returns FALSE to tell GTK to keep processing this event. */
2432 static gboolean
2433 menuitem_highlight_callback (GtkWidget *w,
2434 GdkEventCrossing *event,
2435 gpointer client_data)
2437 GdkEvent ev;
2438 GtkWidget *subwidget;
2439 xg_menu_item_cb_data *data;
2441 ev.crossing = *event;
2442 subwidget = gtk_get_event_widget (&ev);
2443 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
2444 if (data)
2446 if (! NILP (data->help) && data->cl_data->highlight_cb)
2448 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2449 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2450 (*func) (subwidget, call_data);
2454 return FALSE;
2457 /* Callback called when a menu is destroyed. Used to free data.
2458 W is the widget that is being destroyed (not used).
2459 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2461 static void
2462 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2464 unref_cl_data (client_data);
2467 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2468 must be non-NULL) and can be inserted into a menu item.
2470 Returns the GtkHBox. */
2472 static GtkWidget *
2473 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2475 GtkWidget *wlbl;
2476 GtkWidget *wkey;
2477 GtkWidget *wbox;
2479 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2480 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2481 wlbl = gtk_label_new (utf8_label);
2482 wkey = gtk_label_new (utf8_key);
2484 #if GTK_CHECK_VERSION (3, 14, 0)
2485 gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
2486 gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
2487 gtk_widget_set_halign (wkey, GTK_ALIGN_START);
2488 gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
2489 #else
2490 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2491 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2492 #endif
2493 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2494 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2496 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2497 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2498 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2500 return wbox;
2503 /* Make and return a menu item widget with the key to the right.
2504 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2505 UTF8_KEY is the text representing the key binding.
2506 ITEM is the widget_value describing the menu item.
2508 GROUP is an in/out parameter. If the menu item to be created is not
2509 part of any radio menu group, *GROUP contains NULL on entry and exit.
2510 If the menu item to be created is part of a radio menu group, on entry
2511 *GROUP contains the group to use, or NULL if this is the first item
2512 in the group. On exit, *GROUP contains the radio item group.
2514 Unfortunately, keys don't line up as nicely as in Motif,
2515 but the macOS version doesn't either, so I guess that is OK. */
2517 static GtkWidget *
2518 make_menu_item (const char *utf8_label,
2519 const char *utf8_key,
2520 widget_value *item,
2521 GSList **group)
2523 GtkWidget *w;
2524 GtkWidget *wtoadd = 0;
2526 /* It has been observed that some menu items have a NULL name field.
2527 This will lead to this function being called with a NULL utf8_label.
2528 GTK crashes on that so we set a blank label. Why there is a NULL
2529 name remains to be investigated. */
2530 if (! utf8_label) utf8_label = " ";
2532 if (utf8_key)
2533 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2535 if (item->button_type == BUTTON_TYPE_TOGGLE)
2537 *group = NULL;
2538 if (utf8_key) w = gtk_check_menu_item_new ();
2539 else w = gtk_check_menu_item_new_with_label (utf8_label);
2540 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2542 else if (item->button_type == BUTTON_TYPE_RADIO)
2544 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2545 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2546 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2547 if (item->selected)
2548 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2550 else
2552 *group = NULL;
2553 if (utf8_key) w = gtk_menu_item_new ();
2554 else w = gtk_menu_item_new_with_label (utf8_label);
2557 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2558 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2560 return w;
2563 /* Create a menu item widget, and connect the callbacks.
2564 ITEM describes the menu item.
2565 F is the frame the created menu belongs to.
2566 SELECT_CB is the callback to use when a menu item is selected.
2567 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2568 CL_DATA points to the callback data to be used for this menu.
2569 GROUP is an in/out parameter. If the menu item to be created is not
2570 part of any radio menu group, *GROUP contains NULL on entry and exit.
2571 If the menu item to be created is part of a radio menu group, on entry
2572 *GROUP contains the group to use, or NULL if this is the first item
2573 in the group. On exit, *GROUP contains the radio item group.
2575 Returns the created GtkWidget. */
2577 static GtkWidget *
2578 xg_create_one_menuitem (widget_value *item,
2579 struct frame *f,
2580 GCallback select_cb,
2581 GCallback highlight_cb,
2582 xg_menu_cb_data *cl_data,
2583 GSList **group)
2585 char *utf8_label;
2586 char *utf8_key;
2587 GtkWidget *w;
2588 xg_menu_item_cb_data *cb_data;
2590 utf8_label = get_utf8_string (item->name);
2591 utf8_key = get_utf8_string (item->key);
2593 w = make_menu_item (utf8_label, utf8_key, item, group);
2595 if (utf8_label) g_free (utf8_label);
2596 if (utf8_key) g_free (utf8_key);
2598 cb_data = xmalloc (sizeof *cb_data);
2600 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2602 cb_data->select_id = 0;
2603 cb_data->help = item->help;
2604 cb_data->cl_data = cl_data;
2605 cb_data->call_data = item->call_data;
2607 g_signal_connect (G_OBJECT (w),
2608 "destroy",
2609 G_CALLBACK (menuitem_destroy_callback),
2610 cb_data);
2612 /* Put cb_data in widget, so we can get at it when modifying menubar */
2613 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2615 /* final item, not a submenu */
2616 if (item->call_data && ! item->contents)
2618 if (select_cb)
2619 cb_data->select_id
2620 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2623 return w;
2626 /* Create a full menu tree specified by DATA.
2627 F is the frame the created menu belongs to.
2628 SELECT_CB is the callback to use when a menu item is selected.
2629 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2630 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2631 If POP_UP_P, create a popup menu.
2632 If MENU_BAR_P, create a menu bar.
2633 TOPMENU is the topmost GtkWidget that others shall be placed under.
2634 It may be NULL, in that case we create the appropriate widget
2635 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2636 CL_DATA is the callback data we shall use for this menu, or NULL
2637 if we haven't set the first callback yet.
2638 NAME is the name to give to the top level menu if this function
2639 creates it. May be NULL to not set any name.
2641 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2642 not NULL.
2644 This function calls itself to create submenus. */
2646 static GtkWidget *
2647 create_menus (widget_value *data,
2648 struct frame *f,
2649 GCallback select_cb,
2650 GCallback deactivate_cb,
2651 GCallback highlight_cb,
2652 bool pop_up_p,
2653 bool menu_bar_p,
2654 GtkWidget *topmenu,
2655 xg_menu_cb_data *cl_data,
2656 const char *name)
2658 widget_value *item;
2659 GtkWidget *wmenu = topmenu;
2660 GSList *group = NULL;
2662 if (! topmenu)
2664 if (! menu_bar_p)
2666 wmenu = gtk_menu_new ();
2667 xg_set_screen (wmenu, f);
2668 /* Connect this to the menu instead of items so we get enter/leave for
2669 disabled items also. TODO: Still does not get enter/leave for
2670 disabled items in detached menus. */
2671 g_signal_connect (G_OBJECT (wmenu),
2672 "enter-notify-event",
2673 G_CALLBACK (menuitem_highlight_callback),
2674 NULL);
2675 g_signal_connect (G_OBJECT (wmenu),
2676 "leave-notify-event",
2677 G_CALLBACK (menuitem_highlight_callback),
2678 NULL);
2680 else
2682 wmenu = gtk_menu_bar_new ();
2683 /* Set width of menu bar to a small value so it doesn't enlarge
2684 a small initial frame size. The width will be set to the
2685 width of the frame later on when it is added to a container.
2686 height -1: Natural height. */
2687 gtk_widget_set_size_request (wmenu, 1, -1);
2690 /* Put cl_data on the top menu for easier access. */
2691 cl_data = make_cl_data (cl_data, f, highlight_cb);
2692 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2693 g_signal_connect (G_OBJECT (wmenu), "destroy",
2694 G_CALLBACK (menu_destroy_callback), cl_data);
2696 if (name)
2697 gtk_widget_set_name (wmenu, name);
2699 if (deactivate_cb)
2700 g_signal_connect (G_OBJECT (wmenu),
2701 "selection-done", deactivate_cb, 0);
2704 for (item = data; item; item = item->next)
2706 GtkWidget *w;
2708 if (pop_up_p && !item->contents && !item->call_data
2709 && !menu_separator_name_p (item->name))
2711 char *utf8_label;
2712 /* A title for a popup. We do the same as GTK does when
2713 creating titles, but it does not look good. */
2714 group = NULL;
2715 utf8_label = get_utf8_string (item->name);
2717 w = gtk_menu_item_new_with_label (utf8_label);
2718 gtk_widget_set_sensitive (w, FALSE);
2719 if (utf8_label) g_free (utf8_label);
2721 else if (menu_separator_name_p (item->name))
2723 group = NULL;
2724 /* GTK only have one separator type. */
2725 w = gtk_separator_menu_item_new ();
2727 else
2729 w = xg_create_one_menuitem (item,
2731 item->contents ? 0 : select_cb,
2732 highlight_cb,
2733 cl_data,
2734 &group);
2736 /* Create a possibly empty submenu for menu bar items, since some
2737 themes don't highlight items correctly without it. */
2738 if (item->contents || menu_bar_p)
2740 GtkWidget *submenu = create_menus (item->contents,
2742 select_cb,
2743 deactivate_cb,
2744 highlight_cb,
2748 cl_data,
2750 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2754 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2755 gtk_widget_set_name (w, MENU_ITEM_NAME);
2758 return wmenu;
2761 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2762 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2763 with some text and buttons.
2764 F is the frame the created item belongs to.
2765 NAME is the name to use for the top widget.
2766 VAL is a widget_value structure describing items to be created.
2767 SELECT_CB is the callback to use when a menu item is selected or
2768 a dialog button is pressed.
2769 DEACTIVATE_CB is the callback to use when an item is deactivated.
2770 For a menu, when a sub menu is not shown anymore, for a dialog it is
2771 called when the dialog is popped down.
2772 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2774 Returns the widget created. */
2776 GtkWidget *
2777 xg_create_widget (const char *type, const char *name, struct frame *f,
2778 widget_value *val, GCallback select_cb,
2779 GCallback deactivate_cb, GCallback highlight_cb)
2781 GtkWidget *w = 0;
2782 bool menu_bar_p = strcmp (type, "menubar") == 0;
2783 bool pop_up_p = strcmp (type, "popup") == 0;
2785 if (strcmp (type, "dialog") == 0)
2787 w = create_dialog (val, select_cb, deactivate_cb);
2788 xg_set_screen (w, f);
2789 gtk_window_set_transient_for (GTK_WINDOW (w),
2790 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2791 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2792 gtk_widget_set_name (w, "emacs-dialog");
2793 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2795 else if (menu_bar_p || pop_up_p)
2797 w = create_menus (val->contents,
2799 select_cb,
2800 deactivate_cb,
2801 highlight_cb,
2802 pop_up_p,
2803 menu_bar_p,
2806 name);
2808 /* Set the cursor to an arrow for popup menus when they are mapped.
2809 This is done by default for menu bar menus. */
2810 if (pop_up_p)
2812 /* Must realize so the GdkWindow inside the widget is created. */
2813 gtk_widget_realize (w);
2814 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
2817 else
2819 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2820 type);
2823 return w;
2826 /* Return the label for menu item WITEM. */
2828 static const char *
2829 xg_get_menu_item_label (GtkMenuItem *witem)
2831 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2832 return gtk_label_get_label (wlabel);
2835 /* Return true if the menu item WITEM has the text LABEL. */
2837 static bool
2838 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2840 bool is_same = 0;
2841 char *utf8_label = get_utf8_string (label);
2842 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2844 if (! old_label && ! utf8_label)
2845 is_same = 1;
2846 else if (old_label && utf8_label)
2847 is_same = strcmp (utf8_label, old_label) == 0;
2849 if (utf8_label) g_free (utf8_label);
2851 return is_same;
2854 /* Destroy widgets in LIST. */
2856 static void
2857 xg_destroy_widgets (GList *list)
2859 GList *iter;
2861 for (iter = list; iter; iter = g_list_next (iter))
2863 GtkWidget *w = GTK_WIDGET (iter->data);
2865 /* Destroying the widget will remove it from the container it is in. */
2866 gtk_widget_destroy (w);
2870 /* Update the top level names in MENUBAR (i.e. not submenus).
2871 F is the frame the menu bar belongs to.
2872 *LIST is a list with the current menu bar names (menu item widgets).
2873 ITER is the item within *LIST that shall be updated.
2874 POS is the numerical position, starting at 0, of ITER in *LIST.
2875 VAL describes what the menu bar shall look like after the update.
2876 SELECT_CB is the callback to use when a menu item is selected.
2877 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2878 CL_DATA points to the callback data to be used for this menu bar.
2880 This function calls itself to walk through the menu bar names. */
2882 static void
2883 xg_update_menubar (GtkWidget *menubar,
2884 struct frame *f,
2885 GList **list,
2886 GList *iter,
2887 int pos,
2888 widget_value *val,
2889 GCallback select_cb,
2890 GCallback deactivate_cb,
2891 GCallback highlight_cb,
2892 xg_menu_cb_data *cl_data)
2894 if (! iter && ! val)
2895 return;
2896 else if (iter && ! val)
2898 /* Item(s) have been removed. Remove all remaining items. */
2899 xg_destroy_widgets (iter);
2901 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2902 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2903 gtk_menu_item_new_with_label (""),
2905 /* All updated. */
2906 val = 0;
2907 iter = 0;
2909 else if (! iter && val)
2911 /* Item(s) added. Add all new items in one call. */
2912 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2913 0, 1, menubar, cl_data, 0);
2915 /* All updated. */
2916 val = 0;
2917 iter = 0;
2919 /* Below this neither iter or val is NULL */
2920 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2922 /* This item is still the same, check next item. */
2923 val = val->next;
2924 iter = g_list_next (iter);
2925 ++pos;
2927 else /* This item is changed. */
2929 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2930 GtkMenuItem *witem2 = 0;
2931 bool val_in_menubar = 0;
2932 bool iter_in_new_menubar = 0;
2933 GList *iter2;
2934 widget_value *cur;
2936 /* See if the changed entry (val) is present later in the menu bar */
2937 for (iter2 = iter;
2938 iter2 && ! val_in_menubar;
2939 iter2 = g_list_next (iter2))
2941 witem2 = GTK_MENU_ITEM (iter2->data);
2942 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2945 /* See if the current entry (iter) is present later in the
2946 specification for the new menu bar. */
2947 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2948 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2950 if (val_in_menubar && ! iter_in_new_menubar)
2952 int nr = pos;
2954 /* This corresponds to:
2955 Current: A B C
2956 New: A C
2957 Remove B. */
2959 g_object_ref (G_OBJECT (witem));
2960 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2961 gtk_widget_destroy (GTK_WIDGET (witem));
2963 /* Must get new list since the old changed. */
2964 g_list_free (*list);
2965 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2966 while (nr-- > 0) iter = g_list_next (iter);
2968 else if (! val_in_menubar && ! iter_in_new_menubar)
2970 /* This corresponds to:
2971 Current: A B C
2972 New: A X C
2973 Rename B to X. This might seem to be a strange thing to do,
2974 since if there is a menu under B it will be totally wrong for X.
2975 But consider editing a C file. Then there is a C-mode menu
2976 (corresponds to B above).
2977 If then doing C-x C-f the minibuf menu (X above) replaces the
2978 C-mode menu. When returning from the minibuffer, we get
2979 back the C-mode menu. Thus we do:
2980 Rename B to X (C-mode to minibuf menu)
2981 Rename X to B (minibuf to C-mode menu).
2982 If the X menu hasn't been invoked, the menu under B
2983 is up to date when leaving the minibuffer. */
2984 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2985 char *utf8_label = get_utf8_string (val->name);
2987 /* GTK menu items don't notice when their labels have been
2988 changed from underneath them, so we have to explicitly
2989 use g_object_notify to tell listeners (e.g., a GMenuModel
2990 bridge that might be loaded) that the item's label has
2991 changed. */
2992 gtk_label_set_text (wlabel, utf8_label);
2993 #if GTK_CHECK_VERSION (2, 16, 0)
2994 g_object_notify (G_OBJECT (witem), "label");
2995 #endif
2996 if (utf8_label) g_free (utf8_label);
2997 iter = g_list_next (iter);
2998 val = val->next;
2999 ++pos;
3001 else if (! val_in_menubar && iter_in_new_menubar)
3003 /* This corresponds to:
3004 Current: A B C
3005 New: A X B C
3006 Insert X. */
3008 int nr = pos;
3009 GSList *group = 0;
3010 GtkWidget *w = xg_create_one_menuitem (val,
3012 select_cb,
3013 highlight_cb,
3014 cl_data,
3015 &group);
3017 /* Create a possibly empty submenu for menu bar items, since some
3018 themes don't highlight items correctly without it. */
3019 GtkWidget *submenu = create_menus (NULL, f,
3020 select_cb, deactivate_cb,
3021 highlight_cb,
3022 0, 0, 0, cl_data, 0);
3024 gtk_widget_set_name (w, MENU_ITEM_NAME);
3025 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
3026 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
3028 g_list_free (*list);
3029 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
3030 while (nr-- > 0) iter = g_list_next (iter);
3031 iter = g_list_next (iter);
3032 val = val->next;
3033 ++pos;
3035 else /* if (val_in_menubar && iter_in_new_menubar) */
3037 int nr = pos;
3038 /* This corresponds to:
3039 Current: A B C
3040 New: A C B
3041 Move C before B */
3043 g_object_ref (G_OBJECT (witem2));
3044 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
3045 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
3046 GTK_WIDGET (witem2), pos);
3047 g_object_unref (G_OBJECT (witem2));
3049 g_list_free (*list);
3050 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
3051 while (nr-- > 0) iter = g_list_next (iter);
3052 if (iter) iter = g_list_next (iter);
3053 val = val->next;
3054 ++pos;
3058 /* Update the rest of the menu bar. */
3059 xg_update_menubar (menubar, f, list, iter, pos, val,
3060 select_cb, deactivate_cb, highlight_cb, cl_data);
3063 /* Update the menu item W so it corresponds to VAL.
3064 SELECT_CB is the callback to use when a menu item is selected.
3065 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3066 CL_DATA is the data to set in the widget for menu invocation. */
3068 static void
3069 xg_update_menu_item (widget_value *val,
3070 GtkWidget *w,
3071 GCallback select_cb,
3072 GCallback highlight_cb,
3073 xg_menu_cb_data *cl_data)
3075 GtkWidget *wchild;
3076 GtkLabel *wlbl = 0;
3077 GtkLabel *wkey = 0;
3078 char *utf8_label;
3079 char *utf8_key;
3080 const char *old_label = 0;
3081 const char *old_key = 0;
3082 xg_menu_item_cb_data *cb_data;
3083 bool label_changed = false;
3085 wchild = XG_BIN_CHILD (w);
3086 utf8_label = get_utf8_string (val->name);
3087 utf8_key = get_utf8_string (val->key);
3089 /* See if W is a menu item with a key. See make_menu_item above. */
3090 if (GTK_IS_BOX (wchild))
3092 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
3094 wlbl = GTK_LABEL (list->data);
3095 wkey = GTK_LABEL (list->next->data);
3096 g_list_free (list);
3098 if (! utf8_key)
3100 /* Remove the key and keep just the label. */
3101 g_object_ref (G_OBJECT (wlbl));
3102 gtk_container_remove (GTK_CONTAINER (w), wchild);
3103 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
3104 g_object_unref (G_OBJECT (wlbl));
3105 wkey = 0;
3109 else /* Just a label. */
3111 wlbl = GTK_LABEL (wchild);
3113 /* Check if there is now a key. */
3114 if (utf8_key)
3116 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3117 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
3119 wlbl = GTK_LABEL (list->data);
3120 wkey = GTK_LABEL (list->next->data);
3121 g_list_free (list);
3123 gtk_container_remove (GTK_CONTAINER (w), wchild);
3124 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3128 if (wkey) old_key = gtk_label_get_label (wkey);
3129 if (wlbl) old_label = gtk_label_get_label (wlbl);
3131 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3133 label_changed = true;
3134 gtk_label_set_text (wkey, utf8_key);
3137 if (! old_label || strcmp (utf8_label, old_label) != 0)
3139 label_changed = true;
3140 gtk_label_set_text (wlbl, utf8_label);
3143 if (utf8_key) g_free (utf8_key);
3144 if (utf8_label) g_free (utf8_label);
3146 if (! val->enabled && gtk_widget_get_sensitive (w))
3147 gtk_widget_set_sensitive (w, FALSE);
3148 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3149 gtk_widget_set_sensitive (w, TRUE);
3151 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
3152 if (cb_data)
3154 cb_data->call_data = val->call_data;
3155 cb_data->help = val->help;
3156 cb_data->cl_data = cl_data;
3158 /* We assume the callback functions don't change. */
3159 if (val->call_data && ! val->contents)
3161 /* This item shall have a select callback. */
3162 if (! cb_data->select_id)
3163 cb_data->select_id
3164 = g_signal_connect (G_OBJECT (w), "activate",
3165 select_cb, cb_data);
3167 else if (cb_data->select_id)
3169 g_signal_handler_disconnect (w, cb_data->select_id);
3170 cb_data->select_id = 0;
3174 #if GTK_CHECK_VERSION (2, 16, 0)
3175 if (label_changed) /* See comment in xg_update_menubar. */
3176 g_object_notify (G_OBJECT (w), "label");
3177 #endif
3180 /* Update the toggle menu item W so it corresponds to VAL. */
3182 static void
3183 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3185 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3188 /* Update the radio menu item W so it corresponds to VAL. */
3190 static void
3191 xg_update_radio_item (widget_value *val, GtkWidget *w)
3193 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3196 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3197 SUBMENU may be NULL, in that case a new menu is created.
3198 F is the frame the menu bar belongs to.
3199 VAL describes the contents of the menu bar.
3200 SELECT_CB is the callback to use when a menu item is selected.
3201 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3202 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3203 CL_DATA is the call back data to use for any newly created items.
3205 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3206 was NULL. */
3208 static GtkWidget *
3209 xg_update_submenu (GtkWidget *submenu,
3210 struct frame *f,
3211 widget_value *val,
3212 GCallback select_cb,
3213 GCallback deactivate_cb,
3214 GCallback highlight_cb,
3215 xg_menu_cb_data *cl_data)
3217 GtkWidget *newsub = submenu;
3218 GList *list = 0;
3219 GList *iter;
3220 widget_value *cur;
3221 GList *first_radio = 0;
3223 if (submenu)
3224 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3226 for (cur = val, iter = list;
3227 cur && iter;
3228 iter = g_list_next (iter), cur = cur->next)
3230 GtkWidget *w = GTK_WIDGET (iter->data);
3232 /* Remember first radio button in a group. If we get a mismatch in
3233 a radio group we must rebuild the whole group so that the connections
3234 in GTK becomes correct. */
3235 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3236 first_radio = iter;
3237 else if (cur->button_type != BUTTON_TYPE_RADIO
3238 && ! GTK_IS_RADIO_MENU_ITEM (w))
3239 first_radio = 0;
3241 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3243 if (! menu_separator_name_p (cur->name))
3244 break;
3246 else if (GTK_IS_CHECK_MENU_ITEM (w))
3248 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3249 break;
3250 xg_update_toggle_item (cur, w);
3251 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3253 else if (GTK_IS_RADIO_MENU_ITEM (w))
3255 if (cur->button_type != BUTTON_TYPE_RADIO)
3256 break;
3257 xg_update_radio_item (cur, w);
3258 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3260 else if (GTK_IS_MENU_ITEM (w))
3262 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3263 GtkWidget *sub;
3265 if (cur->button_type != BUTTON_TYPE_NONE ||
3266 menu_separator_name_p (cur->name))
3267 break;
3269 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3271 sub = gtk_menu_item_get_submenu (witem);
3272 if (sub && ! cur->contents)
3274 /* Not a submenu anymore. */
3275 g_object_ref (G_OBJECT (sub));
3276 remove_submenu (witem);
3277 gtk_widget_destroy (sub);
3279 else if (cur->contents)
3281 GtkWidget *nsub;
3283 nsub = xg_update_submenu (sub, f, cur->contents,
3284 select_cb, deactivate_cb,
3285 highlight_cb, cl_data);
3287 /* If this item just became a submenu, we must set it. */
3288 if (nsub != sub)
3289 gtk_menu_item_set_submenu (witem, nsub);
3292 else
3294 /* Structural difference. Remove everything from here and down
3295 in SUBMENU. */
3296 break;
3300 /* Remove widgets from first structural change. */
3301 if (iter)
3303 /* If we are adding new menu items below, we must remove from
3304 first radio button so that radio groups become correct. */
3305 if (cur && first_radio) xg_destroy_widgets (first_radio);
3306 else xg_destroy_widgets (iter);
3309 if (cur)
3311 /* More items added. Create them. */
3312 newsub = create_menus (cur,
3314 select_cb,
3315 deactivate_cb,
3316 highlight_cb,
3319 submenu,
3320 cl_data,
3324 if (list) g_list_free (list);
3326 return newsub;
3329 /* Update the MENUBAR.
3330 F is the frame the menu bar belongs to.
3331 VAL describes the contents of the menu bar.
3332 If DEEP_P, rebuild all but the top level menu names in
3333 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3334 SELECT_CB is the callback to use when a menu item is selected.
3335 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3336 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3338 void
3339 xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3340 widget_value *val, bool deep_p,
3341 GCallback select_cb, GCallback deactivate_cb,
3342 GCallback highlight_cb)
3344 xg_menu_cb_data *cl_data;
3345 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3347 if (! list) return;
3349 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
3351 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3352 select_cb, deactivate_cb, highlight_cb, cl_data);
3354 if (deep_p)
3356 widget_value *cur;
3358 /* Update all sub menus.
3359 We must keep the submenus (GTK menu item widgets) since the
3360 X Window in the XEvent that activates the menu are those widgets. */
3362 /* Update cl_data, menu_item things in F may have changed. */
3363 update_cl_data (cl_data, f, highlight_cb);
3365 for (cur = val->contents; cur; cur = cur->next)
3367 GList *iter;
3368 GtkWidget *sub = 0;
3369 GtkWidget *newsub;
3370 GtkMenuItem *witem = 0;
3372 /* Find sub menu that corresponds to val and update it. */
3373 for (iter = list ; iter; iter = g_list_next (iter))
3375 witem = GTK_MENU_ITEM (iter->data);
3376 if (xg_item_label_same_p (witem, cur->name))
3378 sub = gtk_menu_item_get_submenu (witem);
3379 break;
3383 newsub = xg_update_submenu (sub,
3385 cur->contents,
3386 select_cb,
3387 deactivate_cb,
3388 highlight_cb,
3389 cl_data);
3390 /* sub may still be NULL. If we just updated non deep and added
3391 a new menu bar item, it has no sub menu yet. So we set the
3392 newly created sub menu under witem. */
3393 if (newsub != sub && witem != 0)
3395 xg_set_screen (newsub, f);
3396 gtk_menu_item_set_submenu (witem, newsub);
3401 g_list_free (list);
3402 gtk_widget_show_all (menubar);
3405 /* Callback called when the menu bar W is mapped.
3406 Used to find the height of the menu bar if we didn't get it
3407 after showing the widget. */
3409 static void
3410 menubar_map_cb (GtkWidget *w, gpointer user_data)
3412 GtkRequisition req;
3413 struct frame *f = user_data;
3414 gtk_widget_get_preferred_size (w, NULL, &req);
3415 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3417 FRAME_MENUBAR_HEIGHT (f) = req.height;
3418 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3422 /* Recompute all the widgets of frame F, when the menu bar has been
3423 changed. */
3425 void
3426 xg_update_frame_menubar (struct frame *f)
3428 struct x_output *x = f->output_data.x;
3429 GtkRequisition req;
3431 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3432 return;
3434 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3435 return; /* Already done this, happens for frames created invisible. */
3437 block_input ();
3439 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3440 FALSE, FALSE, 0);
3441 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3443 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3444 gtk_widget_show_all (x->menubar_widget);
3445 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3447 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3449 FRAME_MENUBAR_HEIGHT (f) = req.height;
3450 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3452 unblock_input ();
3455 /* Get rid of the menu bar of frame F, and free its storage.
3456 This is used when deleting a frame, and when turning off the menu bar. */
3458 void
3459 free_frame_menubar (struct frame *f)
3461 struct x_output *x = f->output_data.x;
3463 if (x->menubar_widget)
3465 block_input ();
3467 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3468 /* The menubar and its children shall be deleted when removed from
3469 the container. */
3470 x->menubar_widget = 0;
3471 FRAME_MENUBAR_HEIGHT (f) = 0;
3472 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3473 unblock_input ();
3477 bool
3478 xg_event_is_for_menubar (struct frame *f, const XEvent *event)
3480 struct x_output *x = f->output_data.x;
3481 GList *iter;
3482 GdkRectangle rec;
3483 GList *list;
3484 GdkDisplay *gdpy;
3485 GdkWindow *gw;
3486 GdkEvent gevent;
3487 GtkWidget *gwdesc;
3489 if (! x->menubar_widget) return 0;
3491 if (! (event->xbutton.x >= 0
3492 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3493 && event->xbutton.y >= 0
3494 && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
3495 && event->xbutton.same_screen))
3496 return 0;
3498 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3499 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3500 if (! gw) return 0;
3501 gevent.any.window = gw;
3502 gevent.any.type = GDK_NOTHING;
3503 gwdesc = gtk_get_event_widget (&gevent);
3504 if (! gwdesc) return 0;
3505 if (! GTK_IS_MENU_BAR (gwdesc)
3506 && ! GTK_IS_MENU_ITEM (gwdesc)
3507 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3508 return 0;
3510 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3511 if (! list) return 0;
3512 rec.x = event->xbutton.x;
3513 rec.y = event->xbutton.y;
3514 rec.width = 1;
3515 rec.height = 1;
3517 for (iter = list ; iter; iter = g_list_next (iter))
3519 GtkWidget *w = GTK_WIDGET (iter->data);
3520 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3521 break;
3523 g_list_free (list);
3524 return iter != 0;
3529 /***********************************************************************
3530 Scroll bar functions
3531 ***********************************************************************/
3534 /* Setting scroll bar values invokes the callback. Use this variable
3535 to indicate that callback should do nothing. */
3537 bool xg_ignore_gtk_scrollbar;
3539 /* Width and height of scroll bars for the current theme. */
3540 static int scroll_bar_width_for_theme;
3541 static int scroll_bar_height_for_theme;
3543 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3544 may be larger than 32 bits. Keep a mapping from integer index to widget
3545 pointers to get around the 32 bit limitation. */
3547 static struct
3549 GtkWidget **widgets;
3550 ptrdiff_t max_size;
3551 ptrdiff_t used;
3552 } id_to_widget;
3554 /* Grow this much every time we need to allocate more */
3556 #define ID_TO_WIDGET_INCR 32
3558 /* Store the widget pointer W in id_to_widget and return the integer index. */
3560 static ptrdiff_t
3561 xg_store_widget_in_map (GtkWidget *w)
3563 ptrdiff_t i;
3565 if (id_to_widget.max_size == id_to_widget.used)
3567 ptrdiff_t new_size;
3568 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3569 memory_full (SIZE_MAX);
3571 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3572 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3573 new_size, sizeof (GtkWidget *));
3575 for (i = id_to_widget.max_size; i < new_size; ++i)
3576 id_to_widget.widgets[i] = 0;
3577 id_to_widget.max_size = new_size;
3580 /* Just loop over the array and find a free place. After all,
3581 how many scroll bars are we creating? Should be a small number.
3582 The check above guarantees we will find a free place. */
3583 for (i = 0; i < id_to_widget.max_size; ++i)
3585 if (! id_to_widget.widgets[i])
3587 id_to_widget.widgets[i] = w;
3588 ++id_to_widget.used;
3590 return i;
3594 /* Should never end up here */
3595 emacs_abort ();
3598 /* Remove pointer at IDX from id_to_widget.
3599 Called when scroll bar is destroyed. */
3601 static void
3602 xg_remove_widget_from_map (ptrdiff_t idx)
3604 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3606 id_to_widget.widgets[idx] = 0;
3607 --id_to_widget.used;
3611 /* Get the widget pointer at IDX from id_to_widget. */
3613 static GtkWidget *
3614 xg_get_widget_from_map (ptrdiff_t idx)
3616 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3617 return id_to_widget.widgets[idx];
3619 return 0;
3622 static void
3623 update_theme_scrollbar_width (void)
3625 #ifdef HAVE_GTK3
3626 GtkAdjustment *vadj;
3627 #else
3628 GtkObject *vadj;
3629 #endif
3630 GtkWidget *wscroll;
3631 int w = 0, b = 0;
3633 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3634 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3635 g_object_ref_sink (G_OBJECT (wscroll));
3636 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3637 gtk_widget_destroy (wscroll);
3638 g_object_unref (G_OBJECT (wscroll));
3639 w += 2*b;
3640 #ifndef HAVE_GTK3
3641 if (w < 16) w = 16;
3642 #endif
3643 scroll_bar_width_for_theme = w;
3646 static void
3647 update_theme_scrollbar_height (void)
3649 #ifdef HAVE_GTK3
3650 GtkAdjustment *hadj;
3651 #else
3652 GtkObject *hadj;
3653 #endif
3654 GtkWidget *wscroll;
3655 int w = 0, b = 0;
3657 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
3658 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3659 g_object_ref_sink (G_OBJECT (wscroll));
3660 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3661 gtk_widget_destroy (wscroll);
3662 g_object_unref (G_OBJECT (wscroll));
3663 w += 2*b;
3664 if (w < 12) w = 12;
3665 scroll_bar_height_for_theme = w;
3669 xg_get_default_scrollbar_width (struct frame *f)
3671 return scroll_bar_width_for_theme * xg_get_scale (f);
3675 xg_get_default_scrollbar_height (struct frame *f)
3677 /* Apparently there's no default height for themes. */
3678 return scroll_bar_width_for_theme * xg_get_scale (f);
3681 /* Return the scrollbar id for X Window WID on display DPY.
3682 Return -1 if WID not in id_to_widget. */
3684 ptrdiff_t
3685 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3687 ptrdiff_t idx;
3688 GtkWidget *w;
3690 w = xg_win_to_widget (dpy, wid);
3692 if (w)
3694 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3695 if (id_to_widget.widgets[idx] == w)
3696 return idx;
3699 return -1;
3702 /* Callback invoked when scroll bar WIDGET is destroyed.
3703 DATA is the index into id_to_widget for WIDGET.
3704 We free pointer to last scroll bar values here and remove the index. */
3706 static void
3707 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3709 intptr_t id = (intptr_t) data;
3710 xg_remove_widget_from_map (id);
3713 static void
3714 xg_finish_scroll_bar_creation (struct frame *f,
3715 GtkWidget *wscroll,
3716 struct scroll_bar *bar,
3717 GCallback scroll_callback,
3718 GCallback end_callback,
3719 const char *scroll_bar_name)
3721 GtkWidget *webox = gtk_event_box_new ();
3723 gtk_widget_set_name (wscroll, scroll_bar_name);
3724 #ifndef HAVE_GTK3
3725 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3726 #endif
3727 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3729 ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll);
3731 g_signal_connect (G_OBJECT (wscroll),
3732 "destroy",
3733 G_CALLBACK (xg_gtk_scroll_destroy),
3734 (gpointer) scroll_id);
3735 g_signal_connect (G_OBJECT (wscroll),
3736 "change-value",
3737 scroll_callback,
3738 (gpointer) bar);
3739 g_signal_connect (G_OBJECT (wscroll),
3740 "button-release-event",
3741 end_callback,
3742 (gpointer) bar);
3744 /* The scroll bar widget does not draw on a window of its own. Instead
3745 it draws on the parent window, in this case the edit widget. So
3746 whenever the edit widget is cleared, the scroll bar needs to redraw
3747 also, which causes flicker. Put an event box between the edit widget
3748 and the scroll bar, so the scroll bar instead draws itself on the
3749 event box window. */
3750 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3751 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3753 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
3755 /* N.B. The event box doesn't become a real X11 window until we ask
3756 for its XID via GTK_WIDGET_TO_X_WIN. If the event box is not a
3757 real X window, it and its scroll-bar child try to draw on the
3758 Emacs main window, which we draw over using Xlib. */
3759 gtk_widget_realize (webox);
3760 GTK_WIDGET_TO_X_WIN (webox);
3762 /* Set the cursor to an arrow. */
3763 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3765 bar->x_window = scroll_id;
3768 /* Create a scroll bar widget for frame F. Store the scroll bar
3769 in BAR.
3770 SCROLL_CALLBACK is the callback to invoke when the value of the
3771 bar changes.
3772 END_CALLBACK is the callback to invoke when scrolling ends.
3773 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3774 to set resources for the widget. */
3776 void
3777 xg_create_scroll_bar (struct frame *f,
3778 struct scroll_bar *bar,
3779 GCallback scroll_callback,
3780 GCallback end_callback,
3781 const char *scroll_bar_name)
3783 GtkWidget *wscroll;
3784 #ifdef HAVE_GTK3
3785 GtkAdjustment *vadj;
3786 #else
3787 GtkObject *vadj;
3788 #endif
3790 /* Page, step increment values are not so important here, they
3791 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3792 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3793 0.1, 0.1, 0.1);
3795 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3797 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3798 end_callback, scroll_bar_name);
3799 bar->horizontal = 0;
3802 /* Create a horizontal scroll bar widget for frame F. Store the scroll
3803 bar in BAR. SCROLL_CALLBACK is the callback to invoke when the value
3804 of the bar changes. END_CALLBACK is the callback to invoke when
3805 scrolling ends. SCROLL_BAR_NAME is the name we use for the scroll
3806 bar. Can be used to set resources for the widget. */
3808 void
3809 xg_create_horizontal_scroll_bar (struct frame *f,
3810 struct scroll_bar *bar,
3811 GCallback scroll_callback,
3812 GCallback end_callback,
3813 const char *scroll_bar_name)
3815 GtkWidget *wscroll;
3816 #ifdef HAVE_GTK3
3817 GtkAdjustment *hadj;
3818 #else
3819 GtkObject *hadj;
3820 #endif
3822 /* Page, step increment values are not so important here, they
3823 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3824 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
3825 0.1, 0.1, 0.1);
3827 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3829 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3830 end_callback, scroll_bar_name);
3831 bar->horizontal = 1;
3834 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3836 void
3837 xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
3839 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3840 if (w)
3842 GtkWidget *wparent = gtk_widget_get_parent (w);
3843 gtk_widget_destroy (w);
3844 gtk_widget_destroy (wparent);
3845 SET_FRAME_GARBAGED (f);
3849 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3850 in frame F.
3851 TOP/LEFT are the new pixel positions where the bar shall appear.
3852 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3854 void
3855 xg_update_scrollbar_pos (struct frame *f,
3856 ptrdiff_t scrollbar_id,
3857 int top,
3858 int left,
3859 int width,
3860 int height)
3862 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3863 if (wscroll)
3865 GtkWidget *wfixed = f->output_data.x->edit_widget;
3866 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3867 gint msl;
3868 int scale = xg_get_scale (f);
3870 top /= scale;
3871 left /= scale;
3872 height /= scale;
3873 left -= (scale - 1) * ((width / scale) >> 1);
3875 /* Clear out old position. */
3876 int oldx = -1, oldy = -1, oldw, oldh;
3877 if (gtk_widget_get_parent (wparent) == wfixed)
3879 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3880 "x", &oldx, "y", &oldy, NULL);
3881 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3884 /* Move and resize to new values. */
3885 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3886 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3887 bool hidden = height < msl;
3888 if (hidden)
3890 /* No room. Hide scroll bar as some themes output a warning if
3891 the height is less than the min size. */
3892 gtk_widget_hide (wparent);
3893 gtk_widget_hide (wscroll);
3895 else
3897 gtk_widget_show_all (wparent);
3898 gtk_widget_set_size_request (wscroll, width, height);
3900 if (oldx != -1 && oldw > 0 && oldh > 0)
3902 /* Clear under old scroll bar position. */
3903 oldw += (scale - 1) * oldw;
3904 oldx -= (scale - 1) * oldw;
3905 x_clear_area (f, oldx, oldy, oldw, oldh);
3908 if (!hidden)
3910 GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id);
3911 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
3913 /* Don't obscure any child frames. */
3914 XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
3917 /* GTK does not redraw until the main loop is entered again, but
3918 if there are no X events pending we will not enter it. So we sync
3919 here to get some events. */
3921 x_sync (f);
3922 SET_FRAME_GARBAGED (f);
3923 cancel_mouse_face (f);
3928 /* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
3929 in frame F.
3930 TOP/LEFT are the new pixel positions where the bar shall appear.
3931 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3933 void
3934 xg_update_horizontal_scrollbar_pos (struct frame *f,
3935 ptrdiff_t scrollbar_id,
3936 int top,
3937 int left,
3938 int width,
3939 int height)
3942 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3944 if (wscroll)
3946 GtkWidget *wfixed = f->output_data.x->edit_widget;
3947 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3948 gint msl;
3950 /* Clear out old position. */
3951 int oldx = -1, oldy = -1, oldw, oldh;
3952 if (gtk_widget_get_parent (wparent) == wfixed)
3954 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3955 "x", &oldx, "y", &oldy, NULL);
3956 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3959 /* Move and resize to new values. */
3960 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3961 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3962 if (msl > width)
3964 /* No room. Hide scroll bar as some themes output a warning if
3965 the width is less than the min size. */
3966 gtk_widget_hide (wparent);
3967 gtk_widget_hide (wscroll);
3969 else
3971 gtk_widget_show_all (wparent);
3972 gtk_widget_set_size_request (wscroll, width, height);
3974 if (oldx != -1 && oldw > 0 && oldh > 0)
3975 /* Clear under old scroll bar position. */
3976 x_clear_area (f, oldx, oldy, oldw, oldh);
3978 /* GTK does not redraw until the main loop is entered again, but
3979 if there are no X events pending we will not enter it. So we sync
3980 here to get some events. */
3983 GtkWidget *scrollbar =
3984 xg_get_widget_from_map (scrollbar_id);
3985 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
3987 /* Don't obscure any child frames. */
3988 XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
3991 x_sync (f);
3992 SET_FRAME_GARBAGED (f);
3993 cancel_mouse_face (f);
3998 /* Get the current value of the range, truncated to an integer. */
4000 static int
4001 int_gtk_range_get_value (GtkRange *range)
4003 return gtk_range_get_value (range);
4007 /* Set the thumb size and position of scroll bar BAR. We are currently
4008 displaying PORTION out of a whole WHOLE, and our position POSITION. */
4010 void
4011 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
4012 int portion,
4013 int position,
4014 int whole)
4016 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
4018 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
4020 if (wscroll && bar->dragging == -1)
4022 GtkAdjustment *adj;
4023 gdouble shown;
4024 gdouble top;
4025 int size, value;
4026 int old_size;
4027 int new_step;
4028 bool changed = 0;
4030 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4032 if (scroll_bar_adjust_thumb_portion_p)
4034 /* We do the same as for MOTIF in xterm.c, use 30 chars per
4035 line rather than the real portion value. This makes the
4036 thumb less likely to resize and that looks better. */
4037 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
4039 /* When the thumb is at the bottom, position == whole.
4040 So we need to increase `whole' to make space for the thumb. */
4041 whole += portion;
4044 if (whole <= 0)
4045 top = 0, shown = 1;
4046 else
4048 top = (gdouble) position / whole;
4049 shown = (gdouble) portion / whole;
4052 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
4053 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
4055 /* Assume all lines are of equal size. */
4056 new_step = size / max (1, FRAME_LINES (f));
4058 old_size = gtk_adjustment_get_page_size (adj);
4059 if (old_size != size)
4061 int old_step = gtk_adjustment_get_step_increment (adj);
4062 if (old_step != new_step)
4064 gtk_adjustment_set_page_size (adj, size);
4065 gtk_adjustment_set_step_increment (adj, new_step);
4066 /* Assume a page increment is about 95% of the page size */
4067 gtk_adjustment_set_page_increment (adj, size - size / 20);
4068 changed = 1;
4072 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
4074 block_input ();
4076 /* gtk_range_set_value invokes the callback. Set
4077 ignore_gtk_scrollbar to make the callback do nothing */
4078 xg_ignore_gtk_scrollbar = 1;
4080 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
4081 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
4082 else if (changed)
4083 gtk_adjustment_changed (adj);
4085 xg_ignore_gtk_scrollbar = 0;
4087 unblock_input ();
4092 /* Set the thumb size and position of horizontal scroll bar BAR. We are
4093 currently displaying PORTION out of a whole WHOLE, and our position
4094 POSITION. */
4095 void
4096 xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
4097 int portion,
4098 int position,
4099 int whole)
4101 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
4103 if (wscroll && bar->dragging == -1)
4105 GtkAdjustment *adj;
4106 int lower = 0;
4107 int upper = max (whole - 1, 0);
4108 int pagesize = min (upper, max (portion, 0));
4109 int value = max (0, min (position, upper - pagesize));
4110 /* These should be set to something more <portion, whole>
4111 related. */
4112 int page_increment = 4;
4113 int step_increment = 1;
4115 block_input ();
4116 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4117 gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
4118 (gdouble) upper, (gdouble) step_increment,
4119 (gdouble) page_increment, (gdouble) pagesize);
4120 gtk_adjustment_changed (adj);
4121 unblock_input ();
4125 /* Return true if EVENT is for a scroll bar in frame F.
4126 When the same X window is used for several Gtk+ widgets, we cannot
4127 say for sure based on the X window alone if an event is for the
4128 frame. This function does additional checks. */
4130 bool
4131 xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
4133 bool retval = 0;
4135 if (f && event->type == ButtonPress && event->xbutton.button < 4)
4137 /* Check if press occurred outside the edit widget. */
4138 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
4139 GdkWindow *gwin;
4140 #ifdef HAVE_GTK3
4141 GdkDevice *gdev = gdk_device_manager_get_client_pointer
4142 (gdk_display_get_device_manager (gdpy));
4143 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
4144 #else
4145 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
4146 #endif
4147 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
4149 else if (f
4150 && ((event->type == ButtonRelease && event->xbutton.button < 4)
4151 || event->type == MotionNotify))
4153 /* If we are releasing or moving the scroll bar, it has the grab. */
4154 GtkWidget *w = gtk_grab_get_current ();
4155 retval = w != 0 && GTK_IS_SCROLLBAR (w);
4158 return retval;
4162 /***********************************************************************
4163 Printing
4164 ***********************************************************************/
4165 #ifdef USE_CAIRO
4166 static GtkPrintSettings *print_settings = NULL;
4167 static GtkPageSetup *page_setup = NULL;
4169 void
4170 xg_page_setup_dialog (void)
4172 GtkPageSetup *new_page_setup = NULL;
4174 if (print_settings == NULL)
4175 print_settings = gtk_print_settings_new ();
4176 new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
4177 print_settings);
4178 if (page_setup)
4179 g_object_unref (page_setup);
4180 page_setup = new_page_setup;
4183 Lisp_Object
4184 xg_get_page_setup (void)
4186 Lisp_Object orientation_symbol;
4188 if (page_setup == NULL)
4189 page_setup = gtk_page_setup_new ();
4191 switch (gtk_page_setup_get_orientation (page_setup))
4193 case GTK_PAGE_ORIENTATION_PORTRAIT:
4194 orientation_symbol = Qportrait;
4195 break;
4196 case GTK_PAGE_ORIENTATION_LANDSCAPE:
4197 orientation_symbol = Qlandscape;
4198 break;
4199 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4200 orientation_symbol = Qreverse_portrait;
4201 break;
4202 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4203 orientation_symbol = Qreverse_landscape;
4204 break;
4205 default:
4206 eassume (false);
4209 return listn (CONSTYPE_HEAP, 7,
4210 Fcons (Qorientation, orientation_symbol),
4211 #define MAKE_FLOAT_PAGE_SETUP(f) make_float (f (page_setup, GTK_UNIT_POINTS))
4212 Fcons (Qwidth,
4213 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_width)),
4214 Fcons (Qheight,
4215 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_height)),
4216 Fcons (Qleft_margin,
4217 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_left_margin)),
4218 Fcons (Qright_margin,
4219 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_right_margin)),
4220 Fcons (Qtop_margin,
4221 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_top_margin)),
4222 Fcons (Qbottom_margin,
4223 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_bottom_margin))
4224 #undef MAKE_FLOAT_PAGE_SETUP
4228 static void
4229 draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
4230 gint page_nr, gpointer user_data)
4232 Lisp_Object frames = *((Lisp_Object *) user_data);
4233 struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
4234 cairo_t *cr = gtk_print_context_get_cairo_context (context);
4236 x_cr_draw_frame (cr, f);
4239 void
4240 xg_print_frames_dialog (Lisp_Object frames)
4242 GtkPrintOperation *print;
4243 GtkPrintOperationResult res;
4245 print = gtk_print_operation_new ();
4246 if (print_settings != NULL)
4247 gtk_print_operation_set_print_settings (print, print_settings);
4248 if (page_setup != NULL)
4249 gtk_print_operation_set_default_page_setup (print, page_setup);
4250 gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
4251 g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
4252 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
4253 NULL, NULL);
4254 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
4256 if (print_settings != NULL)
4257 g_object_unref (print_settings);
4258 print_settings =
4259 g_object_ref (gtk_print_operation_get_print_settings (print));
4261 g_object_unref (print);
4264 #endif /* USE_CAIRO */
4268 /***********************************************************************
4269 Tool bar functions
4270 ***********************************************************************/
4271 /* The key for the data we put in the GtkImage widgets. The data is
4272 the image used by Emacs. We use this to see if we need to update
4273 the GtkImage with a new image. */
4274 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
4276 /* The key for storing the latest modifiers so the activate callback can
4277 get them. */
4278 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
4280 /* The key for the data we put in the GtkImage widgets. The data is
4281 the stock name used by Emacs. We use this to see if we need to update
4282 the GtkImage with a new image. */
4283 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
4285 /* As above, but this is used for named theme widgets, as opposed to
4286 stock items. */
4287 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
4289 /* Callback function invoked when a tool bar item is pressed.
4290 W is the button widget in the tool bar that got pressed,
4291 CLIENT_DATA is an integer that is the index of the button in the
4292 tool bar. 0 is the first button. */
4294 static gboolean
4295 xg_tool_bar_button_cb (GtkWidget *widget,
4296 GdkEventButton *event,
4297 gpointer user_data)
4299 intptr_t state = event->state;
4300 gpointer ptr = (gpointer) state;
4301 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
4302 return FALSE;
4306 /* Callback function invoked when a tool bar item is pressed.
4307 W is the button widget in the tool bar that got pressed,
4308 CLIENT_DATA is an integer that is the index of the button in the
4309 tool bar. 0 is the first button. */
4311 static void
4312 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
4314 intptr_t idx = (intptr_t) client_data;
4315 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
4316 intptr_t mod = (intptr_t) gmod;
4318 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4319 Lisp_Object key, frame;
4320 struct input_event event;
4321 EVENT_INIT (event);
4323 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4324 return;
4326 idx *= TOOL_BAR_ITEM_NSLOTS;
4328 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
4329 XSETFRAME (frame, f);
4331 /* We generate two events here. The first one is to set the prefix
4332 to `(tool_bar)', see keyboard.c. */
4333 event.kind = TOOL_BAR_EVENT;
4334 event.frame_or_window = frame;
4335 event.arg = frame;
4336 kbd_buffer_store_event (&event);
4338 event.kind = TOOL_BAR_EVENT;
4339 event.frame_or_window = frame;
4340 event.arg = key;
4341 /* Convert between the modifier bits GDK uses and the modifier bits
4342 Emacs uses. This assumes GDK and X masks are the same, which they are when
4343 this is written. */
4344 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
4345 kbd_buffer_store_event (&event);
4347 /* Return focus to the frame after we have clicked on a detached
4348 tool bar button. */
4349 x_focus_frame (f, false);
4352 static GtkWidget *
4353 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4355 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
4356 GtkWidget *c1 = clist->data;
4357 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
4359 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4360 g_list_free (clist);
4361 return GTK_IS_LABEL (c1) ? c1 : c2;
4365 /* This callback is called when the mouse enters or leaves a tool bar item.
4366 It is used for displaying and hiding the help text.
4367 W is the tool bar item, a button.
4368 EVENT is either an enter event or leave event.
4369 CLIENT_DATA is an integer that is the index of the button in the
4370 tool bar. 0 is the first button.
4372 Returns FALSE to tell GTK to keep processing this event. */
4374 static gboolean
4375 xg_tool_bar_help_callback (GtkWidget *w,
4376 GdkEventCrossing *event,
4377 gpointer client_data)
4379 intptr_t idx = (intptr_t) client_data;
4380 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4381 Lisp_Object help, frame;
4383 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4384 return FALSE;
4386 if (event->type == GDK_ENTER_NOTIFY)
4388 idx *= TOOL_BAR_ITEM_NSLOTS;
4389 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4391 if (NILP (help))
4392 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4394 else
4395 help = Qnil;
4397 XSETFRAME (frame, f);
4398 kbd_buffer_store_help_event (frame, help);
4400 return FALSE;
4404 /* This callback is called when a tool bar item shall be redrawn.
4405 It modifies the expose event so that the GtkImage widget redraws the
4406 whole image. This to overcome a bug that makes GtkImage draw the image
4407 in the wrong place when it tries to redraw just a part of the image.
4408 W is the GtkImage to be redrawn.
4409 EVENT is the expose event for W.
4410 CLIENT_DATA is unused.
4412 Returns FALSE to tell GTK to keep processing this event. */
4414 #ifndef HAVE_GTK3
4415 static gboolean
4416 xg_tool_bar_item_expose_callback (GtkWidget *w,
4417 GdkEventExpose *event,
4418 gpointer client_data)
4420 gint width, height;
4422 gdk_drawable_get_size (event->window, &width, &height);
4423 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4424 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4426 event->area.x = max (0, event->area.x);
4427 event->area.y = max (0, event->area.y);
4429 event->area.width = max (width, event->area.width);
4430 event->area.height = max (height, event->area.height);
4432 return FALSE;
4434 #endif
4436 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4437 #define toolbar_set_orientation(w, o) \
4438 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4439 #else
4440 #define toolbar_set_orientation(w, o) \
4441 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4442 #endif
4444 /* Attach a tool bar to frame F. */
4446 static void
4447 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
4449 struct x_output *x = f->output_data.x;
4450 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4451 GtkWidget *top_widget = x->toolbar_widget;
4453 toolbar_set_orientation (x->toolbar_widget,
4454 into_hbox
4455 ? GTK_ORIENTATION_VERTICAL
4456 : GTK_ORIENTATION_HORIZONTAL);
4458 if (into_hbox)
4460 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
4461 FALSE, FALSE, 0);
4463 if (EQ (pos, Qleft))
4464 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4465 top_widget,
4467 x->toolbar_in_hbox = true;
4469 else
4471 bool vbox_pos = x->menubar_widget != 0;
4472 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
4473 FALSE, FALSE, 0);
4475 if (EQ (pos, Qtop))
4476 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4477 top_widget,
4478 vbox_pos);
4479 x->toolbar_in_hbox = false;
4481 x->toolbar_is_packed = true;
4484 static bool xg_update_tool_bar_sizes (struct frame *f);
4486 static void
4487 tb_size_cb (GtkWidget *widget,
4488 GdkRectangle *allocation,
4489 gpointer user_data)
4491 /* When tool bar is created it has one preferred size. But when size is
4492 allocated between widgets, it may get another. So we must update
4493 size hints if tool bar size changes. Seen on Fedora 18 at least. */
4494 struct frame *f = user_data;
4496 if (xg_update_tool_bar_sizes (f))
4498 frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
4499 adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
4503 /* Create a tool bar for frame F. */
4505 static void
4506 xg_create_tool_bar (struct frame *f)
4508 struct x_output *x = f->output_data.x;
4509 #if GTK_CHECK_VERSION (3, 3, 6)
4510 GtkStyleContext *gsty;
4511 #endif
4512 struct xg_frame_tb_info *tbinfo
4513 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4514 TB_INFO_KEY);
4515 if (! tbinfo)
4517 tbinfo = xmalloc (sizeof (*tbinfo));
4518 tbinfo->last_tool_bar = Qnil;
4519 tbinfo->style = Qnil;
4520 tbinfo->hmargin = tbinfo->vmargin = 0;
4521 tbinfo->dir = GTK_TEXT_DIR_NONE;
4522 tbinfo->n_last_items = 0;
4523 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4524 TB_INFO_KEY,
4525 tbinfo);
4528 x->toolbar_widget = gtk_toolbar_new ();
4530 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4532 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4533 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4534 g_signal_connect (x->toolbar_widget, "size-allocate",
4535 G_CALLBACK (tb_size_cb), f);
4536 #if GTK_CHECK_VERSION (3, 3, 6)
4537 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4538 gtk_style_context_add_class (gsty, "primary-toolbar");
4539 #endif
4543 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4545 /* Find the right-to-left image named by RTL in the tool bar images for F.
4546 Returns IMAGE if RTL is not found. */
4548 static Lisp_Object
4549 find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
4551 int i;
4552 Lisp_Object file, rtl_name;
4554 rtl_name = Ffile_name_nondirectory (rtl);
4556 for (i = 0; i < f->n_tool_bar_items; ++i)
4558 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4559 if (!NILP (file = file_for_image (rtl_image)))
4561 file = call1 (intern ("file-name-sans-extension"),
4562 Ffile_name_nondirectory (file));
4563 if (! NILP (Fequal (file, rtl_name)))
4565 image = rtl_image;
4566 break;
4571 return image;
4574 static GtkToolItem *
4575 xg_make_tool_item (struct frame *f,
4576 GtkWidget *wimage,
4577 GtkWidget **wbutton,
4578 const char *label,
4579 int i, bool horiz, bool text_image)
4581 GtkToolItem *ti = gtk_tool_item_new ();
4582 GtkWidget *vb = gtk_box_new (horiz
4583 ? GTK_ORIENTATION_HORIZONTAL
4584 : GTK_ORIENTATION_VERTICAL,
4586 GtkWidget *wb = gtk_button_new ();
4587 /* The eventbox is here so we can have tooltips on disabled items. */
4588 GtkWidget *weventbox = gtk_event_box_new ();
4589 #if GTK_CHECK_VERSION (3, 3, 6)
4590 GtkCssProvider *css_prov = gtk_css_provider_new ();
4591 GtkStyleContext *gsty;
4593 gtk_css_provider_load_from_data (css_prov,
4594 "GtkEventBox {"
4595 " background-color: transparent;"
4596 "}",
4597 -1, NULL);
4599 gsty = gtk_widget_get_style_context (weventbox);
4600 gtk_style_context_add_provider (gsty,
4601 GTK_STYLE_PROVIDER (css_prov),
4602 GTK_STYLE_PROVIDER_PRIORITY_USER);
4603 g_object_unref (css_prov);
4604 #endif
4606 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4608 if (wimage && !text_image)
4609 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4610 if (label)
4611 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4612 if (wimage && text_image)
4613 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4615 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4616 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4617 gtk_container_add (GTK_CONTAINER (wb), vb);
4618 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4619 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4621 if (wimage || label)
4623 intptr_t ii = i;
4624 gpointer gi = (gpointer) ii;
4626 g_signal_connect (G_OBJECT (wb), "clicked",
4627 G_CALLBACK (xg_tool_bar_callback),
4628 gi);
4630 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4632 #ifndef HAVE_GTK3
4633 /* Catch expose events to overcome an annoying redraw bug, see
4634 comment for xg_tool_bar_item_expose_callback. */
4635 g_signal_connect (G_OBJECT (ti),
4636 "expose-event",
4637 G_CALLBACK (xg_tool_bar_item_expose_callback),
4639 #endif
4640 gtk_tool_item_set_homogeneous (ti, FALSE);
4642 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4643 no distinction based on modifiers in the activate callback,
4644 so we have to do it ourselves. */
4645 g_signal_connect (wb, "button-release-event",
4646 G_CALLBACK (xg_tool_bar_button_cb),
4647 NULL);
4649 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4651 /* Use enter/leave notify to show help. We use the events
4652 rather than the GtkButton specific signals "enter" and
4653 "leave", so we can have only one callback. The event
4654 will tell us what kind of event it is. */
4655 g_signal_connect (G_OBJECT (weventbox),
4656 "enter-notify-event",
4657 G_CALLBACK (xg_tool_bar_help_callback),
4658 gi);
4659 g_signal_connect (G_OBJECT (weventbox),
4660 "leave-notify-event",
4661 G_CALLBACK (xg_tool_bar_help_callback),
4662 gi);
4665 if (wbutton) *wbutton = wb;
4667 return ti;
4670 static bool
4671 is_box_type (GtkWidget *vb, bool is_horizontal)
4673 #ifdef HAVE_GTK3
4674 bool ret = 0;
4675 if (GTK_IS_BOX (vb))
4677 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4678 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4679 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4681 return ret;
4682 #else
4683 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4684 #endif
4688 static bool
4689 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4690 const char *icon_name, const struct image *img,
4691 const char *label, bool horiz)
4693 gpointer old;
4694 GtkWidget *wimage;
4695 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4696 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4698 /* Check if the tool icon matches. */
4699 if (stock_name && wimage)
4701 old = g_object_get_data (G_OBJECT (wimage),
4702 XG_TOOL_BAR_STOCK_NAME);
4703 if (!old || strcmp (old, stock_name))
4704 return 1;
4706 else if (icon_name && wimage)
4708 old = g_object_get_data (G_OBJECT (wimage),
4709 XG_TOOL_BAR_ICON_NAME);
4710 if (!old || strcmp (old, icon_name))
4711 return 1;
4713 else if (wimage)
4715 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4716 XG_TOOL_BAR_IMAGE_DATA);
4717 Pixmap old_img = (Pixmap) gold_img;
4718 if (old_img != img->pixmap)
4719 return 1;
4722 /* Check button configuration and label. */
4723 if (is_box_type (vb, horiz)
4724 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4725 return 1;
4727 /* Ensure label is correct. */
4728 if (label && wlbl)
4729 gtk_label_set_text (GTK_LABEL (wlbl), label);
4730 return 0;
4733 static bool
4734 xg_update_tool_bar_sizes (struct frame *f)
4736 struct x_output *x = f->output_data.x;
4737 GtkRequisition req;
4738 int nl = 0, nr = 0, nt = 0, nb = 0;
4739 GtkWidget *top_widget = x->toolbar_widget;
4741 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
4742 if (x->toolbar_in_hbox)
4744 int pos;
4745 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4746 top_widget,
4747 "position", &pos, NULL);
4748 if (pos == 0) nl = req.width;
4749 else nr = req.width;
4751 else
4753 int pos;
4754 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4755 top_widget,
4756 "position", &pos, NULL);
4757 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4758 else nb = req.height;
4761 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4762 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4763 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4764 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4766 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4767 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4768 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4769 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4770 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4771 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4773 return true;
4775 else
4776 return false;
4779 static char *
4780 find_icon_from_name (char *name,
4781 GtkIconTheme *icon_theme,
4782 char **icon_name)
4784 #if ! GTK_CHECK_VERSION (3, 10, 0)
4785 GtkStockItem stock_item;
4786 #endif
4788 if (name[0] == 'n' && name[1] == ':')
4790 *icon_name = name + 2;
4791 name = NULL;
4793 if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
4794 *icon_name = NULL;
4797 #if ! GTK_CHECK_VERSION (3, 10, 0)
4798 else if (gtk_stock_lookup (name, &stock_item))
4799 *icon_name = NULL;
4800 #endif
4801 else if (gtk_icon_theme_has_icon (icon_theme, name))
4803 *icon_name = name;
4804 name = NULL;
4806 else
4808 name = NULL;
4809 *icon_name = NULL;
4812 return name;
4816 /* Update the tool bar for frame F. Add new buttons and remove old. */
4818 void
4819 update_frame_tool_bar (struct frame *f)
4821 int i, j;
4822 struct x_output *x = f->output_data.x;
4823 int hmargin = 0, vmargin = 0;
4824 GtkToolbar *wtoolbar;
4825 GtkToolItem *ti;
4826 GtkTextDirection dir;
4827 Lisp_Object style;
4828 bool text_image, horiz;
4829 struct xg_frame_tb_info *tbinfo;
4830 GdkScreen *screen;
4831 GtkIconTheme *icon_theme;
4834 if (! FRAME_GTK_WIDGET (f))
4835 return;
4837 block_input ();
4839 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4841 hmargin = XFASTINT (Vtool_bar_button_margin);
4842 vmargin = XFASTINT (Vtool_bar_button_margin);
4844 else if (CONSP (Vtool_bar_button_margin))
4846 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4847 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4849 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4850 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4853 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4854 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4855 i.e. zero. This means that margins less than
4856 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4857 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4858 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4860 if (! x->toolbar_widget)
4861 xg_create_tool_bar (f);
4863 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4864 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4866 style = Ftool_bar_get_system_style ();
4867 screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4868 icon_theme = gtk_icon_theme_get_for_screen (screen);
4870 /* Are we up to date? */
4871 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4872 TB_INFO_KEY);
4874 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4875 && tbinfo->n_last_items == f->n_tool_bar_items
4876 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4877 && tbinfo->dir == dir
4878 && ! NILP (Fequal (tbinfo->style, style))
4879 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4881 unblock_input ();
4882 return;
4885 tbinfo->last_tool_bar = f->tool_bar_items;
4886 tbinfo->n_last_items = f->n_tool_bar_items;
4887 tbinfo->style = style;
4888 tbinfo->hmargin = hmargin;
4889 tbinfo->vmargin = vmargin;
4890 tbinfo->dir = dir;
4892 text_image = EQ (style, Qtext_image_horiz);
4893 horiz = EQ (style, Qboth_horiz) || text_image;
4895 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4897 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4898 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4899 int idx;
4900 ptrdiff_t img_id;
4901 int icon_size = 0;
4902 struct image *img = NULL;
4903 Lisp_Object image;
4904 Lisp_Object stock = Qnil;
4905 char *stock_name = NULL;
4906 char *icon_name = NULL;
4907 Lisp_Object rtl;
4908 GtkWidget *wbutton = NULL;
4909 Lisp_Object specified_file;
4910 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4911 const char *label
4912 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4913 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4914 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4915 : "";
4917 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4919 /* If this is a separator, use a gtk separator item. */
4920 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4922 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4924 if (ti)
4925 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4926 GTK_WIDGET (ti));
4927 ti = gtk_separator_tool_item_new ();
4928 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4930 j++;
4931 continue;
4934 /* Otherwise, the tool-bar item is an ordinary button. */
4936 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4938 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4939 ti = NULL;
4942 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4944 /* Ignore invalid image specifications. */
4945 image = PROP (TOOL_BAR_ITEM_IMAGES);
4946 if (!valid_image_p (image))
4948 if (ti)
4949 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4950 GTK_WIDGET (ti));
4951 continue;
4954 specified_file = file_for_image (image);
4955 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4956 stock = call1 (Qx_gtk_map_stock, specified_file);
4958 if (CONSP (stock))
4960 Lisp_Object tem;
4961 for (tem = stock; CONSP (tem); tem = XCDR (tem))
4962 if (! NILP (tem) && STRINGP (XCAR (tem)))
4964 stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
4965 icon_theme,
4966 &icon_name);
4967 if (stock_name || icon_name) break;
4970 else if (STRINGP (stock))
4972 stock_name = find_icon_from_name (SSDATA (stock),
4973 icon_theme,
4974 &icon_name);
4977 if (stock_name || icon_name)
4978 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4980 if (stock_name == NULL && icon_name == NULL)
4982 /* No stock image, or stock item not known. Try regular
4983 image. If image is a vector, choose it according to the
4984 button state. */
4985 if (dir == GTK_TEXT_DIR_RTL
4986 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4987 && STRINGP (rtl))
4988 image = find_rtl_image (f, image, rtl);
4990 if (VECTORP (image))
4992 if (enabled_p)
4993 idx = (selected_p
4994 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4995 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4996 else
4997 idx = (selected_p
4998 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4999 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
5001 eassert (ASIZE (image) >= idx);
5002 image = AREF (image, idx);
5004 else
5005 idx = -1;
5007 img_id = lookup_image (f, image);
5008 img = IMAGE_FROM_ID (f, img_id);
5009 prepare_image_for_display (f, img);
5011 if (img->load_failed_p || img->pixmap == None)
5013 if (ti)
5014 gtk_container_remove (GTK_CONTAINER (wtoolbar),
5015 GTK_WIDGET (ti));
5016 continue;
5020 /* If there is an existing widget, check if it's stale; if so,
5021 remove it and make a new tool item from scratch. */
5022 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
5023 img, label, horiz))
5025 gtk_container_remove (GTK_CONTAINER (wtoolbar),
5026 GTK_WIDGET (ti));
5027 ti = NULL;
5030 if (ti == NULL)
5032 GtkWidget *w;
5034 /* Save the image so we can see if an update is needed the
5035 next time we call xg_tool_item_match_p. */
5036 if (EQ (style, Qtext))
5037 w = NULL;
5038 else if (stock_name)
5041 #if GTK_CHECK_VERSION (3, 10, 0)
5042 w = gtk_image_new_from_icon_name (stock_name, icon_size);
5043 #else
5044 w = gtk_image_new_from_stock (stock_name, icon_size);
5045 #endif
5046 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
5047 (gpointer) xstrdup (stock_name),
5048 (GDestroyNotify) xfree);
5050 else if (icon_name)
5052 w = gtk_image_new_from_icon_name (icon_name, icon_size);
5053 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
5054 (gpointer) xstrdup (icon_name),
5055 (GDestroyNotify) xfree);
5057 else
5059 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
5060 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
5061 (gpointer)img->pixmap);
5064 #if GTK_CHECK_VERSION (3, 14, 0)
5065 if (w)
5067 gtk_widget_set_margin_start (w, hmargin);
5068 gtk_widget_set_margin_end (w, hmargin);
5069 gtk_widget_set_margin_top (w, vmargin);
5070 gtk_widget_set_margin_bottom (w, vmargin);
5072 #else
5073 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
5074 #endif
5075 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
5076 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
5079 #undef PROP
5081 gtk_widget_set_sensitive (wbutton, enabled_p);
5082 j++;
5085 /* Remove buttons not longer needed. */
5088 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
5089 if (ti)
5090 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
5091 } while (ti != NULL);
5093 if (f->n_tool_bar_items != 0)
5095 if (! x->toolbar_is_packed)
5096 xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
5097 gtk_widget_show_all (x->toolbar_widget);
5098 if (xg_update_tool_bar_sizes (f))
5100 int inhibit
5101 = ((f->after_make_frame
5102 && !f->tool_bar_resized
5103 && (EQ (frame_inhibit_implied_resize, Qt)
5104 || (CONSP (frame_inhibit_implied_resize)
5105 && !NILP (Fmemq (Qtool_bar_lines,
5106 frame_inhibit_implied_resize))))
5107 /* This will probably fail to DTRT in the
5108 fullheight/-width cases. */
5109 && NILP (get_frame_param (f, Qfullscreen)))
5111 : 2);
5113 frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
5114 adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
5116 f->tool_bar_resized = f->tool_bar_redisplayed;
5119 unblock_input ();
5122 /* Deallocate all resources for the tool bar on frame F.
5123 Remove the tool bar. */
5125 void
5126 free_frame_tool_bar (struct frame *f)
5128 struct x_output *x = f->output_data.x;
5130 if (x->toolbar_widget)
5132 struct xg_frame_tb_info *tbinfo;
5133 GtkWidget *top_widget = x->toolbar_widget;
5135 block_input ();
5136 if (x->toolbar_is_packed)
5138 if (x->toolbar_in_hbox)
5139 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5140 top_widget);
5141 else
5142 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5143 top_widget);
5145 else
5146 gtk_widget_destroy (x->toolbar_widget);
5148 x->toolbar_widget = 0;
5149 x->toolbar_widget = 0;
5150 x->toolbar_is_packed = false;
5151 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
5152 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
5154 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5155 TB_INFO_KEY);
5156 if (tbinfo)
5158 xfree (tbinfo);
5159 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5160 TB_INFO_KEY,
5161 NULL);
5164 frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
5165 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5167 unblock_input ();
5171 void
5172 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
5174 struct x_output *x = f->output_data.x;
5175 GtkWidget *top_widget = x->toolbar_widget;
5177 if (! x->toolbar_widget || ! top_widget)
5178 return;
5180 block_input ();
5181 g_object_ref (top_widget);
5182 if (x->toolbar_is_packed)
5184 if (x->toolbar_in_hbox)
5185 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5186 top_widget);
5187 else
5188 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5189 top_widget);
5192 xg_pack_tool_bar (f, pos);
5193 g_object_unref (top_widget);
5195 if (xg_update_tool_bar_sizes (f))
5197 frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
5198 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5202 unblock_input ();
5207 /***********************************************************************
5208 Initializing
5209 ***********************************************************************/
5210 void
5211 xg_initialize (void)
5213 GtkBindingSet *binding_set;
5214 GtkSettings *settings;
5216 #if HAVE_XFT
5217 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5218 we keep it permanently linked in. */
5219 XftInit (0);
5220 #endif
5222 gdpy_def = NULL;
5223 xg_ignore_gtk_scrollbar = 0;
5224 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5225 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5227 id_to_widget.max_size = id_to_widget.used = 0;
5228 id_to_widget.widgets = 0;
5230 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5231 (gdk_display_get_default ()));
5232 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5233 bindings. It doesn't seem to be any way to remove properties,
5234 so we set it to "" which in means "no key". */
5235 gtk_settings_set_string_property (settings,
5236 "gtk-menu-bar-accel",
5238 EMACS_CLASS);
5240 /* Make GTK text input widgets use Emacs style keybindings. This is
5241 Emacs after all. */
5242 gtk_settings_set_string_property (settings,
5243 "gtk-key-theme-name",
5244 "Emacs",
5245 EMACS_CLASS);
5247 /* Make dialogs close on C-g. Since file dialog inherits from
5248 dialog, this works for them also. */
5249 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
5250 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5251 "close", 0);
5253 /* Make menus close on C-g. */
5254 binding_set = gtk_binding_set_by_class (g_type_class_ref
5255 (GTK_TYPE_MENU_SHELL));
5256 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5257 "cancel", 0);
5258 update_theme_scrollbar_width ();
5259 update_theme_scrollbar_height ();
5261 #ifdef HAVE_FREETYPE
5262 x_last_font_name = NULL;
5263 #endif
5266 #endif /* USE_GTK */