* lisp/emacs-lisp/cl-generic.el: Accomodate future changes
[emacs.git] / src / gtkutil.c
blob701bcab70609ff66b507c74db4a3893276446c3d
1 /* Functions for creating and updating GTK widgets.
3 Copyright (C) 2003-2015 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
10 (at 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 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
52 #define gtk_widget_set_has_window(w, b) \
53 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
54 #endif
55 #ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
56 #define gtk_dialog_get_action_area(w) ((w)->action_area)
57 #define gtk_dialog_get_content_area(w) ((w)->vbox)
58 #endif
59 #ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
60 #define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
61 #endif
62 #ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
63 #define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
64 #define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
65 #define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
66 #define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
67 #endif
68 #if GTK_CHECK_VERSION (2, 12, 0)
69 #define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
70 #else
71 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
72 #endif
74 #if ! GTK_CHECK_VERSION (2, 14, 0)
75 #define gtk_adjustment_configure(adj, xvalue, xlower, \
76 xupper, xstep_increment, \
77 xpage_increment, xpagesize) \
78 do { \
79 adj->lower = xlower; \
80 adj->upper = xupper; \
81 adj->page_size = xpagesize; \
82 gtk_adjustment_set_value (adj, xvalue); \
83 adj->page_increment = xpage_increment; \
84 adj->step_increment = xstep_increment; \
85 } while (0)
86 #endif /* < Gtk+ 2.14 */
88 #ifdef HAVE_FREETYPE
89 #if GTK_CHECK_VERSION (3, 2, 0)
90 #define USE_NEW_GTK_FONT_CHOOSER 1
91 #else
92 #define USE_NEW_GTK_FONT_CHOOSER 0
93 #define gtk_font_chooser_dialog_new(x, y) \
94 gtk_font_selection_dialog_new (x)
95 #undef GTK_FONT_CHOOSER
96 #define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
97 #define gtk_font_chooser_set_font(x, y) \
98 gtk_font_selection_dialog_set_font_name (x, y)
99 #endif
100 #endif /* HAVE_FREETYPE */
102 #if GTK_CHECK_VERSION (3, 10, 0)
103 #define XG_TEXT_CANCEL "Cancel"
104 #define XG_TEXT_OK "OK"
105 #define XG_TEXT_OPEN "Open"
106 #else
107 #define XG_TEXT_CANCEL GTK_STOCK_CANCEL
108 #define XG_TEXT_OK GTK_STOCK_OK
109 #define XG_TEXT_OPEN GTK_STOCK_OPEN
110 #endif
112 #ifndef HAVE_GTK3
113 #ifdef USE_GTK_TOOLTIP
114 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
115 #endif
116 #define gdk_window_get_geometry(w, a, b, c, d) \
117 gdk_window_get_geometry (w, a, b, c, d, 0)
118 #define gdk_x11_window_lookup_for_display(d, w) \
119 gdk_xid_table_lookup_for_display (d, w)
120 #define gtk_box_new(ori, spacing) \
121 ((ori) == GTK_ORIENTATION_HORIZONTAL \
122 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
123 #define gtk_scrollbar_new(ori, spacing) \
124 ((ori) == GTK_ORIENTATION_HORIZONTAL \
125 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
126 #ifndef GDK_KEY_g
127 #define GDK_KEY_g GDK_g
128 #endif
129 #endif /* HAVE_GTK3 */
131 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
133 static void update_theme_scrollbar_width (void);
134 static void update_theme_scrollbar_height (void);
136 #define TB_INFO_KEY "xg_frame_tb_info"
137 struct xg_frame_tb_info
139 Lisp_Object last_tool_bar;
140 Lisp_Object style;
141 int n_last_items;
142 int hmargin, vmargin;
143 GtkTextDirection dir;
147 /***********************************************************************
148 Display handling functions
149 ***********************************************************************/
151 /* Keep track of the default display, or NULL if there is none. Emacs
152 may close all its displays. */
154 static GdkDisplay *gdpy_def;
156 /* When the GTK widget W is to be created on a display for F that
157 is not the default display, set the display for W.
158 W can be a GtkMenu or a GtkWindow widget. */
160 static void
161 xg_set_screen (GtkWidget *w, struct frame *f)
163 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
165 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
166 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
168 if (GTK_IS_MENU (w))
169 gtk_menu_set_screen (GTK_MENU (w), gscreen);
170 else
171 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
176 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
177 *DPY is set to NULL if the display can't be opened.
179 Returns non-zero if display could be opened, zero if display could not
180 be opened, and less than zero if the GTK version doesn't support
181 multiple displays. */
183 void
184 xg_display_open (char *display_name, Display **dpy)
186 GdkDisplay *gdpy;
188 unrequest_sigio (); // See comment in x_display_ok, xterm.c.
189 gdpy = gdk_display_open (display_name);
190 request_sigio ();
191 if (!gdpy_def && gdpy)
193 gdpy_def = gdpy;
194 gdk_display_manager_set_default_display (gdk_display_manager_get (),
195 gdpy);
198 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
202 /* Close display DPY. */
204 void
205 xg_display_close (Display *dpy)
207 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
209 /* If this is the default display, try to change it before closing.
210 If there is no other display to use, gdpy_def is set to NULL, and
211 the next call to xg_display_open resets the default display. */
212 if (gdk_display_get_default () == gdpy)
214 struct x_display_info *dpyinfo;
215 GdkDisplay *gdpy_new = NULL;
217 /* Find another display. */
218 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
219 if (dpyinfo->display != dpy)
221 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
222 gdk_display_manager_set_default_display (gdk_display_manager_get (),
223 gdpy_new);
224 break;
226 gdpy_def = gdpy_new;
229 #if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
230 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
231 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
232 can continue running, but there will be memory leaks. */
233 g_object_run_dispose (G_OBJECT (gdpy));
234 #else
235 /* This seems to be fixed in GTK 2.10. */
236 gdk_display_close (gdpy);
237 #endif
241 /***********************************************************************
242 Utility functions
243 ***********************************************************************/
245 /* Create and return the cursor to be used for popup menus and
246 scroll bars on display DPY. */
248 GdkCursor *
249 xg_create_default_cursor (Display *dpy)
251 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
252 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
255 static GdkPixbuf *
256 xg_get_pixbuf_from_pixmap (struct frame *f, Pixmap pix)
258 int iunused;
259 GdkPixbuf *tmp_buf;
260 Window wunused;
261 unsigned int width, height, uunused;
262 XImage *xim;
264 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
265 &width, &height, &uunused, &uunused);
267 xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
268 ~0, XYPixmap);
269 if (!xim) return 0;
271 tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data,
272 GDK_COLORSPACE_RGB,
273 FALSE,
274 xim->bitmap_unit,
275 width,
276 height,
277 xim->bytes_per_line,
278 NULL,
279 NULL);
280 XDestroyImage (xim);
281 return tmp_buf;
284 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
286 static GdkPixbuf *
287 xg_get_pixbuf_from_pix_and_mask (struct frame *f,
288 Pixmap pix,
289 Pixmap mask)
291 int width, height;
292 GdkPixbuf *icon_buf, *tmp_buf;
294 tmp_buf = xg_get_pixbuf_from_pixmap (f, pix);
295 icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
296 g_object_unref (G_OBJECT (tmp_buf));
298 width = gdk_pixbuf_get_width (icon_buf);
299 height = gdk_pixbuf_get_height (icon_buf);
301 if (mask)
303 GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask);
304 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
305 guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
306 int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
307 int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
308 int y;
310 for (y = 0; y < height; ++y)
312 guchar *iconptr, *maskptr;
313 int x;
315 iconptr = pixels + y * rowstride;
316 maskptr = mask_pixels + y * mask_rowstride;
318 for (x = 0; x < width; ++x)
320 /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
321 just R is sufficient. */
322 if (maskptr[0] == 0)
323 iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
325 iconptr += rowstride/width;
326 maskptr += mask_rowstride/width;
330 g_object_unref (G_OBJECT (mask_buf));
333 return icon_buf;
336 static Lisp_Object
337 file_for_image (Lisp_Object image)
339 Lisp_Object specified_file = Qnil;
340 Lisp_Object tail;
342 for (tail = XCDR (image);
343 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
344 tail = XCDR (XCDR (tail)))
345 if (EQ (XCAR (tail), QCfile))
346 specified_file = XCAR (XCDR (tail));
348 return specified_file;
351 /* For the image defined in IMG, make and return a GtkImage. For displays with
352 8 planes or less we must make a GdkPixbuf and apply the mask manually.
353 Otherwise the highlighting and dimming the tool bar code in GTK does
354 will look bad. For display with more than 8 planes we just use the
355 pixmap and mask directly. For monochrome displays, GTK doesn't seem
356 able to use external pixmaps, it looks bad whatever we do.
357 The image is defined on the display where frame F is.
358 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
359 If OLD_WIDGET is NULL, a new widget is constructed and returned.
360 If OLD_WIDGET is not NULL, that widget is modified. */
362 static GtkWidget *
363 xg_get_image_for_pixmap (struct frame *f,
364 struct image *img,
365 GtkWidget *widget,
366 GtkImage *old_widget)
368 GdkPixbuf *icon_buf;
370 /* If we have a file, let GTK do all the image handling.
371 This seems to be the only way to make insensitive and activated icons
372 look good in all cases. */
373 Lisp_Object specified_file = file_for_image (img->spec);
374 Lisp_Object file;
376 /* We already loaded the image once before calling this
377 function, so this only fails if the image file has been removed.
378 In that case, use the pixmap already loaded. */
380 if (STRINGP (specified_file)
381 && STRINGP (file = x_find_image_file (specified_file)))
383 char *encoded_file = SSDATA (ENCODE_FILE (file));
384 if (! old_widget)
385 old_widget = GTK_IMAGE (gtk_image_new_from_file (encoded_file));
386 else
387 gtk_image_set_from_file (old_widget, encoded_file);
389 return GTK_WIDGET (old_widget);
392 /* No file, do the image handling ourselves. This will look very bad
393 on a monochrome display, and sometimes bad on all displays with
394 certain themes. */
396 /* This is a workaround to make icons look good on pseudo color
397 displays. Apparently GTK expects the images to have an alpha
398 channel. If they don't, insensitive and activated icons will
399 look bad. This workaround does not work on monochrome displays,
400 and is strictly not needed on true color/static color displays (i.e.
401 16 bits and higher). But we do it anyway so we get a pixbuf that is
402 not associated with the img->pixmap. The img->pixmap may be removed
403 by clearing the image cache and then the tool bar redraw fails, since
404 Gtk+ assumes the pixmap is always there. */
405 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
407 if (icon_buf)
409 if (! old_widget)
410 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
411 else
412 gtk_image_set_from_pixbuf (old_widget, icon_buf);
414 g_object_unref (G_OBJECT (icon_buf));
417 return GTK_WIDGET (old_widget);
421 /* Set CURSOR on W and all widgets W contain. We must do like this
422 for scroll bars and menu because they create widgets internally,
423 and it is those widgets that are visible. */
425 static void
426 xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
428 GdkWindow *window = gtk_widget_get_window (w);
429 GList *children = gdk_window_peek_children (window);
431 gdk_window_set_cursor (window, cursor);
433 /* The scroll bar widget has more than one GDK window (had to look at
434 the source to figure this out), and there is no way to set cursor
435 on widgets in GTK. So we must set the cursor for all GDK windows.
436 Ditto for menus. */
438 for ( ; children; children = g_list_next (children))
439 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
442 /* Insert NODE into linked LIST. */
444 static void
445 xg_list_insert (xg_list_node *list, xg_list_node *node)
447 xg_list_node *list_start = list->next;
449 if (list_start) list_start->prev = node;
450 node->next = list_start;
451 node->prev = 0;
452 list->next = node;
455 /* Remove NODE from linked LIST. */
457 static void
458 xg_list_remove (xg_list_node *list, xg_list_node *node)
460 xg_list_node *list_start = list->next;
461 if (node == list_start)
463 list->next = node->next;
464 if (list->next) list->next->prev = 0;
466 else
468 node->prev->next = node->next;
469 if (node->next) node->next->prev = node->prev;
473 /* Allocate and return a utf8 version of STR. If STR is already
474 utf8 or NULL, just return a copy of STR.
475 A new string is allocated and the caller must free the result
476 with g_free. */
478 static char *
479 get_utf8_string (const char *str)
481 char *utf8_str;
483 if (!str) return NULL;
485 /* If not UTF-8, try current locale. */
486 if (!g_utf8_validate (str, -1, NULL))
487 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
488 else
489 return g_strdup (str);
491 if (!utf8_str)
493 /* Probably some control characters in str. Escape them. */
494 ptrdiff_t len;
495 ptrdiff_t nr_bad = 0;
496 gsize bytes_read;
497 gsize bytes_written;
498 unsigned char *p = (unsigned char *)str;
499 char *cp, *up;
500 GError *err = NULL;
502 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
503 &bytes_written, &err))
504 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
506 ++nr_bad;
507 p += bytes_written+1;
508 g_error_free (err);
509 err = NULL;
512 if (err)
514 g_error_free (err);
515 err = NULL;
517 if (cp) g_free (cp);
519 len = strlen (str);
520 if ((min (PTRDIFF_MAX, SIZE_MAX) - len - 1) / 4 < nr_bad)
521 memory_full (SIZE_MAX);
522 up = utf8_str = xmalloc (len + nr_bad * 4 + 1);
523 p = (unsigned char *)str;
525 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
526 &bytes_written, &err))
527 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
529 memcpy (up, p, bytes_written);
530 up += bytes_written;
531 up += sprintf (up, "\\%03o", p[bytes_written]);
532 p += bytes_written + 1;
533 g_error_free (err);
534 err = NULL;
537 if (cp)
539 strcpy (up, cp);
540 g_free (cp);
542 if (err)
544 g_error_free (err);
545 err = NULL;
548 return utf8_str;
551 /* Check for special colors used in face spec for region face.
552 The colors are fetched from the Gtk+ theme.
553 Return true if color was found, false if not. */
555 bool
556 xg_check_special_colors (struct frame *f,
557 const char *color_name,
558 XColor *color)
560 bool success_p = 0;
561 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
562 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
564 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
565 return success_p;
567 block_input ();
569 #ifdef HAVE_GTK3
570 GtkStyleContext *gsty
571 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
572 GdkRGBA col;
573 char buf[sizeof "rgb://rrrr/gggg/bbbb"];
574 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
575 if (get_fg)
576 gtk_style_context_get_color (gsty, state, &col);
577 else
578 gtk_style_context_get_background_color (gsty, state, &col);
580 sprintf (buf, "rgb:%04x/%04x/%04x",
581 (unsigned) (col.red * 65535),
582 (unsigned) (col.green * 65535),
583 (unsigned) (col.blue * 65535));
584 success_p = x_parse_color (f, buf, color) != 0;
585 #else
586 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
587 GdkColor *grgb = get_bg
588 ? &gsty->bg[GTK_STATE_SELECTED]
589 : &gsty->fg[GTK_STATE_SELECTED];
591 color->red = grgb->red;
592 color->green = grgb->green;
593 color->blue = grgb->blue;
594 color->pixel = grgb->pixel;
595 success_p = 1;
596 #endif
599 unblock_input ();
600 return success_p;
605 /***********************************************************************
606 Tooltips
607 ***********************************************************************/
608 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
609 We use that to pop down the tooltip. This happens if Gtk+ for some
610 reason wants to change or hide the tooltip. */
612 #ifdef USE_GTK_TOOLTIP
614 static void
615 hierarchy_ch_cb (GtkWidget *widget,
616 GtkWidget *previous_toplevel,
617 gpointer user_data)
619 struct frame *f = user_data;
620 struct x_output *x = f->output_data.x;
621 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
623 if (! top || ! GTK_IS_WINDOW (top))
624 gtk_widget_hide (previous_toplevel);
627 /* Callback called when Gtk+ thinks a tooltip should be displayed.
628 We use it to get the tooltip window and the tooltip widget so
629 we can manipulate the ourselves.
631 Return FALSE ensures that the tooltip is not shown. */
633 static gboolean
634 qttip_cb (GtkWidget *widget,
635 gint xpos,
636 gint ypos,
637 gboolean keyboard_mode,
638 GtkTooltip *tooltip,
639 gpointer user_data)
641 struct frame *f = user_data;
642 struct x_output *x = f->output_data.x;
643 if (x->ttip_widget == NULL)
645 GtkWidget *p;
646 GList *list, *iter;
648 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
649 x->ttip_widget = tooltip;
650 g_object_ref (G_OBJECT (tooltip));
651 x->ttip_lbl = gtk_label_new ("");
652 g_object_ref (G_OBJECT (x->ttip_lbl));
653 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
654 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
656 /* Change stupid Gtk+ default line wrapping. */
657 p = gtk_widget_get_parent (x->ttip_lbl);
658 list = gtk_container_get_children (GTK_CONTAINER (p));
659 for (iter = list; iter; iter = g_list_next (iter))
661 GtkWidget *w = GTK_WIDGET (iter->data);
662 if (GTK_IS_LABEL (w))
663 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
665 g_list_free (list);
667 /* ATK needs an empty title for some reason. */
668 gtk_window_set_title (x->ttip_window, "");
669 /* Realize so we can safely get screen later on. */
670 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
671 gtk_widget_realize (x->ttip_lbl);
673 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
674 G_CALLBACK (hierarchy_ch_cb), f);
676 return FALSE;
679 #endif /* USE_GTK_TOOLTIP */
681 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
682 Return true if a system tooltip is available. */
684 bool
685 xg_prepare_tooltip (struct frame *f,
686 Lisp_Object string,
687 int *width,
688 int *height)
690 #ifndef USE_GTK_TOOLTIP
691 return 0;
692 #else
693 struct x_output *x = f->output_data.x;
694 GtkWidget *widget;
695 GdkWindow *gwin;
696 GdkScreen *screen;
697 GtkSettings *settings;
698 gboolean tt_enabled = TRUE;
699 GtkRequisition req;
700 Lisp_Object encoded_string;
702 if (!x->ttip_lbl) return 0;
704 block_input ();
705 encoded_string = ENCODE_UTF_8 (string);
706 widget = GTK_WIDGET (x->ttip_lbl);
707 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
708 screen = gdk_window_get_screen (gwin);
709 settings = gtk_settings_get_for_screen (screen);
710 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
711 if (tt_enabled)
713 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
714 /* Record that we disabled it so it can be enabled again. */
715 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
716 (gpointer)f);
719 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
720 g_object_set_data (G_OBJECT
721 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
722 "gdk-display-current-tooltip", NULL);
724 /* Put our dummy widget in so we can get callbacks for unrealize and
725 hierarchy-changed. */
726 gtk_tooltip_set_custom (x->ttip_widget, widget);
727 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
728 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
729 if (width) *width = req.width;
730 if (height) *height = req.height;
732 unblock_input ();
734 return 1;
735 #endif /* USE_GTK_TOOLTIP */
738 /* Show the tooltip at ROOT_X and ROOT_Y.
739 xg_prepare_tooltip must have been called before this function. */
741 void
742 xg_show_tooltip (struct frame *f, int root_x, int root_y)
744 #ifdef USE_GTK_TOOLTIP
745 struct x_output *x = f->output_data.x;
746 if (x->ttip_window)
748 block_input ();
749 gtk_window_move (x->ttip_window, root_x, root_y);
750 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
751 unblock_input ();
753 #endif
756 /* Hide tooltip if shown. Do nothing if not shown.
757 Return true if tip was hidden, false if not (i.e. not using
758 system tooltips). */
760 bool
761 xg_hide_tooltip (struct frame *f)
763 bool ret = 0;
764 #ifdef USE_GTK_TOOLTIP
765 if (f->output_data.x->ttip_window)
767 GtkWindow *win = f->output_data.x->ttip_window;
768 block_input ();
769 gtk_widget_hide (GTK_WIDGET (win));
771 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
773 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
774 GdkScreen *screen = gdk_window_get_screen (gwin);
775 GtkSettings *settings = gtk_settings_get_for_screen (screen);
776 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
778 unblock_input ();
780 ret = 1;
782 #endif
783 return ret;
787 /***********************************************************************
788 General functions for creating widgets, resizing, events, e.t.c.
789 ***********************************************************************/
791 static void
792 my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
793 const gchar *msg, gpointer user_data)
795 if (!strstr (msg, "visible children"))
796 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
799 /* Make a geometry string and pass that to GTK. It seems this is the
800 only way to get geometry position right if the user explicitly
801 asked for a position when starting Emacs.
802 F is the frame we shall set geometry for. */
804 static void
805 xg_set_geometry (struct frame *f)
807 if (f->size_hint_flags & (USPosition | PPosition))
809 int left = f->left_pos;
810 int xneg = f->size_hint_flags & XNegative;
811 int top = f->top_pos;
812 int yneg = f->size_hint_flags & YNegative;
813 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
814 guint id;
816 if (xneg)
817 left = -left;
818 if (yneg)
819 top = -top;
821 sprintf (geom_str, "=%dx%d%c%d%c%d",
822 FRAME_PIXEL_WIDTH (f),
823 FRAME_PIXEL_HEIGHT (f),
824 (xneg ? '-' : '+'), left,
825 (yneg ? '-' : '+'), top);
827 /* Silence warning about visible children. */
828 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
829 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
831 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
832 geom_str))
833 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
835 g_log_remove_handler ("Gtk", id);
839 /* Clear under internal border if any. As we use a mix of Gtk+ and X calls
840 and use a GtkFixed widget, this doesn't happen automatically. */
842 void
843 xg_clear_under_internal_border (struct frame *f)
845 if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
847 #ifndef USE_CAIRO
848 GtkWidget *wfixed = f->output_data.x->edit_widget;
850 gtk_widget_queue_draw (wfixed);
851 gdk_window_process_all_updates ();
852 #endif
853 x_clear_area (f, 0, 0,
854 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
856 x_clear_area (f, 0, 0,
857 FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
859 x_clear_area (f, 0,
860 FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
861 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
863 x_clear_area (f,
864 FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
865 0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
869 static int
870 xg_get_gdk_scale (void)
872 const char *sscale = getenv ("GDK_SCALE");
874 if (sscale)
876 long scale = atol (sscale);
877 if (0 < scale)
878 return min (scale, INT_MAX);
881 return 1;
884 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
885 and a Gtk+ menu bar, we get resize events for the edit part of the
886 frame only. We let Gtk+ deal with the Gtk+ parts.
887 F is the frame to resize.
888 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
890 void
891 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
893 int width, height;
895 if (pixelwidth == -1 && pixelheight == -1)
897 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
898 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
899 0, 0, &pixelwidth, &pixelheight);
900 else
901 return;
904 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
905 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
907 frame_size_history_add
908 (f, Qxg_frame_resized, width, height, Qnil);
910 if (width != FRAME_TEXT_WIDTH (f)
911 || height != FRAME_TEXT_HEIGHT (f)
912 || pixelwidth != FRAME_PIXEL_WIDTH (f)
913 || pixelheight != FRAME_PIXEL_HEIGHT (f))
915 xg_clear_under_internal_border (f);
916 change_frame_size (f, width, height, 0, 1, 0, 1);
917 SET_FRAME_GARBAGED (f);
918 cancel_mouse_face (f);
922 /* Resize the outer window of frame F after changing the height.
923 COLUMNS/ROWS is the size the edit area shall have after the resize. */
925 void
926 xg_frame_set_char_size (struct frame *f, int width, int height)
928 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
929 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
930 Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
931 gint gwidth, gheight;
932 int totalheight
933 = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
934 int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
936 if (FRAME_PIXEL_HEIGHT (f) == 0)
937 return;
939 gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
940 &gwidth, &gheight);
942 /* Do this before resize, as we don't know yet if we will be resized. */
943 xg_clear_under_internal_border (f);
945 if (FRAME_VISIBLE_P (f))
947 int scale = xg_get_gdk_scale ();
948 totalheight /= scale;
949 totalwidth /= scale;
952 x_wm_set_size_hint (f, 0, 0);
954 /* Resize the top level widget so rows and columns remain constant.
956 When the frame is fullheight and we only want to change the width
957 or it is fullwidth and we only want to change the height we should
958 be able to preserve the fullscreen property. However, due to the
959 fact that we have to send a resize request anyway, the window
960 manager will abolish it. At least the respective size should
961 remain unchanged but giving the frame back its normal size will
962 be broken ... */
963 if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
965 frame_size_history_add
966 (f, Qxg_frame_set_char_size_1, width, height,
967 list2 (make_number (gheight), make_number (totalheight)));
969 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
970 gwidth, totalheight);
972 else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
974 frame_size_history_add
975 (f, Qxg_frame_set_char_size_2, width, height,
976 list2 (make_number (gwidth), make_number (totalwidth)));
978 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
979 totalwidth, gheight);
981 else
983 frame_size_history_add
984 (f, Qxg_frame_set_char_size_3, width, height,
985 list2 (make_number (totalwidth), make_number (totalheight)));
987 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
988 totalwidth, totalheight);
989 fullscreen = Qnil;
992 SET_FRAME_GARBAGED (f);
993 cancel_mouse_face (f);
995 /* We can not call change_frame_size for a mapped frame,
996 we can not set pixel width/height either. The window manager may
997 override our resize request, XMonad does this all the time.
998 The best we can do is try to sync, so lisp code sees the updated
999 size as fast as possible.
1000 For unmapped windows, we can set rows/cols. When
1001 the frame is mapped again we will (hopefully) get the correct size. */
1002 if (FRAME_VISIBLE_P (f))
1004 /* Must call this to flush out events */
1005 (void)gtk_events_pending ();
1006 gdk_flush ();
1007 x_wait_for_event (f, ConfigureNotify);
1009 if (!NILP (fullscreen))
1010 /* Try to restore fullscreen state. */
1012 store_frame_param (f, Qfullscreen, fullscreen);
1013 x_set_fullscreen (f, fullscreen, fullscreen);
1016 else
1017 adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
1021 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
1022 The policy is to keep the number of editable lines. */
1024 #if 0
1025 static void
1026 xg_height_or_width_changed (struct frame *f)
1028 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1029 FRAME_TOTAL_PIXEL_WIDTH (f),
1030 FRAME_TOTAL_PIXEL_HEIGHT (f));
1031 f->output_data.x->hint_flags = 0;
1032 x_wm_set_size_hint (f, 0, 0);
1034 #endif
1036 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1037 Must be done like this, because GtkWidget:s can have "hidden"
1038 X Window that aren't accessible.
1040 Return 0 if no widget match WDESC. */
1042 GtkWidget *
1043 xg_win_to_widget (Display *dpy, Window wdesc)
1045 gpointer gdkwin;
1046 GtkWidget *gwdesc = 0;
1048 block_input ();
1050 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1051 wdesc);
1052 if (gdkwin)
1054 GdkEvent event;
1055 event.any.window = gdkwin;
1056 event.any.type = GDK_NOTHING;
1057 gwdesc = gtk_get_event_widget (&event);
1060 unblock_input ();
1061 return gwdesc;
1064 /* Set the background of widget W to PIXEL. */
1066 static void
1067 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
1069 #ifdef HAVE_GTK3
1070 GdkRGBA bg;
1071 XColor xbg;
1072 xbg.pixel = pixel;
1073 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1075 bg.red = (double)xbg.red/65535.0;
1076 bg.green = (double)xbg.green/65535.0;
1077 bg.blue = (double)xbg.blue/65535.0;
1078 bg.alpha = 1.0;
1079 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1081 #else
1082 GdkColor bg;
1083 GdkColormap *map = gtk_widget_get_colormap (w);
1084 gdk_colormap_query_color (map, pixel, &bg);
1085 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1086 #endif
1089 /* Callback called when the gtk theme changes.
1090 We notify lisp code so it can fix faces used for region for example. */
1092 static void
1093 style_changed_cb (GObject *go,
1094 GParamSpec *spec,
1095 gpointer user_data)
1097 struct input_event event;
1098 GdkDisplay *gdpy = user_data;
1099 const char *display_name = gdk_display_get_name (gdpy);
1100 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1102 EVENT_INIT (event);
1103 event.kind = CONFIG_CHANGED_EVENT;
1104 event.frame_or_window = build_string (display_name);
1105 /* Theme doesn't change often, so intern is called seldom. */
1106 event.arg = intern ("theme-name");
1107 kbd_buffer_store_event (&event);
1109 update_theme_scrollbar_width ();
1110 update_theme_scrollbar_height ();
1112 /* If scroll bar width changed, we need set the new size on all frames
1113 on this display. */
1114 if (dpy)
1116 Lisp_Object rest, frame;
1117 FOR_EACH_FRAME (rest, frame)
1119 struct frame *f = XFRAME (frame);
1120 if (FRAME_LIVE_P (f)
1121 && FRAME_X_P (f)
1122 && FRAME_X_DISPLAY (f) == dpy)
1124 x_set_scroll_bar_default_width (f);
1125 x_set_scroll_bar_default_height (f);
1126 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
1132 /* Called when a delete-event occurs on WIDGET. */
1134 static gboolean
1135 delete_cb (GtkWidget *widget,
1136 GdkEvent *event,
1137 gpointer user_data)
1139 return TRUE;
1142 /* Create and set up the GTK widgets for frame F.
1143 Return true if creation succeeded. */
1145 bool
1146 xg_create_frame_widgets (struct frame *f)
1148 GtkWidget *wtop;
1149 GtkWidget *wvbox, *whbox;
1150 GtkWidget *wfixed;
1151 #ifndef HAVE_GTK3
1152 GtkRcStyle *style;
1153 #endif
1154 char *title = 0;
1156 block_input ();
1158 if (FRAME_X_EMBEDDED_P (f))
1160 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1161 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1163 else
1164 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1166 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1167 has backported it to Gtk+ 2.0 and they add the resize grip for
1168 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1169 forever, so disable the grip. */
1170 #if (! GTK_CHECK_VERSION (3, 0, 0) \
1171 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1172 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1173 #endif
1175 xg_set_screen (wtop, f);
1177 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1178 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1179 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1180 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1182 #ifdef HAVE_GTK3
1183 wfixed = emacs_fixed_new (f);
1184 #else
1185 wfixed = gtk_fixed_new ();
1186 #endif
1188 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1190 if (wtop) gtk_widget_destroy (wtop);
1191 if (wvbox) gtk_widget_destroy (wvbox);
1192 if (whbox) gtk_widget_destroy (whbox);
1193 if (wfixed) gtk_widget_destroy (wfixed);
1195 unblock_input ();
1196 return 0;
1199 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1200 gtk_widget_set_name (wtop, EMACS_CLASS);
1201 gtk_widget_set_name (wvbox, "pane");
1202 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1204 /* If this frame has a title or name, set it in the title bar. */
1205 if (! NILP (f->title))
1206 title = SSDATA (ENCODE_UTF_8 (f->title));
1207 else if (! NILP (f->name))
1208 title = SSDATA (ENCODE_UTF_8 (f->name));
1210 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
1212 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1213 FRAME_GTK_WIDGET (f) = wfixed;
1214 f->output_data.x->vbox_widget = wvbox;
1215 f->output_data.x->hbox_widget = whbox;
1217 gtk_widget_set_has_window (wfixed, TRUE);
1219 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1220 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1221 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1223 if (FRAME_EXTERNAL_TOOL_BAR (f))
1224 update_frame_tool_bar (f);
1226 /* We don't want this widget double buffered, because we draw on it
1227 with regular X drawing primitives, so from a GTK/GDK point of
1228 view, the widget is totally blank. When an expose comes, this
1229 will make the widget blank, and then Emacs redraws it. This flickers
1230 a lot, so we turn off double buffering. */
1231 gtk_widget_set_double_buffered (wfixed, FALSE);
1233 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1234 SSDATA (Vx_resource_name),
1235 SSDATA (Vx_resource_class));
1237 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1238 GTK is to destroy the widget. We want Emacs to do that instead. */
1239 g_signal_connect (G_OBJECT (wtop), "delete-event",
1240 G_CALLBACK (delete_cb), f);
1242 /* Convert our geometry parameters into a geometry string
1243 and specify it.
1244 GTK will itself handle calculating the real position this way. */
1245 xg_set_geometry (f);
1246 f->win_gravity
1247 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1249 gtk_widget_add_events (wfixed,
1250 GDK_POINTER_MOTION_MASK
1251 | GDK_EXPOSURE_MASK
1252 | GDK_BUTTON_PRESS_MASK
1253 | GDK_BUTTON_RELEASE_MASK
1254 | GDK_KEY_PRESS_MASK
1255 | GDK_ENTER_NOTIFY_MASK
1256 | GDK_LEAVE_NOTIFY_MASK
1257 | GDK_FOCUS_CHANGE_MASK
1258 | GDK_STRUCTURE_MASK
1259 | GDK_VISIBILITY_NOTIFY_MASK);
1261 /* Must realize the windows so the X window gets created. It is used
1262 by callers of this function. */
1263 gtk_widget_realize (wfixed);
1264 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1266 /* Since GTK clears its window by filling with the background color,
1267 we must keep X and GTK background in sync. */
1268 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1270 #ifndef HAVE_GTK3
1271 /* Also, do not let any background pixmap to be set, this looks very
1272 bad as Emacs overwrites the background pixmap with its own idea
1273 of background color. */
1274 style = gtk_widget_get_modifier_style (wfixed);
1276 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1277 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1278 gtk_widget_modify_style (wfixed, style);
1279 #else
1280 gtk_widget_set_can_focus (wfixed, TRUE);
1281 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1282 #endif
1284 #ifdef USE_GTK_TOOLTIP
1285 /* Steal a tool tip window we can move ourselves. */
1286 f->output_data.x->ttip_widget = 0;
1287 f->output_data.x->ttip_lbl = 0;
1288 f->output_data.x->ttip_window = 0;
1289 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1290 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1291 #endif
1294 GdkScreen *screen = gtk_widget_get_screen (wtop);
1295 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1296 /* Only connect this signal once per screen. */
1297 if (! g_signal_handler_find (G_OBJECT (gs),
1298 G_SIGNAL_MATCH_FUNC,
1299 0, 0, 0,
1300 G_CALLBACK (style_changed_cb),
1303 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1304 G_CALLBACK (style_changed_cb),
1305 gdk_screen_get_display (screen));
1309 unblock_input ();
1311 return 1;
1314 void
1315 xg_free_frame_widgets (struct frame *f)
1317 if (FRAME_GTK_OUTER_WIDGET (f))
1319 #ifdef USE_GTK_TOOLTIP
1320 struct x_output *x = f->output_data.x;
1321 #endif
1322 struct xg_frame_tb_info *tbinfo
1323 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1324 TB_INFO_KEY);
1325 if (tbinfo)
1326 xfree (tbinfo);
1328 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1329 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1330 FRAME_GTK_OUTER_WIDGET (f) = 0;
1331 #ifdef USE_GTK_TOOLTIP
1332 if (x->ttip_lbl)
1333 gtk_widget_destroy (x->ttip_lbl);
1334 if (x->ttip_widget)
1335 g_object_unref (G_OBJECT (x->ttip_widget));
1336 #endif
1340 /* Set the normal size hints for the window manager, for frame F.
1341 FLAGS is the flags word to use--or 0 meaning preserve the flags
1342 that the window now has.
1343 If USER_POSITION, set the User Position
1344 flag (this is useful when FLAGS is 0). */
1346 void
1347 x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
1349 /* Must use GTK routines here, otherwise GTK resets the size hints
1350 to its own defaults. */
1351 GdkGeometry size_hints;
1352 gint hint_flags = 0;
1353 int base_width, base_height;
1354 int min_rows = 0, min_cols = 0;
1355 int win_gravity = f->win_gravity;
1356 Lisp_Object fs_state, frame;
1357 int scale = xg_get_gdk_scale ();
1359 /* Don't set size hints during initialization; that apparently leads
1360 to a race condition. See the thread at
1361 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1362 if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
1363 return;
1365 XSETFRAME (frame, f);
1366 fs_state = Fframe_parameter (frame, Qfullscreen);
1367 if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
1368 (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
1369 x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
1371 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1372 Gtk3 don't get along and the frame shrinks (!).
1374 return;
1377 if (flags)
1379 memset (&size_hints, 0, sizeof (size_hints));
1380 f->output_data.x->size_hints = size_hints;
1381 f->output_data.x->hint_flags = hint_flags;
1383 else
1384 flags = f->size_hint_flags;
1386 size_hints = f->output_data.x->size_hints;
1387 hint_flags = f->output_data.x->hint_flags;
1389 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1390 size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
1391 size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
1393 hint_flags |= GDK_HINT_BASE_SIZE;
1394 /* Use one row/col here so base_height/width does not become zero.
1395 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
1396 Obviously this makes the row/col value displayed off by 1. */
1397 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1398 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1399 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1401 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1402 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1404 size_hints.base_width = base_width;
1405 size_hints.base_height = base_height;
1406 size_hints.min_width = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
1407 size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
1409 /* These currently have a one to one mapping with the X values, but I
1410 don't think we should rely on that. */
1411 hint_flags |= GDK_HINT_WIN_GRAVITY;
1412 size_hints.win_gravity = 0;
1413 if (win_gravity == NorthWestGravity)
1414 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1415 else if (win_gravity == NorthGravity)
1416 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1417 else if (win_gravity == NorthEastGravity)
1418 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1419 else if (win_gravity == WestGravity)
1420 size_hints.win_gravity = GDK_GRAVITY_WEST;
1421 else if (win_gravity == CenterGravity)
1422 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1423 else if (win_gravity == EastGravity)
1424 size_hints.win_gravity = GDK_GRAVITY_EAST;
1425 else if (win_gravity == SouthWestGravity)
1426 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1427 else if (win_gravity == SouthGravity)
1428 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1429 else if (win_gravity == SouthEastGravity)
1430 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1431 else if (win_gravity == StaticGravity)
1432 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1434 if (user_position)
1436 hint_flags &= ~GDK_HINT_POS;
1437 hint_flags |= GDK_HINT_USER_POS;
1440 size_hints.base_width /= scale;
1441 size_hints.base_height /= scale;
1442 size_hints.width_inc /= scale;
1443 size_hints.height_inc /= scale;
1445 if (hint_flags != f->output_data.x->hint_flags
1446 || memcmp (&size_hints,
1447 &f->output_data.x->size_hints,
1448 sizeof (size_hints)) != 0)
1450 block_input ();
1451 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1452 NULL, &size_hints, hint_flags);
1453 f->output_data.x->size_hints = size_hints;
1454 f->output_data.x->hint_flags = hint_flags;
1455 unblock_input ();
1459 /* Change background color of a frame.
1460 Since GTK uses the background color to clear the window, we must
1461 keep the GTK and X colors in sync.
1462 F is the frame to change,
1463 BG is the pixel value to change to. */
1465 void
1466 xg_set_background_color (struct frame *f, unsigned long bg)
1468 if (FRAME_GTK_WIDGET (f))
1470 block_input ();
1471 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1472 unblock_input ();
1477 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1478 functions so GTK does not overwrite the icon. */
1480 void
1481 xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1483 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1484 icon_pixmap,
1485 icon_mask);
1486 if (gp)
1487 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1492 /***********************************************************************
1493 Dialog functions
1494 ***********************************************************************/
1495 /* Return the dialog title to use for a dialog of type KEY.
1496 This is the encoding used by lwlib. We use the same for GTK. */
1498 static const char *
1499 get_dialog_title (char key)
1501 const char *title = "";
1503 switch (key) {
1504 case 'E': case 'e':
1505 title = "Error";
1506 break;
1508 case 'I': case 'i':
1509 title = "Information";
1510 break;
1512 case 'L': case 'l':
1513 title = "Prompt";
1514 break;
1516 case 'P': case 'p':
1517 title = "Prompt";
1518 break;
1520 case 'Q': case 'q':
1521 title = "Question";
1522 break;
1525 return title;
1528 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1529 the dialog, but return TRUE so the event does not propagate further
1530 in GTK. This prevents GTK from destroying the dialog widget automatically
1531 and we can always destroy the widget manually, regardless of how
1532 it was popped down (button press or WM_DELETE_WINDOW).
1533 W is the dialog widget.
1534 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1535 user_data is NULL (not used).
1537 Returns TRUE to end propagation of event. */
1539 static gboolean
1540 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1542 gtk_widget_unmap (w);
1543 return TRUE;
1546 /* Create a popup dialog window. See also xg_create_widget below.
1547 WV is a widget_value describing the dialog.
1548 SELECT_CB is the callback to use when a button has been pressed.
1549 DEACTIVATE_CB is the callback to use when the dialog pops down.
1551 Returns the GTK dialog widget. */
1553 static GtkWidget *
1554 create_dialog (widget_value *wv,
1555 GCallback select_cb,
1556 GCallback deactivate_cb)
1558 const char *title = get_dialog_title (wv->name[0]);
1559 int total_buttons = wv->name[1] - '0';
1560 int right_buttons = wv->name[4] - '0';
1561 int left_buttons;
1562 int button_nr = 0;
1563 int button_spacing = 10;
1564 GtkWidget *wdialog = gtk_dialog_new ();
1565 GtkDialog *wd = GTK_DIALOG (wdialog);
1566 widget_value *item;
1567 GtkWidget *whbox_down;
1569 /* If the number of buttons is greater than 4, make two rows of buttons
1570 instead. This looks better. */
1571 bool make_two_rows = total_buttons > 4;
1573 #if GTK_CHECK_VERSION (3, 12, 0)
1574 GtkBuilder *gbld = gtk_builder_new ();
1575 GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
1576 gbld,
1577 "action_area");
1578 GtkBox *cur_box = GTK_BOX (go);
1579 g_object_unref (G_OBJECT (gbld));
1580 #else
1581 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1582 #endif
1584 if (right_buttons == 0) right_buttons = total_buttons/2;
1585 left_buttons = total_buttons - right_buttons;
1587 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1588 gtk_widget_set_name (wdialog, "emacs-dialog");
1591 if (make_two_rows)
1593 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1594 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1595 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1596 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1597 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1598 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1600 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1601 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1602 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1604 cur_box = GTK_BOX (whbox_up);
1607 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1608 G_CALLBACK (dialog_delete_callback), 0);
1610 if (deactivate_cb)
1612 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1613 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1616 for (item = wv->contents; item; item = item->next)
1618 char *utf8_label = get_utf8_string (item->value);
1619 GtkWidget *w;
1620 GtkRequisition req;
1622 if (item->name && strcmp (item->name, "message") == 0)
1624 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1625 /* This is the text part of the dialog. */
1626 w = gtk_label_new (utf8_label);
1627 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1628 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1629 #if GTK_CHECK_VERSION (3, 14, 0)
1630 gtk_widget_set_halign (w, GTK_ALIGN_START);
1631 gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
1632 #else
1633 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1634 #endif
1635 /* Try to make dialog look better. Must realize first so
1636 the widget can calculate the size it needs. */
1637 gtk_widget_realize (w);
1638 gtk_widget_get_preferred_size (w, NULL, &req);
1639 gtk_box_set_spacing (wvbox, req.height);
1640 if (item->value && strlen (item->value) > 0)
1641 button_spacing = 2*req.width/strlen (item->value);
1642 if (button_spacing < 10) button_spacing = 10;
1644 else
1646 /* This is one button to add to the dialog. */
1647 w = gtk_button_new_with_label (utf8_label);
1648 if (! item->enabled)
1649 gtk_widget_set_sensitive (w, FALSE);
1650 if (select_cb)
1651 g_signal_connect (G_OBJECT (w), "clicked",
1652 select_cb, item->call_data);
1654 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1655 if (++button_nr == left_buttons)
1657 if (make_two_rows)
1658 cur_box = GTK_BOX (whbox_down);
1662 if (utf8_label)
1663 g_free (utf8_label);
1666 return wdialog;
1669 struct xg_dialog_data
1671 GMainLoop *loop;
1672 int response;
1673 GtkWidget *w;
1674 guint timerid;
1677 /* Function that is called when the file or font dialogs pop down.
1678 W is the dialog widget, RESPONSE is the response code.
1679 USER_DATA is what we passed in to g_signal_connect. */
1681 static void
1682 xg_dialog_response_cb (GtkDialog *w,
1683 gint response,
1684 gpointer user_data)
1686 struct xg_dialog_data *dd = user_data;
1687 dd->response = response;
1688 g_main_loop_quit (dd->loop);
1692 /* Destroy the dialog. This makes it pop down. */
1694 static void
1695 pop_down_dialog (void *arg)
1697 struct xg_dialog_data *dd = arg;
1699 block_input ();
1700 if (dd->w) gtk_widget_destroy (dd->w);
1701 if (dd->timerid != 0) g_source_remove (dd->timerid);
1703 g_main_loop_quit (dd->loop);
1704 g_main_loop_unref (dd->loop);
1706 unblock_input ();
1709 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1710 We pass in DATA as gpointer* so we can use this as a callback. */
1712 static gboolean
1713 xg_maybe_add_timer (gpointer data)
1715 struct xg_dialog_data *dd = data;
1716 struct timespec next_time = timer_check ();
1718 dd->timerid = 0;
1720 if (timespec_valid_p (next_time))
1722 time_t s = next_time.tv_sec;
1723 int per_ms = TIMESPEC_RESOLUTION / 1000;
1724 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
1725 if (s <= ((guint) -1 - ms) / 1000)
1726 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1728 return FALSE;
1732 /* Pops up a modal dialog W and waits for response.
1733 We don't use gtk_dialog_run because we want to process emacs timers.
1734 The dialog W is not destroyed when this function returns. */
1736 static int
1737 xg_dialog_run (struct frame *f, GtkWidget *w)
1739 ptrdiff_t count = SPECPDL_INDEX ();
1740 struct xg_dialog_data dd;
1742 xg_set_screen (w, f);
1743 gtk_window_set_transient_for (GTK_WINDOW (w),
1744 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1745 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1746 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1748 dd.loop = g_main_loop_new (NULL, FALSE);
1749 dd.response = GTK_RESPONSE_CANCEL;
1750 dd.w = w;
1751 dd.timerid = 0;
1753 g_signal_connect (G_OBJECT (w),
1754 "response",
1755 G_CALLBACK (xg_dialog_response_cb),
1756 &dd);
1757 /* Don't destroy the widget if closed by the window manager close button. */
1758 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1759 gtk_widget_show (w);
1761 record_unwind_protect_ptr (pop_down_dialog, &dd);
1763 (void) xg_maybe_add_timer (&dd);
1764 g_main_loop_run (dd.loop);
1766 dd.w = 0;
1767 unbind_to (count, Qnil);
1769 return dd.response;
1773 /***********************************************************************
1774 File dialog functions
1775 ***********************************************************************/
1776 /* Return true if the old file selection dialog is being used. */
1778 bool
1779 xg_uses_old_file_dialog (void)
1781 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1782 return x_gtk_use_old_file_dialog;
1783 #else
1784 return 0;
1785 #endif
1789 typedef char * (*xg_get_file_func) (GtkWidget *);
1791 /* Return the selected file for file chooser dialog W.
1792 The returned string must be free:d. */
1794 static char *
1795 xg_get_file_name_from_chooser (GtkWidget *w)
1797 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1800 /* Callback called when the "Show hidden files" toggle is pressed.
1801 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1803 static void
1804 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1806 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1807 gboolean visible;
1808 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1809 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1813 /* Callback called when a property changes in a file chooser.
1814 GOBJECT is the file chooser dialog, ARG1 describes the property.
1815 USER_DATA is the toggle widget in the file chooser dialog.
1816 We use this to update the "Show hidden files" toggle when the user
1817 changes that property by right clicking in the file list. */
1819 static void
1820 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1822 if (strcmp (arg1->name, "show-hidden") == 0)
1824 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1825 gboolean visible, toggle_on;
1827 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1828 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1830 if (!!visible != !!toggle_on)
1832 g_signal_handlers_block_by_func (G_OBJECT (wtoggle),
1833 G_CALLBACK (xg_toggle_visibility_cb),
1834 gobject);
1835 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1836 g_signal_handlers_unblock_by_func
1837 (G_OBJECT (wtoggle),
1838 G_CALLBACK (xg_toggle_visibility_cb),
1839 gobject);
1841 x_gtk_show_hidden_files = visible;
1845 /* Read a file name from the user using a file chooser dialog.
1846 F is the current frame.
1847 PROMPT is a prompt to show to the user. May not be NULL.
1848 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1849 If MUSTMATCH_P, the returned file name must be an existing
1850 file. (Actually, this only has cosmetic effects, the user can
1851 still enter a non-existing file.) *FUNC is set to a function that
1852 can be used to retrieve the selected file name from the returned widget.
1854 Returns the created widget. */
1856 static GtkWidget *
1857 xg_get_file_with_chooser (struct frame *f,
1858 char *prompt,
1859 char *default_filename,
1860 bool mustmatch_p, bool only_dir_p,
1861 xg_get_file_func *func)
1863 char msgbuf[1024];
1865 GtkWidget *filewin, *wtoggle, *wbox, *wmessage IF_LINT (= NULL);
1866 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1867 GtkFileChooserAction action = (mustmatch_p ?
1868 GTK_FILE_CHOOSER_ACTION_OPEN :
1869 GTK_FILE_CHOOSER_ACTION_SAVE);
1871 if (only_dir_p)
1872 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1874 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1875 XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
1876 (mustmatch_p || only_dir_p ?
1877 XG_TEXT_OPEN : XG_TEXT_OK),
1878 GTK_RESPONSE_OK,
1879 NULL);
1880 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
1882 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1883 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
1884 gtk_widget_show (wbox);
1885 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
1887 if (x_gtk_show_hidden_files)
1889 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
1890 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
1892 gtk_widget_show (wtoggle);
1893 g_signal_connect (G_OBJECT (wtoggle), "clicked",
1894 G_CALLBACK (xg_toggle_visibility_cb), filewin);
1895 g_signal_connect (G_OBJECT (filewin), "notify",
1896 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
1898 if (x_gtk_file_dialog_help_text)
1900 char *z = msgbuf;
1901 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
1902 Show the C-l help text only for versions < 2.10. */
1903 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
1904 z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
1905 strcpy (z, "\nIf you don't like this file selector, use the "
1906 "corresponding\nkey binding or customize "
1907 "use-file-dialog to turn it off.");
1909 wmessage = gtk_label_new (msgbuf);
1910 gtk_widget_show (wmessage);
1913 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
1914 if (x_gtk_file_dialog_help_text)
1915 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
1916 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
1918 if (default_filename)
1920 Lisp_Object file;
1921 char *utf8_filename;
1923 file = build_string (default_filename);
1925 /* File chooser does not understand ~/... in the file name. It must be
1926 an absolute name starting with /. */
1927 if (default_filename[0] != '/')
1928 file = Fexpand_file_name (file, Qnil);
1930 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
1931 if (! NILP (Ffile_directory_p (file)))
1932 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
1933 utf8_filename);
1934 else
1936 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1937 utf8_filename);
1938 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
1940 char *cp = strrchr (utf8_filename, '/');
1941 if (cp) ++cp;
1942 else cp = utf8_filename;
1943 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
1948 *func = xg_get_file_name_from_chooser;
1949 return filewin;
1952 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1954 /* Return the selected file for file selector dialog W.
1955 The returned string must be free:d. */
1957 static char *
1958 xg_get_file_name_from_selector (GtkWidget *w)
1960 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
1961 return xstrdup (gtk_file_selection_get_filename (filesel));
1964 /* Create a file selection dialog.
1965 F is the current frame.
1966 PROMPT is a prompt to show to the user. May not be NULL.
1967 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1968 If MUSTMATCH_P, the returned file name must be an existing
1969 file. *FUNC is set to a function that can be used to retrieve the
1970 selected file name from the returned widget.
1972 Returns the created widget. */
1974 static GtkWidget *
1975 xg_get_file_with_selection (struct frame *f,
1976 char *prompt,
1977 char *default_filename,
1978 bool mustmatch_p, bool only_dir_p,
1979 xg_get_file_func *func)
1981 GtkWidget *filewin;
1982 GtkFileSelection *filesel;
1984 filewin = gtk_file_selection_new (prompt);
1985 filesel = GTK_FILE_SELECTION (filewin);
1987 if (default_filename)
1988 gtk_file_selection_set_filename (filesel, default_filename);
1990 if (mustmatch_p)
1992 /* The selection_entry part of filesel is not documented. */
1993 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1994 gtk_file_selection_hide_fileop_buttons (filesel);
1997 *func = xg_get_file_name_from_selector;
1999 return filewin;
2001 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
2003 /* Read a file name from the user using a file dialog, either the old
2004 file selection dialog, or the new file chooser dialog. Which to use
2005 depends on what the GTK version used has, and what the value of
2006 gtk-use-old-file-dialog.
2007 F is the current frame.
2008 PROMPT is a prompt to show to the user. May not be NULL.
2009 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2010 If MUSTMATCH_P, the returned file name must be an existing
2011 file.
2013 Returns a file name or NULL if no file was selected.
2014 The returned string must be freed by the caller. */
2016 char *
2017 xg_get_file_name (struct frame *f,
2018 char *prompt,
2019 char *default_filename,
2020 bool mustmatch_p,
2021 bool only_dir_p)
2023 GtkWidget *w = 0;
2024 char *fn = 0;
2025 int filesel_done = 0;
2026 xg_get_file_func func;
2028 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2030 if (xg_uses_old_file_dialog ())
2031 w = xg_get_file_with_selection (f, prompt, default_filename,
2032 mustmatch_p, only_dir_p, &func);
2033 else
2034 w = xg_get_file_with_chooser (f, prompt, default_filename,
2035 mustmatch_p, only_dir_p, &func);
2037 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
2038 w = xg_get_file_with_chooser (f, prompt, default_filename,
2039 mustmatch_p, only_dir_p, &func);
2040 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
2042 gtk_widget_set_name (w, "emacs-filedialog");
2044 filesel_done = xg_dialog_run (f, w);
2045 if (filesel_done == GTK_RESPONSE_OK)
2046 fn = (*func) (w);
2048 gtk_widget_destroy (w);
2049 return fn;
2052 /***********************************************************************
2053 GTK font chooser
2054 ***********************************************************************/
2056 #ifdef HAVE_FREETYPE
2058 #if USE_NEW_GTK_FONT_CHOOSER
2060 #define XG_WEIGHT_TO_SYMBOL(w) \
2061 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2062 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2063 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2064 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2065 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2066 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2067 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2068 : Qultra_bold)
2070 #define XG_STYLE_TO_SYMBOL(s) \
2071 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2072 : s == PANGO_STYLE_ITALIC ? Qitalic \
2073 : Qnormal)
2075 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2078 static char *x_last_font_name;
2080 /* Pop up a GTK font selector and return the name of the font the user
2081 selects, as a C string. The returned font name follows GTK's own
2082 format:
2084 `FAMILY [VALUE1 VALUE2] SIZE'
2086 This can be parsed using font_parse_fcname in font.c.
2087 DEFAULT_NAME, if non-zero, is the default font name. */
2089 Lisp_Object
2090 xg_get_font (struct frame *f, const char *default_name)
2092 GtkWidget *w;
2093 int done = 0;
2094 Lisp_Object font = Qnil;
2096 w = gtk_font_chooser_dialog_new
2097 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2099 if (default_name)
2101 /* Convert fontconfig names to Gtk names, i.e. remove - before
2102 number */
2103 char *p = strrchr (default_name, '-');
2104 if (p)
2106 char *ep = p+1;
2107 while (c_isdigit (*ep))
2108 ++ep;
2109 if (*ep == '\0') *p = ' ';
2112 else if (x_last_font_name)
2113 default_name = x_last_font_name;
2115 if (default_name)
2116 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2118 gtk_widget_set_name (w, "emacs-fontdialog");
2119 done = xg_dialog_run (f, w);
2120 if (done == GTK_RESPONSE_OK)
2122 #if USE_NEW_GTK_FONT_CHOOSER
2123 /* Use the GTK3 font chooser. */
2124 PangoFontDescription *desc
2125 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2127 if (desc)
2129 const char *name = pango_font_description_get_family (desc);
2130 gint size = pango_font_description_get_size (desc);
2131 PangoWeight weight = pango_font_description_get_weight (desc);
2132 PangoStyle style = pango_font_description_get_style (desc);
2134 #ifdef USE_CAIRO
2135 #define FONT_TYPE_WANTED (Qftcr)
2136 #else
2137 #define FONT_TYPE_WANTED (Qxft)
2138 #endif
2139 font = CALLN (Ffont_spec,
2140 QCname, build_string (name),
2141 QCsize, make_float (pango_units_to_double (size)),
2142 QCweight, XG_WEIGHT_TO_SYMBOL (weight),
2143 QCslant, XG_STYLE_TO_SYMBOL (style),
2144 QCtype,
2145 FONT_TYPE_WANTED);
2147 pango_font_description_free (desc);
2148 dupstring (&x_last_font_name, name);
2151 #else /* Use old font selector, which just returns the font name. */
2153 char *font_name
2154 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2156 if (font_name)
2158 font = build_string (font_name);
2159 g_free (x_last_font_name);
2160 x_last_font_name = font_name;
2162 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2165 gtk_widget_destroy (w);
2166 return font;
2168 #endif /* HAVE_FREETYPE */
2172 /***********************************************************************
2173 Menu functions.
2174 ***********************************************************************/
2176 /* The name of menu items that can be used for customization. Since GTK
2177 RC files are very crude and primitive, we have to set this on all
2178 menu item names so a user can easily customize menu items. */
2180 #define MENU_ITEM_NAME "emacs-menuitem"
2183 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2184 during GC. The next member points to the items. */
2185 static xg_list_node xg_menu_cb_list;
2187 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2188 during GC. The next member points to the items. */
2189 static xg_list_node xg_menu_item_cb_list;
2191 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2192 F is the frame CL_DATA will be initialized for.
2193 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2195 The menu bar and all sub menus under the menu bar in a frame
2196 share the same structure, hence the reference count.
2198 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2199 allocated xg_menu_cb_data if CL_DATA is NULL. */
2201 static xg_menu_cb_data *
2202 make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
2204 if (! cl_data)
2206 cl_data = xmalloc (sizeof *cl_data);
2207 cl_data->f = f;
2208 cl_data->menu_bar_vector = f->menu_bar_vector;
2209 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2210 cl_data->highlight_cb = highlight_cb;
2211 cl_data->ref_count = 0;
2213 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2216 cl_data->ref_count++;
2218 return cl_data;
2221 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2222 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2224 When the menu bar is updated, menu items may have been added and/or
2225 removed, so menu_bar_vector and menu_bar_items_used change. We must
2226 then update CL_DATA since it is used to determine which menu
2227 item that is invoked in the menu.
2228 HIGHLIGHT_CB could change, there is no check that the same
2229 function is given when modifying a menu bar as was given when
2230 creating the menu bar. */
2232 static void
2233 update_cl_data (xg_menu_cb_data *cl_data,
2234 struct frame *f,
2235 GCallback highlight_cb)
2237 if (cl_data)
2239 cl_data->f = f;
2240 cl_data->menu_bar_vector = f->menu_bar_vector;
2241 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2242 cl_data->highlight_cb = highlight_cb;
2246 /* Decrease reference count for CL_DATA.
2247 If reference count is zero, free CL_DATA. */
2249 static void
2250 unref_cl_data (xg_menu_cb_data *cl_data)
2252 if (cl_data && cl_data->ref_count > 0)
2254 cl_data->ref_count--;
2255 if (cl_data->ref_count == 0)
2257 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2258 xfree (cl_data);
2263 /* Function that marks all lisp data during GC. */
2265 void
2266 xg_mark_data (void)
2268 xg_list_node *iter;
2269 Lisp_Object rest, frame;
2271 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2272 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2274 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2276 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2278 if (! NILP (cb_data->help))
2279 mark_object (cb_data->help);
2282 FOR_EACH_FRAME (rest, frame)
2284 struct frame *f = XFRAME (frame);
2286 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2288 struct xg_frame_tb_info *tbinfo
2289 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2290 TB_INFO_KEY);
2291 if (tbinfo)
2293 mark_object (tbinfo->last_tool_bar);
2294 mark_object (tbinfo->style);
2301 /* Callback called when a menu item is destroyed. Used to free data.
2302 W is the widget that is being destroyed (not used).
2303 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2305 static void
2306 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2308 if (client_data)
2310 xg_menu_item_cb_data *data = client_data;
2311 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2312 xfree (data);
2316 /* Callback called when the pointer enters/leaves a menu item.
2317 W is the parent of the menu item.
2318 EVENT is either an enter event or leave event.
2319 CLIENT_DATA is not used.
2321 Returns FALSE to tell GTK to keep processing this event. */
2323 static gboolean
2324 menuitem_highlight_callback (GtkWidget *w,
2325 GdkEventCrossing *event,
2326 gpointer client_data)
2328 GdkEvent ev;
2329 GtkWidget *subwidget;
2330 xg_menu_item_cb_data *data;
2332 ev.crossing = *event;
2333 subwidget = gtk_get_event_widget (&ev);
2334 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
2335 if (data)
2337 if (! NILP (data->help) && data->cl_data->highlight_cb)
2339 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2340 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2341 (*func) (subwidget, call_data);
2345 return FALSE;
2348 /* Callback called when a menu is destroyed. Used to free data.
2349 W is the widget that is being destroyed (not used).
2350 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2352 static void
2353 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2355 unref_cl_data (client_data);
2358 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2359 must be non-NULL) and can be inserted into a menu item.
2361 Returns the GtkHBox. */
2363 static GtkWidget *
2364 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2366 GtkWidget *wlbl;
2367 GtkWidget *wkey;
2368 GtkWidget *wbox;
2370 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2371 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2372 wlbl = gtk_label_new (utf8_label);
2373 wkey = gtk_label_new (utf8_key);
2375 #if GTK_CHECK_VERSION (3, 14, 0)
2376 gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
2377 gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
2378 gtk_widget_set_halign (wkey, GTK_ALIGN_START);
2379 gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
2380 #else
2381 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2382 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2383 #endif
2384 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2385 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2387 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2388 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2389 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2391 return wbox;
2394 /* Make and return a menu item widget with the key to the right.
2395 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2396 UTF8_KEY is the text representing the key binding.
2397 ITEM is the widget_value describing the menu item.
2399 GROUP is an in/out parameter. If the menu item to be created is not
2400 part of any radio menu group, *GROUP contains NULL on entry and exit.
2401 If the menu item to be created is part of a radio menu group, on entry
2402 *GROUP contains the group to use, or NULL if this is the first item
2403 in the group. On exit, *GROUP contains the radio item group.
2405 Unfortunately, keys don't line up as nicely as in Motif,
2406 but the MacOS X version doesn't either, so I guess that is OK. */
2408 static GtkWidget *
2409 make_menu_item (const char *utf8_label,
2410 const char *utf8_key,
2411 widget_value *item,
2412 GSList **group)
2414 GtkWidget *w;
2415 GtkWidget *wtoadd = 0;
2417 /* It has been observed that some menu items have a NULL name field.
2418 This will lead to this function being called with a NULL utf8_label.
2419 GTK crashes on that so we set a blank label. Why there is a NULL
2420 name remains to be investigated. */
2421 if (! utf8_label) utf8_label = " ";
2423 if (utf8_key)
2424 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2426 if (item->button_type == BUTTON_TYPE_TOGGLE)
2428 *group = NULL;
2429 if (utf8_key) w = gtk_check_menu_item_new ();
2430 else w = gtk_check_menu_item_new_with_label (utf8_label);
2431 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2433 else if (item->button_type == BUTTON_TYPE_RADIO)
2435 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2436 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2437 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2438 if (item->selected)
2439 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2441 else
2443 *group = NULL;
2444 if (utf8_key) w = gtk_menu_item_new ();
2445 else w = gtk_menu_item_new_with_label (utf8_label);
2448 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2449 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2451 return w;
2454 /* Create a menu item widget, and connect the callbacks.
2455 ITEM describes the menu item.
2456 F is the frame the created menu belongs to.
2457 SELECT_CB is the callback to use when a menu item is selected.
2458 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2459 CL_DATA points to the callback data to be used for this menu.
2460 GROUP is an in/out parameter. If the menu item to be created is not
2461 part of any radio menu group, *GROUP contains NULL on entry and exit.
2462 If the menu item to be created is part of a radio menu group, on entry
2463 *GROUP contains the group to use, or NULL if this is the first item
2464 in the group. On exit, *GROUP contains the radio item group.
2466 Returns the created GtkWidget. */
2468 static GtkWidget *
2469 xg_create_one_menuitem (widget_value *item,
2470 struct frame *f,
2471 GCallback select_cb,
2472 GCallback highlight_cb,
2473 xg_menu_cb_data *cl_data,
2474 GSList **group)
2476 char *utf8_label;
2477 char *utf8_key;
2478 GtkWidget *w;
2479 xg_menu_item_cb_data *cb_data;
2481 utf8_label = get_utf8_string (item->name);
2482 utf8_key = get_utf8_string (item->key);
2484 w = make_menu_item (utf8_label, utf8_key, item, group);
2486 if (utf8_label) g_free (utf8_label);
2487 if (utf8_key) g_free (utf8_key);
2489 cb_data = xmalloc (sizeof *cb_data);
2491 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2493 cb_data->select_id = 0;
2494 cb_data->help = item->help;
2495 cb_data->cl_data = cl_data;
2496 cb_data->call_data = item->call_data;
2498 g_signal_connect (G_OBJECT (w),
2499 "destroy",
2500 G_CALLBACK (menuitem_destroy_callback),
2501 cb_data);
2503 /* Put cb_data in widget, so we can get at it when modifying menubar */
2504 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2506 /* final item, not a submenu */
2507 if (item->call_data && ! item->contents)
2509 if (select_cb)
2510 cb_data->select_id
2511 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2514 return w;
2517 /* Create a full menu tree specified by DATA.
2518 F is the frame the created menu belongs to.
2519 SELECT_CB is the callback to use when a menu item is selected.
2520 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2521 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2522 If POP_UP_P, create a popup menu.
2523 If MENU_BAR_P, create a menu bar.
2524 TOPMENU is the topmost GtkWidget that others shall be placed under.
2525 It may be NULL, in that case we create the appropriate widget
2526 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2527 CL_DATA is the callback data we shall use for this menu, or NULL
2528 if we haven't set the first callback yet.
2529 NAME is the name to give to the top level menu if this function
2530 creates it. May be NULL to not set any name.
2532 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2533 not NULL.
2535 This function calls itself to create submenus. */
2537 static GtkWidget *
2538 create_menus (widget_value *data,
2539 struct frame *f,
2540 GCallback select_cb,
2541 GCallback deactivate_cb,
2542 GCallback highlight_cb,
2543 bool pop_up_p,
2544 bool menu_bar_p,
2545 GtkWidget *topmenu,
2546 xg_menu_cb_data *cl_data,
2547 const char *name)
2549 widget_value *item;
2550 GtkWidget *wmenu = topmenu;
2551 GSList *group = NULL;
2553 if (! topmenu)
2555 if (! menu_bar_p)
2557 wmenu = gtk_menu_new ();
2558 xg_set_screen (wmenu, f);
2559 /* Connect this to the menu instead of items so we get enter/leave for
2560 disabled items also. TODO: Still does not get enter/leave for
2561 disabled items in detached menus. */
2562 g_signal_connect (G_OBJECT (wmenu),
2563 "enter-notify-event",
2564 G_CALLBACK (menuitem_highlight_callback),
2565 NULL);
2566 g_signal_connect (G_OBJECT (wmenu),
2567 "leave-notify-event",
2568 G_CALLBACK (menuitem_highlight_callback),
2569 NULL);
2571 else
2573 wmenu = gtk_menu_bar_new ();
2574 /* Set width of menu bar to a small value so it doesn't enlarge
2575 a small initial frame size. The width will be set to the
2576 width of the frame later on when it is added to a container.
2577 height -1: Natural height. */
2578 gtk_widget_set_size_request (wmenu, 1, -1);
2581 /* Put cl_data on the top menu for easier access. */
2582 cl_data = make_cl_data (cl_data, f, highlight_cb);
2583 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2584 g_signal_connect (G_OBJECT (wmenu), "destroy",
2585 G_CALLBACK (menu_destroy_callback), cl_data);
2587 if (name)
2588 gtk_widget_set_name (wmenu, name);
2590 if (deactivate_cb)
2591 g_signal_connect (G_OBJECT (wmenu),
2592 "selection-done", deactivate_cb, 0);
2595 for (item = data; item; item = item->next)
2597 GtkWidget *w;
2599 if (pop_up_p && !item->contents && !item->call_data
2600 && !menu_separator_name_p (item->name))
2602 char *utf8_label;
2603 /* A title for a popup. We do the same as GTK does when
2604 creating titles, but it does not look good. */
2605 group = NULL;
2606 utf8_label = get_utf8_string (item->name);
2608 w = gtk_menu_item_new_with_label (utf8_label);
2609 gtk_widget_set_sensitive (w, FALSE);
2610 if (utf8_label) g_free (utf8_label);
2612 else if (menu_separator_name_p (item->name))
2614 group = NULL;
2615 /* GTK only have one separator type. */
2616 w = gtk_separator_menu_item_new ();
2618 else
2620 w = xg_create_one_menuitem (item,
2622 item->contents ? 0 : select_cb,
2623 highlight_cb,
2624 cl_data,
2625 &group);
2627 /* Create a possibly empty submenu for menu bar items, since some
2628 themes don't highlight items correctly without it. */
2629 if (item->contents || menu_bar_p)
2631 GtkWidget *submenu = create_menus (item->contents,
2633 select_cb,
2634 deactivate_cb,
2635 highlight_cb,
2639 cl_data,
2641 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2645 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2646 gtk_widget_set_name (w, MENU_ITEM_NAME);
2649 return wmenu;
2652 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2653 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2654 with some text and buttons.
2655 F is the frame the created item belongs to.
2656 NAME is the name to use for the top widget.
2657 VAL is a widget_value structure describing items to be created.
2658 SELECT_CB is the callback to use when a menu item is selected or
2659 a dialog button is pressed.
2660 DEACTIVATE_CB is the callback to use when an item is deactivated.
2661 For a menu, when a sub menu is not shown anymore, for a dialog it is
2662 called when the dialog is popped down.
2663 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2665 Returns the widget created. */
2667 GtkWidget *
2668 xg_create_widget (const char *type, const char *name, struct frame *f,
2669 widget_value *val, GCallback select_cb,
2670 GCallback deactivate_cb, GCallback highlight_cb)
2672 GtkWidget *w = 0;
2673 bool menu_bar_p = strcmp (type, "menubar") == 0;
2674 bool pop_up_p = strcmp (type, "popup") == 0;
2676 if (strcmp (type, "dialog") == 0)
2678 w = create_dialog (val, select_cb, deactivate_cb);
2679 xg_set_screen (w, f);
2680 gtk_window_set_transient_for (GTK_WINDOW (w),
2681 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2682 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2683 gtk_widget_set_name (w, "emacs-dialog");
2684 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2686 else if (menu_bar_p || pop_up_p)
2688 w = create_menus (val->contents,
2690 select_cb,
2691 deactivate_cb,
2692 highlight_cb,
2693 pop_up_p,
2694 menu_bar_p,
2697 name);
2699 /* Set the cursor to an arrow for popup menus when they are mapped.
2700 This is done by default for menu bar menus. */
2701 if (pop_up_p)
2703 /* Must realize so the GdkWindow inside the widget is created. */
2704 gtk_widget_realize (w);
2705 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
2708 else
2710 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2711 type);
2714 return w;
2717 /* Return the label for menu item WITEM. */
2719 static const char *
2720 xg_get_menu_item_label (GtkMenuItem *witem)
2722 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2723 return gtk_label_get_label (wlabel);
2726 /* Return true if the menu item WITEM has the text LABEL. */
2728 static bool
2729 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2731 bool is_same = 0;
2732 char *utf8_label = get_utf8_string (label);
2733 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2735 if (! old_label && ! utf8_label)
2736 is_same = 1;
2737 else if (old_label && utf8_label)
2738 is_same = strcmp (utf8_label, old_label) == 0;
2740 if (utf8_label) g_free (utf8_label);
2742 return is_same;
2745 /* Destroy widgets in LIST. */
2747 static void
2748 xg_destroy_widgets (GList *list)
2750 GList *iter;
2752 for (iter = list; iter; iter = g_list_next (iter))
2754 GtkWidget *w = GTK_WIDGET (iter->data);
2756 /* Destroying the widget will remove it from the container it is in. */
2757 gtk_widget_destroy (w);
2761 /* Update the top level names in MENUBAR (i.e. not submenus).
2762 F is the frame the menu bar belongs to.
2763 *LIST is a list with the current menu bar names (menu item widgets).
2764 ITER is the item within *LIST that shall be updated.
2765 POS is the numerical position, starting at 0, of ITER in *LIST.
2766 VAL describes what the menu bar shall look like after the update.
2767 SELECT_CB is the callback to use when a menu item is selected.
2768 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2769 CL_DATA points to the callback data to be used for this menu bar.
2771 This function calls itself to walk through the menu bar names. */
2773 static void
2774 xg_update_menubar (GtkWidget *menubar,
2775 struct frame *f,
2776 GList **list,
2777 GList *iter,
2778 int pos,
2779 widget_value *val,
2780 GCallback select_cb,
2781 GCallback deactivate_cb,
2782 GCallback highlight_cb,
2783 xg_menu_cb_data *cl_data)
2785 if (! iter && ! val)
2786 return;
2787 else if (iter && ! val)
2789 /* Item(s) have been removed. Remove all remaining items. */
2790 xg_destroy_widgets (iter);
2792 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2793 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2794 gtk_menu_item_new_with_label (""),
2796 /* All updated. */
2797 val = 0;
2798 iter = 0;
2800 else if (! iter && val)
2802 /* Item(s) added. Add all new items in one call. */
2803 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2804 0, 1, menubar, cl_data, 0);
2806 /* All updated. */
2807 val = 0;
2808 iter = 0;
2810 /* Below this neither iter or val is NULL */
2811 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2813 /* This item is still the same, check next item. */
2814 val = val->next;
2815 iter = g_list_next (iter);
2816 ++pos;
2818 else /* This item is changed. */
2820 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2821 GtkMenuItem *witem2 = 0;
2822 bool val_in_menubar = 0;
2823 bool iter_in_new_menubar = 0;
2824 GList *iter2;
2825 widget_value *cur;
2827 /* See if the changed entry (val) is present later in the menu bar */
2828 for (iter2 = iter;
2829 iter2 && ! val_in_menubar;
2830 iter2 = g_list_next (iter2))
2832 witem2 = GTK_MENU_ITEM (iter2->data);
2833 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2836 /* See if the current entry (iter) is present later in the
2837 specification for the new menu bar. */
2838 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2839 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2841 if (val_in_menubar && ! iter_in_new_menubar)
2843 int nr = pos;
2845 /* This corresponds to:
2846 Current: A B C
2847 New: A C
2848 Remove B. */
2850 g_object_ref (G_OBJECT (witem));
2851 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2852 gtk_widget_destroy (GTK_WIDGET (witem));
2854 /* Must get new list since the old changed. */
2855 g_list_free (*list);
2856 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2857 while (nr-- > 0) iter = g_list_next (iter);
2859 else if (! val_in_menubar && ! iter_in_new_menubar)
2861 /* This corresponds to:
2862 Current: A B C
2863 New: A X C
2864 Rename B to X. This might seem to be a strange thing to do,
2865 since if there is a menu under B it will be totally wrong for X.
2866 But consider editing a C file. Then there is a C-mode menu
2867 (corresponds to B above).
2868 If then doing C-x C-f the minibuf menu (X above) replaces the
2869 C-mode menu. When returning from the minibuffer, we get
2870 back the C-mode menu. Thus we do:
2871 Rename B to X (C-mode to minibuf menu)
2872 Rename X to B (minibuf to C-mode menu).
2873 If the X menu hasn't been invoked, the menu under B
2874 is up to date when leaving the minibuffer. */
2875 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2876 char *utf8_label = get_utf8_string (val->name);
2878 /* GTK menu items don't notice when their labels have been
2879 changed from underneath them, so we have to explicitly
2880 use g_object_notify to tell listeners (e.g., a GMenuModel
2881 bridge that might be loaded) that the item's label has
2882 changed. */
2883 gtk_label_set_text (wlabel, utf8_label);
2884 #if GTK_CHECK_VERSION (2, 16, 0)
2885 g_object_notify (G_OBJECT (witem), "label");
2886 #endif
2887 if (utf8_label) g_free (utf8_label);
2888 iter = g_list_next (iter);
2889 val = val->next;
2890 ++pos;
2892 else if (! val_in_menubar && iter_in_new_menubar)
2894 /* This corresponds to:
2895 Current: A B C
2896 New: A X B C
2897 Insert X. */
2899 int nr = pos;
2900 GSList *group = 0;
2901 GtkWidget *w = xg_create_one_menuitem (val,
2903 select_cb,
2904 highlight_cb,
2905 cl_data,
2906 &group);
2908 /* Create a possibly empty submenu for menu bar items, since some
2909 themes don't highlight items correctly without it. */
2910 GtkWidget *submenu = create_menus (NULL, f,
2911 select_cb, deactivate_cb,
2912 highlight_cb,
2913 0, 0, 0, cl_data, 0);
2915 gtk_widget_set_name (w, MENU_ITEM_NAME);
2916 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2919 g_list_free (*list);
2920 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2921 while (nr-- > 0) iter = g_list_next (iter);
2922 iter = g_list_next (iter);
2923 val = val->next;
2924 ++pos;
2926 else /* if (val_in_menubar && iter_in_new_menubar) */
2928 int nr = pos;
2929 /* This corresponds to:
2930 Current: A B C
2931 New: A C B
2932 Move C before B */
2934 g_object_ref (G_OBJECT (witem2));
2935 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2936 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2937 GTK_WIDGET (witem2), pos);
2938 g_object_unref (G_OBJECT (witem2));
2940 g_list_free (*list);
2941 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2942 while (nr-- > 0) iter = g_list_next (iter);
2943 if (iter) iter = g_list_next (iter);
2944 val = val->next;
2945 ++pos;
2949 /* Update the rest of the menu bar. */
2950 xg_update_menubar (menubar, f, list, iter, pos, val,
2951 select_cb, deactivate_cb, highlight_cb, cl_data);
2954 /* Update the menu item W so it corresponds to VAL.
2955 SELECT_CB is the callback to use when a menu item is selected.
2956 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2957 CL_DATA is the data to set in the widget for menu invocation. */
2959 static void
2960 xg_update_menu_item (widget_value *val,
2961 GtkWidget *w,
2962 GCallback select_cb,
2963 GCallback highlight_cb,
2964 xg_menu_cb_data *cl_data)
2966 GtkWidget *wchild;
2967 GtkLabel *wlbl = 0;
2968 GtkLabel *wkey = 0;
2969 char *utf8_label;
2970 char *utf8_key;
2971 const char *old_label = 0;
2972 const char *old_key = 0;
2973 xg_menu_item_cb_data *cb_data;
2974 bool label_changed = false;
2976 wchild = XG_BIN_CHILD (w);
2977 utf8_label = get_utf8_string (val->name);
2978 utf8_key = get_utf8_string (val->key);
2980 /* See if W is a menu item with a key. See make_menu_item above. */
2981 if (GTK_IS_BOX (wchild))
2983 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2985 wlbl = GTK_LABEL (list->data);
2986 wkey = GTK_LABEL (list->next->data);
2987 g_list_free (list);
2989 if (! utf8_key)
2991 /* Remove the key and keep just the label. */
2992 g_object_ref (G_OBJECT (wlbl));
2993 gtk_container_remove (GTK_CONTAINER (w), wchild);
2994 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
2995 g_object_unref (G_OBJECT (wlbl));
2996 wkey = 0;
3000 else /* Just a label. */
3002 wlbl = GTK_LABEL (wchild);
3004 /* Check if there is now a key. */
3005 if (utf8_key)
3007 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3008 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
3010 wlbl = GTK_LABEL (list->data);
3011 wkey = GTK_LABEL (list->next->data);
3012 g_list_free (list);
3014 gtk_container_remove (GTK_CONTAINER (w), wchild);
3015 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3019 if (wkey) old_key = gtk_label_get_label (wkey);
3020 if (wlbl) old_label = gtk_label_get_label (wlbl);
3022 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3024 label_changed = true;
3025 gtk_label_set_text (wkey, utf8_key);
3028 if (! old_label || strcmp (utf8_label, old_label) != 0)
3030 label_changed = true;
3031 gtk_label_set_text (wlbl, utf8_label);
3034 if (utf8_key) g_free (utf8_key);
3035 if (utf8_label) g_free (utf8_label);
3037 if (! val->enabled && gtk_widget_get_sensitive (w))
3038 gtk_widget_set_sensitive (w, FALSE);
3039 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3040 gtk_widget_set_sensitive (w, TRUE);
3042 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
3043 if (cb_data)
3045 cb_data->call_data = val->call_data;
3046 cb_data->help = val->help;
3047 cb_data->cl_data = cl_data;
3049 /* We assume the callback functions don't change. */
3050 if (val->call_data && ! val->contents)
3052 /* This item shall have a select callback. */
3053 if (! cb_data->select_id)
3054 cb_data->select_id
3055 = g_signal_connect (G_OBJECT (w), "activate",
3056 select_cb, cb_data);
3058 else if (cb_data->select_id)
3060 g_signal_handler_disconnect (w, cb_data->select_id);
3061 cb_data->select_id = 0;
3065 #if GTK_CHECK_VERSION (2, 16, 0)
3066 if (label_changed) /* See comment in xg_update_menubar. */
3067 g_object_notify (G_OBJECT (w), "label");
3068 #endif
3071 /* Update the toggle menu item W so it corresponds to VAL. */
3073 static void
3074 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3076 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3079 /* Update the radio menu item W so it corresponds to VAL. */
3081 static void
3082 xg_update_radio_item (widget_value *val, GtkWidget *w)
3084 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3087 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3088 SUBMENU may be NULL, in that case a new menu is created.
3089 F is the frame the menu bar belongs to.
3090 VAL describes the contents of the menu bar.
3091 SELECT_CB is the callback to use when a menu item is selected.
3092 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3093 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3094 CL_DATA is the call back data to use for any newly created items.
3096 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3097 was NULL. */
3099 static GtkWidget *
3100 xg_update_submenu (GtkWidget *submenu,
3101 struct frame *f,
3102 widget_value *val,
3103 GCallback select_cb,
3104 GCallback deactivate_cb,
3105 GCallback highlight_cb,
3106 xg_menu_cb_data *cl_data)
3108 GtkWidget *newsub = submenu;
3109 GList *list = 0;
3110 GList *iter;
3111 widget_value *cur;
3112 GList *first_radio = 0;
3114 if (submenu)
3115 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3117 for (cur = val, iter = list;
3118 cur && iter;
3119 iter = g_list_next (iter), cur = cur->next)
3121 GtkWidget *w = GTK_WIDGET (iter->data);
3123 /* Remember first radio button in a group. If we get a mismatch in
3124 a radio group we must rebuild the whole group so that the connections
3125 in GTK becomes correct. */
3126 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3127 first_radio = iter;
3128 else if (cur->button_type != BUTTON_TYPE_RADIO
3129 && ! GTK_IS_RADIO_MENU_ITEM (w))
3130 first_radio = 0;
3132 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3134 if (! menu_separator_name_p (cur->name))
3135 break;
3137 else if (GTK_IS_CHECK_MENU_ITEM (w))
3139 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3140 break;
3141 xg_update_toggle_item (cur, w);
3142 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3144 else if (GTK_IS_RADIO_MENU_ITEM (w))
3146 if (cur->button_type != BUTTON_TYPE_RADIO)
3147 break;
3148 xg_update_radio_item (cur, w);
3149 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3151 else if (GTK_IS_MENU_ITEM (w))
3153 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3154 GtkWidget *sub;
3156 if (cur->button_type != BUTTON_TYPE_NONE ||
3157 menu_separator_name_p (cur->name))
3158 break;
3160 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3162 sub = gtk_menu_item_get_submenu (witem);
3163 if (sub && ! cur->contents)
3165 /* Not a submenu anymore. */
3166 g_object_ref (G_OBJECT (sub));
3167 remove_submenu (witem);
3168 gtk_widget_destroy (sub);
3170 else if (cur->contents)
3172 GtkWidget *nsub;
3174 nsub = xg_update_submenu (sub, f, cur->contents,
3175 select_cb, deactivate_cb,
3176 highlight_cb, cl_data);
3178 /* If this item just became a submenu, we must set it. */
3179 if (nsub != sub)
3180 gtk_menu_item_set_submenu (witem, nsub);
3183 else
3185 /* Structural difference. Remove everything from here and down
3186 in SUBMENU. */
3187 break;
3191 /* Remove widgets from first structural change. */
3192 if (iter)
3194 /* If we are adding new menu items below, we must remove from
3195 first radio button so that radio groups become correct. */
3196 if (cur && first_radio) xg_destroy_widgets (first_radio);
3197 else xg_destroy_widgets (iter);
3200 if (cur)
3202 /* More items added. Create them. */
3203 newsub = create_menus (cur,
3205 select_cb,
3206 deactivate_cb,
3207 highlight_cb,
3210 submenu,
3211 cl_data,
3215 if (list) g_list_free (list);
3217 return newsub;
3220 /* Update the MENUBAR.
3221 F is the frame the menu bar belongs to.
3222 VAL describes the contents of the menu bar.
3223 If DEEP_P, rebuild all but the top level menu names in
3224 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3225 SELECT_CB is the callback to use when a menu item is selected.
3226 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3227 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3229 void
3230 xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3231 widget_value *val, bool deep_p,
3232 GCallback select_cb, GCallback deactivate_cb,
3233 GCallback highlight_cb)
3235 xg_menu_cb_data *cl_data;
3236 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3238 if (! list) return;
3240 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
3242 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3243 select_cb, deactivate_cb, highlight_cb, cl_data);
3245 if (deep_p)
3247 widget_value *cur;
3249 /* Update all sub menus.
3250 We must keep the submenus (GTK menu item widgets) since the
3251 X Window in the XEvent that activates the menu are those widgets. */
3253 /* Update cl_data, menu_item things in F may have changed. */
3254 update_cl_data (cl_data, f, highlight_cb);
3256 for (cur = val->contents; cur; cur = cur->next)
3258 GList *iter;
3259 GtkWidget *sub = 0;
3260 GtkWidget *newsub;
3261 GtkMenuItem *witem = 0;
3263 /* Find sub menu that corresponds to val and update it. */
3264 for (iter = list ; iter; iter = g_list_next (iter))
3266 witem = GTK_MENU_ITEM (iter->data);
3267 if (xg_item_label_same_p (witem, cur->name))
3269 sub = gtk_menu_item_get_submenu (witem);
3270 break;
3274 newsub = xg_update_submenu (sub,
3276 cur->contents,
3277 select_cb,
3278 deactivate_cb,
3279 highlight_cb,
3280 cl_data);
3281 /* sub may still be NULL. If we just updated non deep and added
3282 a new menu bar item, it has no sub menu yet. So we set the
3283 newly created sub menu under witem. */
3284 if (newsub != sub && witem != 0)
3286 xg_set_screen (newsub, f);
3287 gtk_menu_item_set_submenu (witem, newsub);
3292 g_list_free (list);
3293 gtk_widget_show_all (menubar);
3296 /* Callback called when the menu bar W is mapped.
3297 Used to find the height of the menu bar if we didn't get it
3298 after showing the widget. */
3300 static void
3301 menubar_map_cb (GtkWidget *w, gpointer user_data)
3303 GtkRequisition req;
3304 struct frame *f = user_data;
3305 gtk_widget_get_preferred_size (w, NULL, &req);
3306 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3308 FRAME_MENUBAR_HEIGHT (f) = req.height;
3309 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3313 /* Recompute all the widgets of frame F, when the menu bar has been
3314 changed. */
3316 void
3317 xg_update_frame_menubar (struct frame *f)
3319 struct x_output *x = f->output_data.x;
3320 GtkRequisition req;
3322 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3323 return;
3325 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3326 return; /* Already done this, happens for frames created invisible. */
3328 block_input ();
3330 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3331 FALSE, FALSE, 0);
3332 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3334 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3335 gtk_widget_show_all (x->menubar_widget);
3336 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3338 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3340 FRAME_MENUBAR_HEIGHT (f) = req.height;
3341 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3343 unblock_input ();
3346 /* Get rid of the menu bar of frame F, and free its storage.
3347 This is used when deleting a frame, and when turning off the menu bar. */
3349 void
3350 free_frame_menubar (struct frame *f)
3352 struct x_output *x = f->output_data.x;
3354 if (x->menubar_widget)
3356 block_input ();
3358 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3359 /* The menubar and its children shall be deleted when removed from
3360 the container. */
3361 x->menubar_widget = 0;
3362 FRAME_MENUBAR_HEIGHT (f) = 0;
3363 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3364 unblock_input ();
3368 bool
3369 xg_event_is_for_menubar (struct frame *f, const XEvent *event)
3371 struct x_output *x = f->output_data.x;
3372 GList *iter;
3373 GdkRectangle rec;
3374 GList *list;
3375 GdkDisplay *gdpy;
3376 GdkWindow *gw;
3377 GdkEvent gevent;
3378 GtkWidget *gwdesc;
3380 if (! x->menubar_widget) return 0;
3382 if (! (event->xbutton.x >= 0
3383 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3384 && event->xbutton.y >= 0
3385 && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
3386 && event->xbutton.same_screen))
3387 return 0;
3389 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3390 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3391 if (! gw) return 0;
3392 gevent.any.window = gw;
3393 gevent.any.type = GDK_NOTHING;
3394 gwdesc = gtk_get_event_widget (&gevent);
3395 if (! gwdesc) return 0;
3396 if (! GTK_IS_MENU_BAR (gwdesc)
3397 && ! GTK_IS_MENU_ITEM (gwdesc)
3398 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3399 return 0;
3401 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3402 if (! list) return 0;
3403 rec.x = event->xbutton.x;
3404 rec.y = event->xbutton.y;
3405 rec.width = 1;
3406 rec.height = 1;
3408 for (iter = list ; iter; iter = g_list_next (iter))
3410 GtkWidget *w = GTK_WIDGET (iter->data);
3411 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3412 break;
3414 g_list_free (list);
3415 return iter != 0;
3420 /***********************************************************************
3421 Scroll bar functions
3422 ***********************************************************************/
3425 /* Setting scroll bar values invokes the callback. Use this variable
3426 to indicate that callback should do nothing. */
3428 bool xg_ignore_gtk_scrollbar;
3430 /* Width and height of scroll bars for the current theme. */
3431 static int scroll_bar_width_for_theme;
3432 static int scroll_bar_height_for_theme;
3434 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3435 may be larger than 32 bits. Keep a mapping from integer index to widget
3436 pointers to get around the 32 bit limitation. */
3438 static struct
3440 GtkWidget **widgets;
3441 ptrdiff_t max_size;
3442 ptrdiff_t used;
3443 } id_to_widget;
3445 /* Grow this much every time we need to allocate more */
3447 #define ID_TO_WIDGET_INCR 32
3449 /* Store the widget pointer W in id_to_widget and return the integer index. */
3451 static ptrdiff_t
3452 xg_store_widget_in_map (GtkWidget *w)
3454 ptrdiff_t i;
3456 if (id_to_widget.max_size == id_to_widget.used)
3458 ptrdiff_t new_size;
3459 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3460 memory_full (SIZE_MAX);
3462 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3463 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3464 new_size, sizeof (GtkWidget *));
3466 for (i = id_to_widget.max_size; i < new_size; ++i)
3467 id_to_widget.widgets[i] = 0;
3468 id_to_widget.max_size = new_size;
3471 /* Just loop over the array and find a free place. After all,
3472 how many scroll bars are we creating? Should be a small number.
3473 The check above guarantees we will find a free place. */
3474 for (i = 0; i < id_to_widget.max_size; ++i)
3476 if (! id_to_widget.widgets[i])
3478 id_to_widget.widgets[i] = w;
3479 ++id_to_widget.used;
3481 return i;
3485 /* Should never end up here */
3486 emacs_abort ();
3489 /* Remove pointer at IDX from id_to_widget.
3490 Called when scroll bar is destroyed. */
3492 static void
3493 xg_remove_widget_from_map (ptrdiff_t idx)
3495 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3497 id_to_widget.widgets[idx] = 0;
3498 --id_to_widget.used;
3502 /* Get the widget pointer at IDX from id_to_widget. */
3504 static GtkWidget *
3505 xg_get_widget_from_map (ptrdiff_t idx)
3507 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3508 return id_to_widget.widgets[idx];
3510 return 0;
3513 static void
3514 update_theme_scrollbar_width (void)
3516 #ifdef HAVE_GTK3
3517 GtkAdjustment *vadj;
3518 #else
3519 GtkObject *vadj;
3520 #endif
3521 GtkWidget *wscroll;
3522 int w = 0, b = 0;
3524 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3525 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3526 g_object_ref_sink (G_OBJECT (wscroll));
3527 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3528 gtk_widget_destroy (wscroll);
3529 g_object_unref (G_OBJECT (wscroll));
3530 w += 2*b;
3531 #ifndef HAVE_GTK3
3532 if (w < 16) w = 16;
3533 #endif
3534 scroll_bar_width_for_theme = w;
3537 static void
3538 update_theme_scrollbar_height (void)
3540 #ifdef HAVE_GTK3
3541 GtkAdjustment *hadj;
3542 #else
3543 GtkObject *hadj;
3544 #endif
3545 GtkWidget *wscroll;
3546 int w = 0, b = 0;
3548 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
3549 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3550 g_object_ref_sink (G_OBJECT (wscroll));
3551 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3552 gtk_widget_destroy (wscroll);
3553 g_object_unref (G_OBJECT (wscroll));
3554 w += 2*b;
3555 if (w < 12) w = 12;
3556 scroll_bar_height_for_theme = w;
3560 xg_get_default_scrollbar_width (void)
3562 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3566 xg_get_default_scrollbar_height (void)
3568 /* Apparently there's no default height for themes. */
3569 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3572 /* Return the scrollbar id for X Window WID on display DPY.
3573 Return -1 if WID not in id_to_widget. */
3575 ptrdiff_t
3576 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3578 ptrdiff_t idx;
3579 GtkWidget *w;
3581 w = xg_win_to_widget (dpy, wid);
3583 if (w)
3585 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3586 if (id_to_widget.widgets[idx] == w)
3587 return idx;
3590 return -1;
3593 /* Callback invoked when scroll bar WIDGET is destroyed.
3594 DATA is the index into id_to_widget for WIDGET.
3595 We free pointer to last scroll bar values here and remove the index. */
3597 static void
3598 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3600 intptr_t id = (intptr_t) data;
3601 xg_remove_widget_from_map (id);
3604 /* Create a scroll bar widget for frame F. Store the scroll bar
3605 in BAR.
3606 SCROLL_CALLBACK is the callback to invoke when the value of the
3607 bar changes.
3608 END_CALLBACK is the callback to invoke when scrolling ends.
3609 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3610 to set resources for the widget. */
3612 void
3613 xg_create_scroll_bar (struct frame *f,
3614 struct scroll_bar *bar,
3615 GCallback scroll_callback,
3616 GCallback end_callback,
3617 const char *scroll_bar_name)
3619 GtkWidget *wscroll;
3620 GtkWidget *webox;
3621 intptr_t scroll_id;
3622 #ifdef HAVE_GTK3
3623 GtkAdjustment *vadj;
3624 #else
3625 GtkObject *vadj;
3626 #endif
3628 /* Page, step increment values are not so important here, they
3629 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3630 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3631 0.1, 0.1, 0.1);
3633 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3634 webox = gtk_event_box_new ();
3635 gtk_widget_set_name (wscroll, scroll_bar_name);
3636 #ifndef HAVE_GTK3
3637 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3638 #endif
3639 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3641 scroll_id = xg_store_widget_in_map (wscroll);
3643 g_signal_connect (G_OBJECT (wscroll),
3644 "destroy",
3645 G_CALLBACK (xg_gtk_scroll_destroy),
3646 (gpointer) scroll_id);
3647 g_signal_connect (G_OBJECT (wscroll),
3648 "change-value",
3649 scroll_callback,
3650 (gpointer) bar);
3651 g_signal_connect (G_OBJECT (wscroll),
3652 "button-release-event",
3653 end_callback,
3654 (gpointer) bar);
3656 /* The scroll bar widget does not draw on a window of its own. Instead
3657 it draws on the parent window, in this case the edit widget. So
3658 whenever the edit widget is cleared, the scroll bar needs to redraw
3659 also, which causes flicker. Put an event box between the edit widget
3660 and the scroll bar, so the scroll bar instead draws itself on the
3661 event box window. */
3662 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3663 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3666 /* Set the cursor to an arrow. */
3667 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3669 bar->x_window = scroll_id;
3670 bar->horizontal = 0;
3673 /* Create a horizontal scroll bar widget for frame F. Store the scroll
3674 bar in BAR. SCROLL_CALLBACK is the callback to invoke when the value
3675 of the bar changes. END_CALLBACK is the callback to invoke when
3676 scrolling ends. SCROLL_BAR_NAME is the name we use for the scroll
3677 bar. Can be used to set resources for the widget. */
3679 void
3680 xg_create_horizontal_scroll_bar (struct frame *f,
3681 struct scroll_bar *bar,
3682 GCallback scroll_callback,
3683 GCallback end_callback,
3684 const char *scroll_bar_name)
3686 GtkWidget *wscroll;
3687 GtkWidget *webox;
3688 intptr_t scroll_id;
3689 #ifdef HAVE_GTK3
3690 GtkAdjustment *hadj;
3691 #else
3692 GtkObject *hadj;
3693 #endif
3695 /* Page, step increment values are not so important here, they
3696 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3697 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
3698 0.1, 0.1, 0.1);
3700 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3701 webox = gtk_event_box_new ();
3702 gtk_widget_set_name (wscroll, scroll_bar_name);
3703 #ifndef HAVE_GTK3
3704 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3705 #endif
3706 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3708 scroll_id = xg_store_widget_in_map (wscroll);
3710 g_signal_connect (G_OBJECT (wscroll),
3711 "destroy",
3712 G_CALLBACK (xg_gtk_scroll_destroy),
3713 (gpointer) scroll_id);
3714 g_signal_connect (G_OBJECT (wscroll),
3715 "change-value",
3716 scroll_callback,
3717 (gpointer) bar);
3718 g_signal_connect (G_OBJECT (wscroll),
3719 "button-release-event",
3720 end_callback,
3721 (gpointer) bar);
3723 /* The scroll bar widget does not draw on a window of its own. Instead
3724 it draws on the parent window, in this case the edit widget. So
3725 whenever the edit widget is cleared, the scroll bar needs to redraw
3726 also, which causes flicker. Put an event box between the edit widget
3727 and the scroll bar, so the scroll bar instead draws itself on the
3728 event box window. */
3729 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3730 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3733 /* Set the cursor to an arrow. */
3734 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3736 bar->x_window = scroll_id;
3737 bar->horizontal = 1;
3740 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3742 void
3743 xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
3745 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3746 if (w)
3748 GtkWidget *wparent = gtk_widget_get_parent (w);
3749 gtk_widget_destroy (w);
3750 gtk_widget_destroy (wparent);
3751 SET_FRAME_GARBAGED (f);
3755 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3756 in frame F.
3757 TOP/LEFT are the new pixel positions where the bar shall appear.
3758 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3760 void
3761 xg_update_scrollbar_pos (struct frame *f,
3762 ptrdiff_t scrollbar_id,
3763 int top,
3764 int left,
3765 int width,
3766 int height)
3768 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3769 if (wscroll)
3771 GtkWidget *wfixed = f->output_data.x->edit_widget;
3772 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3773 gint msl;
3774 int scale = xg_get_gdk_scale ();
3776 top /= scale;
3777 left /= scale;
3778 height /= scale;
3779 left -= (scale - 1) * ((width / scale) >> 1);
3781 /* Clear out old position. */
3782 int oldx = -1, oldy = -1, oldw, oldh;
3783 if (gtk_widget_get_parent (wparent) == wfixed)
3785 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3786 "x", &oldx, "y", &oldy, NULL);
3787 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3790 /* Move and resize to new values. */
3791 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3792 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3793 if (msl > height)
3795 /* No room. Hide scroll bar as some themes output a warning if
3796 the height is less than the min size. */
3797 gtk_widget_hide (wparent);
3798 gtk_widget_hide (wscroll);
3800 else
3802 gtk_widget_show_all (wparent);
3803 gtk_widget_set_size_request (wscroll, width, height);
3805 #ifndef USE_CAIRO
3806 gtk_widget_queue_draw (wfixed);
3807 gdk_window_process_all_updates ();
3808 #endif
3809 if (oldx != -1 && oldw > 0 && oldh > 0)
3811 /* Clear under old scroll bar position. This must be done after
3812 the gtk_widget_queue_draw and gdk_window_process_all_updates
3813 above. */
3814 oldw += (scale - 1) * oldw;
3815 oldx -= (scale - 1) * oldw;
3816 x_clear_area (f, oldx, oldy, oldw, oldh);
3819 /* GTK does not redraw until the main loop is entered again, but
3820 if there are no X events pending we will not enter it. So we sync
3821 here to get some events. */
3823 x_sync (f);
3824 SET_FRAME_GARBAGED (f);
3825 cancel_mouse_face (f);
3830 /* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
3831 in frame F.
3832 TOP/LEFT are the new pixel positions where the bar shall appear.
3833 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3835 void
3836 xg_update_horizontal_scrollbar_pos (struct frame *f,
3837 ptrdiff_t scrollbar_id,
3838 int top,
3839 int left,
3840 int width,
3841 int height)
3844 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3846 if (wscroll)
3848 GtkWidget *wfixed = f->output_data.x->edit_widget;
3849 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3850 gint msl;
3852 /* Clear out old position. */
3853 int oldx = -1, oldy = -1, oldw, oldh;
3854 if (gtk_widget_get_parent (wparent) == wfixed)
3856 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3857 "x", &oldx, "y", &oldy, NULL);
3858 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3861 /* Move and resize to new values. */
3862 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3863 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3864 if (msl > width)
3866 /* No room. Hide scroll bar as some themes output a warning if
3867 the width is less than the min size. */
3868 gtk_widget_hide (wparent);
3869 gtk_widget_hide (wscroll);
3871 else
3873 gtk_widget_show_all (wparent);
3874 gtk_widget_set_size_request (wscroll, width, height);
3876 gtk_widget_queue_draw (wfixed);
3877 gdk_window_process_all_updates ();
3878 if (oldx != -1 && oldw > 0 && oldh > 0)
3879 /* Clear under old scroll bar position. This must be done after
3880 the gtk_widget_queue_draw and gdk_window_process_all_updates
3881 above. */
3882 x_clear_area (f,
3883 oldx, oldy, oldw, oldh);
3885 /* GTK does not redraw until the main loop is entered again, but
3886 if there are no X events pending we will not enter it. So we sync
3887 here to get some events. */
3889 x_sync (f);
3890 SET_FRAME_GARBAGED (f);
3891 cancel_mouse_face (f);
3896 /* Get the current value of the range, truncated to an integer. */
3898 static int
3899 int_gtk_range_get_value (GtkRange *range)
3901 return gtk_range_get_value (range);
3905 /* Set the thumb size and position of scroll bar BAR. We are currently
3906 displaying PORTION out of a whole WHOLE, and our position POSITION. */
3908 void
3909 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
3910 int portion,
3911 int position,
3912 int whole)
3914 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3916 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
3918 if (wscroll && bar->dragging == -1)
3920 GtkAdjustment *adj;
3921 gdouble shown;
3922 gdouble top;
3923 int size, value;
3924 int old_size;
3925 int new_step;
3926 bool changed = 0;
3928 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3930 if (scroll_bar_adjust_thumb_portion_p)
3932 /* We do the same as for MOTIF in xterm.c, use 30 chars per
3933 line rather than the real portion value. This makes the
3934 thumb less likely to resize and that looks better. */
3935 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
3937 /* When the thumb is at the bottom, position == whole.
3938 So we need to increase `whole' to make space for the thumb. */
3939 whole += portion;
3942 if (whole <= 0)
3943 top = 0, shown = 1;
3944 else
3946 top = (gdouble) position / whole;
3947 shown = (gdouble) portion / whole;
3950 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
3951 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
3953 /* Assume all lines are of equal size. */
3954 new_step = size / max (1, FRAME_LINES (f));
3956 old_size = gtk_adjustment_get_page_size (adj);
3957 if (old_size != size)
3959 int old_step = gtk_adjustment_get_step_increment (adj);
3960 if (old_step != new_step)
3962 gtk_adjustment_set_page_size (adj, size);
3963 gtk_adjustment_set_step_increment (adj, new_step);
3964 /* Assume a page increment is about 95% of the page size */
3965 gtk_adjustment_set_page_increment (adj, size - size / 20);
3966 changed = 1;
3970 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3972 block_input ();
3974 /* gtk_range_set_value invokes the callback. Set
3975 ignore_gtk_scrollbar to make the callback do nothing */
3976 xg_ignore_gtk_scrollbar = 1;
3978 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3979 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3980 else if (changed)
3981 gtk_adjustment_changed (adj);
3983 xg_ignore_gtk_scrollbar = 0;
3985 unblock_input ();
3990 /* Set the thumb size and position of horizontal scroll bar BAR. We are
3991 currently displaying PORTION out of a whole WHOLE, and our position
3992 POSITION. */
3993 void
3994 xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
3995 int portion,
3996 int position,
3997 int whole)
3999 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
4001 if (wscroll && bar->dragging == -1)
4003 GtkAdjustment *adj;
4004 int lower = 0;
4005 int upper = max (whole - 1, 0);
4006 int pagesize = min (upper, max (portion, 0));
4007 int value = max (0, min (position, upper - pagesize));
4008 /* These should be set to something more <portion, whole>
4009 related. */
4010 int page_increment = 4;
4011 int step_increment = 1;
4013 block_input ();
4014 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4015 gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
4016 (gdouble) upper, (gdouble) step_increment,
4017 (gdouble) page_increment, (gdouble) pagesize);
4018 gtk_adjustment_changed (adj);
4019 unblock_input ();
4023 /* Return true if EVENT is for a scroll bar in frame F.
4024 When the same X window is used for several Gtk+ widgets, we cannot
4025 say for sure based on the X window alone if an event is for the
4026 frame. This function does additional checks. */
4028 bool
4029 xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
4031 bool retval = 0;
4033 if (f && event->type == ButtonPress && event->xbutton.button < 4)
4035 /* Check if press occurred outside the edit widget. */
4036 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
4037 GdkWindow *gwin;
4038 #ifdef HAVE_GTK3
4039 GdkDevice *gdev = gdk_device_manager_get_client_pointer
4040 (gdk_display_get_device_manager (gdpy));
4041 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
4042 #else
4043 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
4044 #endif
4045 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
4047 else if (f
4048 && ((event->type == ButtonRelease && event->xbutton.button < 4)
4049 || event->type == MotionNotify))
4051 /* If we are releasing or moving the scroll bar, it has the grab. */
4052 GtkWidget *w = gtk_grab_get_current ();
4053 retval = w != 0 && GTK_IS_SCROLLBAR (w);
4056 return retval;
4060 /***********************************************************************
4061 Printing
4062 ***********************************************************************/
4063 #ifdef USE_CAIRO
4064 static GtkPrintSettings *print_settings = NULL;
4065 static GtkPageSetup *page_setup = NULL;
4067 void
4068 xg_page_setup_dialog (void)
4070 GtkPageSetup *new_page_setup = NULL;
4072 if (print_settings == NULL)
4073 print_settings = gtk_print_settings_new ();
4074 new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
4075 print_settings);
4076 if (page_setup)
4077 g_object_unref (page_setup);
4078 page_setup = new_page_setup;
4081 Lisp_Object
4082 xg_get_page_setup (void)
4084 Lisp_Object result, orientation_symbol;
4085 GtkPageOrientation orientation;
4087 if (page_setup == NULL)
4088 page_setup = gtk_page_setup_new ();
4089 result = list4 (Fcons (Qleft_margin,
4090 make_float (gtk_page_setup_get_left_margin (page_setup,
4091 GTK_UNIT_POINTS))),
4092 Fcons (Qright_margin,
4093 make_float (gtk_page_setup_get_right_margin (page_setup,
4094 GTK_UNIT_POINTS))),
4095 Fcons (Qtop_margin,
4096 make_float (gtk_page_setup_get_top_margin (page_setup,
4097 GTK_UNIT_POINTS))),
4098 Fcons (Qbottom_margin,
4099 make_float (gtk_page_setup_get_bottom_margin (page_setup,
4100 GTK_UNIT_POINTS))));
4101 result = Fcons (Fcons (Qheight,
4102 make_float (gtk_page_setup_get_page_height (page_setup,
4103 GTK_UNIT_POINTS))),
4104 result);
4105 result = Fcons (Fcons (Qwidth,
4106 make_float (gtk_page_setup_get_page_width (page_setup,
4107 GTK_UNIT_POINTS))),
4108 result);
4109 orientation = gtk_page_setup_get_orientation (page_setup);
4110 if (orientation == GTK_PAGE_ORIENTATION_PORTRAIT)
4111 orientation_symbol = Qportrait;
4112 else if (orientation == GTK_PAGE_ORIENTATION_LANDSCAPE)
4113 orientation_symbol = Qlandscape;
4114 else if (orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
4115 orientation_symbol = Qreverse_portrait;
4116 else if (orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)
4117 orientation_symbol = Qreverse_landscape;
4118 result = Fcons (Fcons (Qorientation, orientation_symbol), result);
4120 return result;
4123 static void
4124 draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
4125 gint page_nr, gpointer user_data)
4127 Lisp_Object frames = *((Lisp_Object *) user_data);
4128 struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
4129 cairo_t *cr = gtk_print_context_get_cairo_context (context);
4131 x_cr_draw_frame (cr, f);
4134 void
4135 xg_print_frames_dialog (Lisp_Object frames)
4137 GtkPrintOperation *print;
4138 GtkPrintOperationResult res;
4140 print = gtk_print_operation_new ();
4141 if (print_settings != NULL)
4142 gtk_print_operation_set_print_settings (print, print_settings);
4143 if (page_setup != NULL)
4144 gtk_print_operation_set_default_page_setup (print, page_setup);
4145 gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
4146 g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
4147 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
4148 NULL, NULL);
4149 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
4151 if (print_settings != NULL)
4152 g_object_unref (print_settings);
4153 print_settings =
4154 g_object_ref (gtk_print_operation_get_print_settings (print));
4156 g_object_unref (print);
4159 #endif /* USE_CAIRO */
4163 /***********************************************************************
4164 Tool bar functions
4165 ***********************************************************************/
4166 /* The key for the data we put in the GtkImage widgets. The data is
4167 the image used by Emacs. We use this to see if we need to update
4168 the GtkImage with a new image. */
4169 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
4171 /* The key for storing the latest modifiers so the activate callback can
4172 get them. */
4173 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
4175 /* The key for the data we put in the GtkImage widgets. The data is
4176 the stock name used by Emacs. We use this to see if we need to update
4177 the GtkImage with a new image. */
4178 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
4180 /* As above, but this is used for named theme widgets, as opposed to
4181 stock items. */
4182 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
4184 /* Callback function invoked when a tool bar item is pressed.
4185 W is the button widget in the tool bar that got pressed,
4186 CLIENT_DATA is an integer that is the index of the button in the
4187 tool bar. 0 is the first button. */
4189 static gboolean
4190 xg_tool_bar_button_cb (GtkWidget *widget,
4191 GdkEventButton *event,
4192 gpointer user_data)
4194 intptr_t state = event->state;
4195 gpointer ptr = (gpointer) state;
4196 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
4197 return FALSE;
4201 /* Callback function invoked when a tool bar item is pressed.
4202 W is the button widget in the tool bar that got pressed,
4203 CLIENT_DATA is an integer that is the index of the button in the
4204 tool bar. 0 is the first button. */
4206 static void
4207 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
4209 intptr_t idx = (intptr_t) client_data;
4210 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
4211 intptr_t mod = (intptr_t) gmod;
4213 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4214 Lisp_Object key, frame;
4215 struct input_event event;
4216 EVENT_INIT (event);
4218 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4219 return;
4221 idx *= TOOL_BAR_ITEM_NSLOTS;
4223 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
4224 XSETFRAME (frame, f);
4226 /* We generate two events here. The first one is to set the prefix
4227 to `(tool_bar)', see keyboard.c. */
4228 event.kind = TOOL_BAR_EVENT;
4229 event.frame_or_window = frame;
4230 event.arg = frame;
4231 kbd_buffer_store_event (&event);
4233 event.kind = TOOL_BAR_EVENT;
4234 event.frame_or_window = frame;
4235 event.arg = key;
4236 /* Convert between the modifier bits GDK uses and the modifier bits
4237 Emacs uses. This assumes GDK and X masks are the same, which they are when
4238 this is written. */
4239 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
4240 kbd_buffer_store_event (&event);
4242 /* Return focus to the frame after we have clicked on a detached
4243 tool bar button. */
4244 x_focus_frame (f);
4247 static GtkWidget *
4248 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4250 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
4251 GtkWidget *c1 = clist->data;
4252 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
4254 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4255 g_list_free (clist);
4256 return GTK_IS_LABEL (c1) ? c1 : c2;
4260 /* This callback is called when the mouse enters or leaves a tool bar item.
4261 It is used for displaying and hiding the help text.
4262 W is the tool bar item, a button.
4263 EVENT is either an enter event or leave event.
4264 CLIENT_DATA is an integer that is the index of the button in the
4265 tool bar. 0 is the first button.
4267 Returns FALSE to tell GTK to keep processing this event. */
4269 static gboolean
4270 xg_tool_bar_help_callback (GtkWidget *w,
4271 GdkEventCrossing *event,
4272 gpointer client_data)
4274 intptr_t idx = (intptr_t) client_data;
4275 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4276 Lisp_Object help, frame;
4278 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4279 return FALSE;
4281 if (event->type == GDK_ENTER_NOTIFY)
4283 idx *= TOOL_BAR_ITEM_NSLOTS;
4284 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4286 if (NILP (help))
4287 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4289 else
4290 help = Qnil;
4292 XSETFRAME (frame, f);
4293 kbd_buffer_store_help_event (frame, help);
4295 return FALSE;
4299 /* This callback is called when a tool bar item shall be redrawn.
4300 It modifies the expose event so that the GtkImage widget redraws the
4301 whole image. This to overcome a bug that makes GtkImage draw the image
4302 in the wrong place when it tries to redraw just a part of the image.
4303 W is the GtkImage to be redrawn.
4304 EVENT is the expose event for W.
4305 CLIENT_DATA is unused.
4307 Returns FALSE to tell GTK to keep processing this event. */
4309 #ifndef HAVE_GTK3
4310 static gboolean
4311 xg_tool_bar_item_expose_callback (GtkWidget *w,
4312 GdkEventExpose *event,
4313 gpointer client_data)
4315 gint width, height;
4317 gdk_drawable_get_size (event->window, &width, &height);
4318 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4319 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4321 event->area.x = max (0, event->area.x);
4322 event->area.y = max (0, event->area.y);
4324 event->area.width = max (width, event->area.width);
4325 event->area.height = max (height, event->area.height);
4327 return FALSE;
4329 #endif
4331 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4332 #define toolbar_set_orientation(w, o) \
4333 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4334 #else
4335 #define toolbar_set_orientation(w, o) \
4336 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4337 #endif
4339 /* Attach a tool bar to frame F. */
4341 static void
4342 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
4344 struct x_output *x = f->output_data.x;
4345 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4346 GtkWidget *top_widget = x->toolbar_widget;
4348 toolbar_set_orientation (x->toolbar_widget,
4349 into_hbox
4350 ? GTK_ORIENTATION_VERTICAL
4351 : GTK_ORIENTATION_HORIZONTAL);
4353 if (into_hbox)
4355 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
4356 FALSE, FALSE, 0);
4358 if (EQ (pos, Qleft))
4359 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4360 top_widget,
4362 x->toolbar_in_hbox = true;
4364 else
4366 bool vbox_pos = x->menubar_widget != 0;
4367 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
4368 FALSE, FALSE, 0);
4370 if (EQ (pos, Qtop))
4371 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4372 top_widget,
4373 vbox_pos);
4374 x->toolbar_in_hbox = false;
4376 x->toolbar_is_packed = true;
4379 static bool xg_update_tool_bar_sizes (struct frame *f);
4381 static void
4382 tb_size_cb (GtkWidget *widget,
4383 GdkRectangle *allocation,
4384 gpointer user_data)
4386 /* When tool bar is created it has one preferred size. But when size is
4387 allocated between widgets, it may get another. So we must update
4388 size hints if tool bar size changes. Seen on Fedora 18 at least. */
4389 struct frame *f = user_data;
4391 if (xg_update_tool_bar_sizes (f))
4393 frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
4394 adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
4398 /* Create a tool bar for frame F. */
4400 static void
4401 xg_create_tool_bar (struct frame *f)
4403 struct x_output *x = f->output_data.x;
4404 #if GTK_CHECK_VERSION (3, 3, 6)
4405 GtkStyleContext *gsty;
4406 #endif
4407 struct xg_frame_tb_info *tbinfo
4408 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4409 TB_INFO_KEY);
4410 if (! tbinfo)
4412 tbinfo = xmalloc (sizeof (*tbinfo));
4413 tbinfo->last_tool_bar = Qnil;
4414 tbinfo->style = Qnil;
4415 tbinfo->hmargin = tbinfo->vmargin = 0;
4416 tbinfo->dir = GTK_TEXT_DIR_NONE;
4417 tbinfo->n_last_items = 0;
4418 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4419 TB_INFO_KEY,
4420 tbinfo);
4423 x->toolbar_widget = gtk_toolbar_new ();
4425 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4427 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4428 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4429 g_signal_connect (x->toolbar_widget, "size-allocate",
4430 G_CALLBACK (tb_size_cb), f);
4431 #if GTK_CHECK_VERSION (3, 3, 6)
4432 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4433 gtk_style_context_add_class (gsty, "primary-toolbar");
4434 #endif
4438 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4440 /* Find the right-to-left image named by RTL in the tool bar images for F.
4441 Returns IMAGE if RTL is not found. */
4443 static Lisp_Object
4444 find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
4446 int i;
4447 Lisp_Object file, rtl_name;
4449 rtl_name = Ffile_name_nondirectory (rtl);
4451 for (i = 0; i < f->n_tool_bar_items; ++i)
4453 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4454 if (!NILP (file = file_for_image (rtl_image)))
4456 file = call1 (intern ("file-name-sans-extension"),
4457 Ffile_name_nondirectory (file));
4458 if (! NILP (Fequal (file, rtl_name)))
4460 image = rtl_image;
4461 break;
4466 return image;
4469 static GtkToolItem *
4470 xg_make_tool_item (struct frame *f,
4471 GtkWidget *wimage,
4472 GtkWidget **wbutton,
4473 const char *label,
4474 int i, bool horiz, bool text_image)
4476 GtkToolItem *ti = gtk_tool_item_new ();
4477 GtkWidget *vb = gtk_box_new (horiz
4478 ? GTK_ORIENTATION_HORIZONTAL
4479 : GTK_ORIENTATION_VERTICAL,
4481 GtkWidget *wb = gtk_button_new ();
4482 /* The eventbox is here so we can have tooltips on disabled items. */
4483 GtkWidget *weventbox = gtk_event_box_new ();
4484 #if GTK_CHECK_VERSION (3, 3, 6)
4485 GtkCssProvider *css_prov = gtk_css_provider_new ();
4486 GtkStyleContext *gsty;
4488 gtk_css_provider_load_from_data (css_prov,
4489 "GtkEventBox {"
4490 " background-color: transparent;"
4491 "}",
4492 -1, NULL);
4494 gsty = gtk_widget_get_style_context (weventbox);
4495 gtk_style_context_add_provider (gsty,
4496 GTK_STYLE_PROVIDER (css_prov),
4497 GTK_STYLE_PROVIDER_PRIORITY_USER);
4498 g_object_unref (css_prov);
4499 #endif
4501 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4503 if (wimage && !text_image)
4504 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4505 if (label)
4506 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4507 if (wimage && text_image)
4508 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4510 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4511 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4512 gtk_container_add (GTK_CONTAINER (wb), vb);
4513 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4514 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4516 if (wimage || label)
4518 intptr_t ii = i;
4519 gpointer gi = (gpointer) ii;
4521 g_signal_connect (G_OBJECT (wb), "clicked",
4522 G_CALLBACK (xg_tool_bar_callback),
4523 gi);
4525 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4527 #ifndef HAVE_GTK3
4528 /* Catch expose events to overcome an annoying redraw bug, see
4529 comment for xg_tool_bar_item_expose_callback. */
4530 g_signal_connect (G_OBJECT (ti),
4531 "expose-event",
4532 G_CALLBACK (xg_tool_bar_item_expose_callback),
4534 #endif
4535 gtk_tool_item_set_homogeneous (ti, FALSE);
4537 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4538 no distinction based on modifiers in the activate callback,
4539 so we have to do it ourselves. */
4540 g_signal_connect (wb, "button-release-event",
4541 G_CALLBACK (xg_tool_bar_button_cb),
4542 NULL);
4544 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4546 /* Use enter/leave notify to show help. We use the events
4547 rather than the GtkButton specific signals "enter" and
4548 "leave", so we can have only one callback. The event
4549 will tell us what kind of event it is. */
4550 g_signal_connect (G_OBJECT (weventbox),
4551 "enter-notify-event",
4552 G_CALLBACK (xg_tool_bar_help_callback),
4553 gi);
4554 g_signal_connect (G_OBJECT (weventbox),
4555 "leave-notify-event",
4556 G_CALLBACK (xg_tool_bar_help_callback),
4557 gi);
4560 if (wbutton) *wbutton = wb;
4562 return ti;
4565 static bool
4566 is_box_type (GtkWidget *vb, bool is_horizontal)
4568 #ifdef HAVE_GTK3
4569 bool ret = 0;
4570 if (GTK_IS_BOX (vb))
4572 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4573 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4574 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4576 return ret;
4577 #else
4578 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4579 #endif
4583 static bool
4584 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4585 const char *icon_name, const struct image *img,
4586 const char *label, bool horiz)
4588 gpointer old;
4589 GtkWidget *wimage;
4590 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4591 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4593 /* Check if the tool icon matches. */
4594 if (stock_name && wimage)
4596 old = g_object_get_data (G_OBJECT (wimage),
4597 XG_TOOL_BAR_STOCK_NAME);
4598 if (!old || strcmp (old, stock_name))
4599 return 1;
4601 else if (icon_name && wimage)
4603 old = g_object_get_data (G_OBJECT (wimage),
4604 XG_TOOL_BAR_ICON_NAME);
4605 if (!old || strcmp (old, icon_name))
4606 return 1;
4608 else if (wimage)
4610 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4611 XG_TOOL_BAR_IMAGE_DATA);
4612 Pixmap old_img = (Pixmap) gold_img;
4613 if (old_img != img->pixmap)
4614 return 1;
4617 /* Check button configuration and label. */
4618 if (is_box_type (vb, horiz)
4619 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4620 return 1;
4622 /* Ensure label is correct. */
4623 if (label && wlbl)
4624 gtk_label_set_text (GTK_LABEL (wlbl), label);
4625 return 0;
4628 static bool
4629 xg_update_tool_bar_sizes (struct frame *f)
4631 struct x_output *x = f->output_data.x;
4632 GtkRequisition req;
4633 int nl = 0, nr = 0, nt = 0, nb = 0;
4634 GtkWidget *top_widget = x->toolbar_widget;
4636 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
4637 if (x->toolbar_in_hbox)
4639 int pos;
4640 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4641 top_widget,
4642 "position", &pos, NULL);
4643 if (pos == 0) nl = req.width;
4644 else nr = req.width;
4646 else
4648 int pos;
4649 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4650 top_widget,
4651 "position", &pos, NULL);
4652 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4653 else nb = req.height;
4656 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4657 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4658 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4659 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4661 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4662 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4663 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4664 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4665 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4666 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4668 return true;
4670 else
4671 return false;
4674 static char *
4675 find_icon_from_name (char *name,
4676 GtkIconTheme *icon_theme,
4677 char **icon_name)
4679 #if ! GTK_CHECK_VERSION (3, 10, 0)
4680 GtkStockItem stock_item;
4681 #endif
4683 if (name[0] == 'n' && name[1] == ':')
4685 *icon_name = name + 2;
4686 name = NULL;
4688 if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
4689 *icon_name = NULL;
4692 #if ! GTK_CHECK_VERSION (3, 10, 0)
4693 else if (gtk_stock_lookup (name, &stock_item))
4694 *icon_name = NULL;
4695 #endif
4696 else if (gtk_icon_theme_has_icon (icon_theme, name))
4698 *icon_name = name;
4699 name = NULL;
4701 else
4703 name = NULL;
4704 *icon_name = NULL;
4707 return name;
4711 /* Update the tool bar for frame F. Add new buttons and remove old. */
4713 void
4714 update_frame_tool_bar (struct frame *f)
4716 int i, j;
4717 struct x_output *x = f->output_data.x;
4718 int hmargin = 0, vmargin = 0;
4719 GtkToolbar *wtoolbar;
4720 GtkToolItem *ti;
4721 GtkTextDirection dir;
4722 Lisp_Object style;
4723 bool text_image, horiz;
4724 struct xg_frame_tb_info *tbinfo;
4725 GdkScreen *screen;
4726 GtkIconTheme *icon_theme;
4729 if (! FRAME_GTK_WIDGET (f))
4730 return;
4732 block_input ();
4734 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4736 hmargin = XFASTINT (Vtool_bar_button_margin);
4737 vmargin = XFASTINT (Vtool_bar_button_margin);
4739 else if (CONSP (Vtool_bar_button_margin))
4741 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4742 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4744 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4745 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4748 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4749 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4750 i.e. zero. This means that margins less than
4751 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4752 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4753 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4755 if (! x->toolbar_widget)
4756 xg_create_tool_bar (f);
4758 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4759 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4761 style = Ftool_bar_get_system_style ();
4762 screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4763 icon_theme = gtk_icon_theme_get_for_screen (screen);
4765 /* Are we up to date? */
4766 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4767 TB_INFO_KEY);
4769 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4770 && tbinfo->n_last_items == f->n_tool_bar_items
4771 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4772 && tbinfo->dir == dir
4773 && ! NILP (Fequal (tbinfo->style, style))
4774 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4776 unblock_input ();
4777 return;
4780 tbinfo->last_tool_bar = f->tool_bar_items;
4781 tbinfo->n_last_items = f->n_tool_bar_items;
4782 tbinfo->style = style;
4783 tbinfo->hmargin = hmargin;
4784 tbinfo->vmargin = vmargin;
4785 tbinfo->dir = dir;
4787 text_image = EQ (style, Qtext_image_horiz);
4788 horiz = EQ (style, Qboth_horiz) || text_image;
4790 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4792 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4793 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4794 int idx;
4795 ptrdiff_t img_id;
4796 int icon_size = 0;
4797 struct image *img = NULL;
4798 Lisp_Object image;
4799 Lisp_Object stock = Qnil;
4800 char *stock_name = NULL;
4801 char *icon_name = NULL;
4802 Lisp_Object rtl;
4803 GtkWidget *wbutton = NULL;
4804 Lisp_Object specified_file;
4805 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4806 const char *label
4807 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4808 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4809 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4810 : "";
4812 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4814 /* If this is a separator, use a gtk separator item. */
4815 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4817 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4819 if (ti)
4820 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4821 GTK_WIDGET (ti));
4822 ti = gtk_separator_tool_item_new ();
4823 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4825 j++;
4826 continue;
4829 /* Otherwise, the tool-bar item is an ordinary button. */
4831 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4833 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4834 ti = NULL;
4837 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4839 /* Ignore invalid image specifications. */
4840 image = PROP (TOOL_BAR_ITEM_IMAGES);
4841 if (!valid_image_p (image))
4843 if (ti)
4844 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4845 GTK_WIDGET (ti));
4846 continue;
4849 specified_file = file_for_image (image);
4850 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4851 stock = call1 (Qx_gtk_map_stock, specified_file);
4853 if (CONSP (stock))
4855 Lisp_Object tem;
4856 for (tem = stock; CONSP (tem); tem = XCDR (tem))
4857 if (! NILP (tem) && STRINGP (XCAR (tem)))
4859 stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
4860 icon_theme,
4861 &icon_name);
4862 if (stock_name || icon_name) break;
4865 else if (STRINGP (stock))
4867 stock_name = find_icon_from_name (SSDATA (stock),
4868 icon_theme,
4869 &icon_name);
4872 if (stock_name || icon_name)
4873 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4875 if (stock_name == NULL && icon_name == NULL)
4877 /* No stock image, or stock item not known. Try regular
4878 image. If image is a vector, choose it according to the
4879 button state. */
4880 if (dir == GTK_TEXT_DIR_RTL
4881 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4882 && STRINGP (rtl))
4883 image = find_rtl_image (f, image, rtl);
4885 if (VECTORP (image))
4887 if (enabled_p)
4888 idx = (selected_p
4889 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4890 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4891 else
4892 idx = (selected_p
4893 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4894 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
4896 eassert (ASIZE (image) >= idx);
4897 image = AREF (image, idx);
4899 else
4900 idx = -1;
4902 img_id = lookup_image (f, image);
4903 img = IMAGE_FROM_ID (f, img_id);
4904 prepare_image_for_display (f, img);
4906 if (img->load_failed_p || img->pixmap == None)
4908 if (ti)
4909 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4910 GTK_WIDGET (ti));
4911 continue;
4915 /* If there is an existing widget, check if it's stale; if so,
4916 remove it and make a new tool item from scratch. */
4917 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
4918 img, label, horiz))
4920 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4921 GTK_WIDGET (ti));
4922 ti = NULL;
4925 if (ti == NULL)
4927 GtkWidget *w;
4929 /* Save the image so we can see if an update is needed the
4930 next time we call xg_tool_item_match_p. */
4931 if (EQ (style, Qtext))
4932 w = NULL;
4933 else if (stock_name)
4936 #if GTK_CHECK_VERSION (3, 10, 0)
4937 w = gtk_image_new_from_icon_name (stock_name, icon_size);
4938 #else
4939 w = gtk_image_new_from_stock (stock_name, icon_size);
4940 #endif
4941 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
4942 (gpointer) xstrdup (stock_name),
4943 (GDestroyNotify) xfree);
4945 else if (icon_name)
4947 w = gtk_image_new_from_icon_name (icon_name, icon_size);
4948 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
4949 (gpointer) xstrdup (icon_name),
4950 (GDestroyNotify) xfree);
4952 else
4954 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
4955 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
4956 (gpointer)img->pixmap);
4959 #if GTK_CHECK_VERSION (3, 14, 0)
4960 if (w)
4962 gtk_widget_set_margin_start (w, hmargin);
4963 gtk_widget_set_margin_end (w, hmargin);
4964 gtk_widget_set_margin_top (w, vmargin);
4965 gtk_widget_set_margin_bottom (w, vmargin);
4967 #else
4968 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
4969 #endif
4970 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
4971 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4974 #undef PROP
4976 gtk_widget_set_sensitive (wbutton, enabled_p);
4977 j++;
4980 /* Remove buttons not longer needed. */
4983 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4984 if (ti)
4985 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4986 } while (ti != NULL);
4988 if (f->n_tool_bar_items != 0)
4990 if (! x->toolbar_is_packed)
4991 xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
4992 gtk_widget_show_all (x->toolbar_widget);
4993 if (xg_update_tool_bar_sizes (f))
4995 int inhibit
4996 = ((f->after_make_frame
4997 && !f->tool_bar_resized
4998 && (EQ (frame_inhibit_implied_resize, Qt)
4999 || (CONSP (frame_inhibit_implied_resize)
5000 && !NILP (Fmemq (Qtool_bar_lines,
5001 frame_inhibit_implied_resize))))
5002 /* This will probably fail to DTRT in the
5003 fullheight/-width cases. */
5004 && NILP (get_frame_param (f, Qfullscreen)))
5006 : 2);
5008 frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
5009 adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
5011 f->tool_bar_resized = f->tool_bar_redisplayed;
5014 unblock_input ();
5017 /* Deallocate all resources for the tool bar on frame F.
5018 Remove the tool bar. */
5020 void
5021 free_frame_tool_bar (struct frame *f)
5023 struct x_output *x = f->output_data.x;
5025 if (x->toolbar_widget)
5027 struct xg_frame_tb_info *tbinfo;
5028 GtkWidget *top_widget = x->toolbar_widget;
5030 block_input ();
5031 if (x->toolbar_is_packed)
5033 if (x->toolbar_in_hbox)
5034 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5035 top_widget);
5036 else
5037 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5038 top_widget);
5040 else
5041 gtk_widget_destroy (x->toolbar_widget);
5043 x->toolbar_widget = 0;
5044 x->toolbar_widget = 0;
5045 x->toolbar_is_packed = false;
5046 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
5047 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
5049 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5050 TB_INFO_KEY);
5051 if (tbinfo)
5053 xfree (tbinfo);
5054 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5055 TB_INFO_KEY,
5056 NULL);
5059 frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
5060 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5062 unblock_input ();
5066 void
5067 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
5069 struct x_output *x = f->output_data.x;
5070 GtkWidget *top_widget = x->toolbar_widget;
5072 if (! x->toolbar_widget || ! top_widget)
5073 return;
5075 block_input ();
5076 g_object_ref (top_widget);
5077 if (x->toolbar_is_packed)
5079 if (x->toolbar_in_hbox)
5080 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5081 top_widget);
5082 else
5083 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5084 top_widget);
5087 xg_pack_tool_bar (f, pos);
5088 g_object_unref (top_widget);
5090 if (xg_update_tool_bar_sizes (f))
5092 frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
5093 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5097 unblock_input ();
5102 /***********************************************************************
5103 Initializing
5104 ***********************************************************************/
5105 void
5106 xg_initialize (void)
5108 GtkBindingSet *binding_set;
5109 GtkSettings *settings;
5111 #if HAVE_XFT
5112 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5113 we keep it permanently linked in. */
5114 XftInit (0);
5115 #endif
5117 gdpy_def = NULL;
5118 xg_ignore_gtk_scrollbar = 0;
5119 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5120 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5122 id_to_widget.max_size = id_to_widget.used = 0;
5123 id_to_widget.widgets = 0;
5125 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5126 (gdk_display_get_default ()));
5127 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5128 bindings. It doesn't seem to be any way to remove properties,
5129 so we set it to "" which in means "no key". */
5130 gtk_settings_set_string_property (settings,
5131 "gtk-menu-bar-accel",
5133 EMACS_CLASS);
5135 /* Make GTK text input widgets use Emacs style keybindings. This is
5136 Emacs after all. */
5137 gtk_settings_set_string_property (settings,
5138 "gtk-key-theme-name",
5139 "Emacs",
5140 EMACS_CLASS);
5142 /* Make dialogs close on C-g. Since file dialog inherits from
5143 dialog, this works for them also. */
5144 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
5145 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5146 "close", 0);
5148 /* Make menus close on C-g. */
5149 binding_set = gtk_binding_set_by_class (g_type_class_ref
5150 (GTK_TYPE_MENU_SHELL));
5151 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5152 "cancel", 0);
5153 update_theme_scrollbar_width ();
5154 update_theme_scrollbar_height ();
5156 #ifdef HAVE_FREETYPE
5157 x_last_font_name = NULL;
5158 #endif
5161 #endif /* USE_GTK */