c:/Temp/gtk-window-move/ChangeLog.txt
[emacs.git] / src / gtkutil.c
blob63f014364132597f47bdda4c2408a8d45e4381ec
1 /* Functions for creating and updating GTK widgets.
3 Copyright (C) 2003-2017 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #ifdef USE_GTK
23 #include <float.h>
24 #include <stdio.h>
26 #include <c-ctype.h>
28 #include "lisp.h"
29 #include "dispextern.h"
30 #include "frame.h"
31 #include "systime.h"
32 #include "xterm.h"
33 #include "blockinput.h"
34 #include "window.h"
35 #include "gtkutil.h"
36 #include "termhooks.h"
37 #include "keyboard.h"
38 #include "coding.h"
40 #include <gdk/gdkkeysyms.h>
42 #ifdef HAVE_XFT
43 #include <X11/Xft/Xft.h>
44 #endif
46 #ifdef HAVE_GTK3
47 #include <gtk/gtkx.h>
48 #include "emacsgtkfixed.h"
49 #endif
51 #ifdef HAVE_XDBE
52 #include <X11/extensions/Xdbe.h>
53 #endif
55 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
56 #define gtk_widget_set_has_window(w, b) \
57 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
58 #endif
59 #ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
60 #define gtk_dialog_get_action_area(w) ((w)->action_area)
61 #define gtk_dialog_get_content_area(w) ((w)->vbox)
62 #endif
63 #ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
64 #define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
65 #endif
66 #ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
67 #define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
68 #define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
69 #define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
70 #define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
71 #endif
72 #if GTK_CHECK_VERSION (2, 12, 0)
73 #define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
74 #else
75 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
76 #endif
78 #if ! GTK_CHECK_VERSION (2, 14, 0)
79 #define gtk_adjustment_configure(adj, xvalue, xlower, \
80 xupper, xstep_increment, \
81 xpage_increment, xpagesize) \
82 do { \
83 adj->lower = xlower; \
84 adj->upper = xupper; \
85 adj->page_size = xpagesize; \
86 gtk_adjustment_set_value (adj, xvalue); \
87 adj->page_increment = xpage_increment; \
88 adj->step_increment = xstep_increment; \
89 } while (0)
90 #endif /* < Gtk+ 2.14 */
92 #ifdef HAVE_FREETYPE
93 #if GTK_CHECK_VERSION (3, 2, 0)
94 #define USE_NEW_GTK_FONT_CHOOSER 1
95 #else
96 #define USE_NEW_GTK_FONT_CHOOSER 0
97 #define gtk_font_chooser_dialog_new(x, y) \
98 gtk_font_selection_dialog_new (x)
99 #undef GTK_FONT_CHOOSER
100 #define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
101 #define gtk_font_chooser_set_font(x, y) \
102 gtk_font_selection_dialog_set_font_name (x, y)
103 #endif
104 #endif /* HAVE_FREETYPE */
106 #if GTK_CHECK_VERSION (3, 10, 0)
107 #define XG_TEXT_CANCEL "Cancel"
108 #define XG_TEXT_OK "OK"
109 #define XG_TEXT_OPEN "Open"
110 #else
111 #define XG_TEXT_CANCEL GTK_STOCK_CANCEL
112 #define XG_TEXT_OK GTK_STOCK_OK
113 #define XG_TEXT_OPEN GTK_STOCK_OPEN
114 #endif
116 #ifndef HAVE_GTK3
117 #ifdef USE_GTK_TOOLTIP
118 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
119 #endif
120 #define gdk_window_get_geometry(w, a, b, c, d) \
121 gdk_window_get_geometry (w, a, b, c, d, 0)
122 #define gdk_x11_window_lookup_for_display(d, w) \
123 gdk_xid_table_lookup_for_display (d, w)
124 #define gtk_box_new(ori, spacing) \
125 ((ori) == GTK_ORIENTATION_HORIZONTAL \
126 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
127 #define gtk_scrollbar_new(ori, spacing) \
128 ((ori) == GTK_ORIENTATION_HORIZONTAL \
129 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
130 #ifndef GDK_KEY_g
131 #define GDK_KEY_g GDK_g
132 #endif
133 #endif /* HAVE_GTK3 */
135 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
137 static void update_theme_scrollbar_width (void);
138 static void update_theme_scrollbar_height (void);
140 #define TB_INFO_KEY "xg_frame_tb_info"
141 struct xg_frame_tb_info
143 Lisp_Object last_tool_bar;
144 Lisp_Object style;
145 int n_last_items;
146 int hmargin, vmargin;
147 GtkTextDirection dir;
150 static GtkWidget * xg_get_widget_from_map (ptrdiff_t idx);
153 /***********************************************************************
154 Display handling functions
155 ***********************************************************************/
157 /* Keep track of the default display, or NULL if there is none. Emacs
158 may close all its displays. */
160 static GdkDisplay *gdpy_def;
162 /* When the GTK widget W is to be created on a display for F that
163 is not the default display, set the display for W.
164 W can be a GtkMenu or a GtkWindow widget. */
166 static void
167 xg_set_screen (GtkWidget *w, struct frame *f)
169 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
171 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
172 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
174 if (GTK_IS_MENU (w))
175 gtk_menu_set_screen (GTK_MENU (w), gscreen);
176 else
177 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
182 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
183 *DPY is set to NULL if the display can't be opened.
185 Returns non-zero if display could be opened, zero if display could not
186 be opened, and less than zero if the GTK version doesn't support
187 multiple displays. */
189 void
190 xg_display_open (char *display_name, Display **dpy)
192 GdkDisplay *gdpy;
194 unrequest_sigio (); /* See comment in x_display_ok, xterm.c. */
195 gdpy = gdk_display_open (display_name);
196 request_sigio ();
197 if (!gdpy_def && gdpy)
199 gdpy_def = gdpy;
200 gdk_display_manager_set_default_display (gdk_display_manager_get (),
201 gdpy);
204 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
208 /* Close display DPY. */
210 void
211 xg_display_close (Display *dpy)
213 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
215 /* If this is the default display, try to change it before closing.
216 If there is no other display to use, gdpy_def is set to NULL, and
217 the next call to xg_display_open resets the default display. */
218 if (gdk_display_get_default () == gdpy)
220 struct x_display_info *dpyinfo;
221 GdkDisplay *gdpy_new = NULL;
223 /* Find another display. */
224 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
225 if (dpyinfo->display != dpy)
227 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
228 gdk_display_manager_set_default_display (gdk_display_manager_get (),
229 gdpy_new);
230 break;
232 gdpy_def = gdpy_new;
235 #if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
236 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
237 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
238 can continue running, but there will be memory leaks. */
239 g_object_run_dispose (G_OBJECT (gdpy));
240 #else
241 /* This seems to be fixed in GTK 2.10. */
242 gdk_display_close (gdpy);
243 #endif
247 /***********************************************************************
248 Utility functions
249 ***********************************************************************/
251 /* Create and return the cursor to be used for popup menus and
252 scroll bars on display DPY. */
254 GdkCursor *
255 xg_create_default_cursor (Display *dpy)
257 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
258 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
261 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
263 static GdkPixbuf *
264 xg_get_pixbuf_from_pix_and_mask (struct frame *f,
265 Pixmap pix,
266 Pixmap mask)
268 GdkPixbuf *icon_buf = 0;
269 int iunused;
270 Window wunused;
271 unsigned int width, height, depth, uunused;
273 if (FRAME_DISPLAY_INFO (f)->red_bits != 8)
274 return 0;
275 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
276 &width, &height, &uunused, &depth);
277 if (depth != 24)
278 return 0;
279 XImage *xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
280 ~0, XYPixmap);
281 if (xim)
283 XImage *xmm = (! mask ? 0
284 : XGetImage (FRAME_X_DISPLAY (f), mask, 0, 0,
285 width, height, ~0, XYPixmap));
286 icon_buf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
287 if (icon_buf)
289 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
290 int rowjunkwidth = gdk_pixbuf_get_rowstride (icon_buf) - width * 4;
291 for (int y = 0; y < height; y++, pixels += rowjunkwidth)
292 for (int x = 0; x < width; x++)
294 unsigned long rgb = XGetPixel (xim, x, y);
295 *pixels++ = (rgb >> 16) & 255;
296 *pixels++ = (rgb >> 8) & 255;
297 *pixels++ = rgb & 255;
298 *pixels++ = xmm && !XGetPixel (xmm, x, y) ? 0 : 255;
302 if (xmm)
303 XDestroyImage (xmm);
304 XDestroyImage (xim);
307 return icon_buf;
310 static Lisp_Object
311 file_for_image (Lisp_Object image)
313 Lisp_Object specified_file = Qnil;
314 Lisp_Object tail;
316 for (tail = XCDR (image);
317 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
318 tail = XCDR (XCDR (tail)))
319 if (EQ (XCAR (tail), QCfile))
320 specified_file = XCAR (XCDR (tail));
322 return specified_file;
325 /* For the image defined in IMG, make and return a GtkImage. For displays with
326 8 planes or less we must make a GdkPixbuf and apply the mask manually.
327 Otherwise the highlighting and dimming the tool bar code in GTK does
328 will look bad. For display with more than 8 planes we just use the
329 pixmap and mask directly. For monochrome displays, GTK doesn't seem
330 able to use external pixmaps, it looks bad whatever we do.
331 The image is defined on the display where frame F is.
332 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
333 If OLD_WIDGET is NULL, a new widget is constructed and returned.
334 If OLD_WIDGET is not NULL, that widget is modified. */
336 static GtkWidget *
337 xg_get_image_for_pixmap (struct frame *f,
338 struct image *img,
339 GtkWidget *widget,
340 GtkImage *old_widget)
342 GdkPixbuf *icon_buf;
344 /* If we have a file, let GTK do all the image handling.
345 This seems to be the only way to make insensitive and activated icons
346 look good in all cases. */
347 Lisp_Object specified_file = file_for_image (img->spec);
348 Lisp_Object file;
350 /* We already loaded the image once before calling this
351 function, so this only fails if the image file has been removed.
352 In that case, use the pixmap already loaded. */
354 if (STRINGP (specified_file)
355 && STRINGP (file = x_find_image_file (specified_file)))
357 char *encoded_file = SSDATA (ENCODE_FILE (file));
358 if (! old_widget)
359 old_widget = GTK_IMAGE (gtk_image_new_from_file (encoded_file));
360 else
361 gtk_image_set_from_file (old_widget, encoded_file);
363 return GTK_WIDGET (old_widget);
366 /* No file, do the image handling ourselves. This will look very bad
367 on a monochrome display, and sometimes bad on all displays with
368 certain themes. */
370 /* This is a workaround to make icons look good on pseudo color
371 displays. Apparently GTK expects the images to have an alpha
372 channel. If they don't, insensitive and activated icons will
373 look bad. This workaround does not work on monochrome displays,
374 and is strictly not needed on true color/static color displays (i.e.
375 16 bits and higher). But we do it anyway so we get a pixbuf that is
376 not associated with the img->pixmap. The img->pixmap may be removed
377 by clearing the image cache and then the tool bar redraw fails, since
378 Gtk+ assumes the pixmap is always there. */
379 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
381 if (icon_buf)
383 if (! old_widget)
384 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
385 else
386 gtk_image_set_from_pixbuf (old_widget, icon_buf);
388 g_object_unref (G_OBJECT (icon_buf));
391 return GTK_WIDGET (old_widget);
395 /* Set CURSOR on W and all widgets W contain. We must do like this
396 for scroll bars and menu because they create widgets internally,
397 and it is those widgets that are visible. */
399 static void
400 xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
402 GdkWindow *window = gtk_widget_get_window (w);
403 GList *children = gdk_window_peek_children (window);
405 gdk_window_set_cursor (window, cursor);
407 /* The scroll bar widget has more than one GDK window (had to look at
408 the source to figure this out), and there is no way to set cursor
409 on widgets in GTK. So we must set the cursor for all GDK windows.
410 Ditto for menus. */
412 for ( ; children; children = g_list_next (children))
413 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
416 /* Insert NODE into linked LIST. */
418 static void
419 xg_list_insert (xg_list_node *list, xg_list_node *node)
421 xg_list_node *list_start = list->next;
423 if (list_start) list_start->prev = node;
424 node->next = list_start;
425 node->prev = 0;
426 list->next = node;
429 /* Remove NODE from linked LIST. */
431 static void
432 xg_list_remove (xg_list_node *list, xg_list_node *node)
434 xg_list_node *list_start = list->next;
435 if (node == list_start)
437 list->next = node->next;
438 if (list->next) list->next->prev = 0;
440 else
442 node->prev->next = node->next;
443 if (node->next) node->next->prev = node->prev;
447 /* Allocate and return a utf8 version of STR. If STR is already
448 utf8 or NULL, just return a copy of STR.
449 A new string is allocated and the caller must free the result
450 with g_free. */
452 static char *
453 get_utf8_string (const char *str)
455 char *utf8_str;
457 if (!str) return NULL;
459 /* If not UTF-8, try current locale. */
460 if (!g_utf8_validate (str, -1, NULL))
461 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
462 else
463 return g_strdup (str);
465 if (!utf8_str)
467 /* Probably some control characters in str. Escape them. */
468 ptrdiff_t len;
469 ptrdiff_t nr_bad = 0;
470 gsize bytes_read;
471 gsize bytes_written;
472 unsigned char *p = (unsigned char *)str;
473 char *cp, *up;
474 GError *err = NULL;
476 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
477 &bytes_written, &err))
478 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
480 ++nr_bad;
481 p += bytes_written+1;
482 g_error_free (err);
483 err = NULL;
486 if (err)
488 g_error_free (err);
489 err = NULL;
491 if (cp) g_free (cp);
493 len = strlen (str);
494 ptrdiff_t alloc;
495 if (INT_MULTIPLY_WRAPV (nr_bad, 4, &alloc)
496 || INT_ADD_WRAPV (len + 1, alloc, &alloc)
497 || SIZE_MAX < alloc)
498 memory_full (SIZE_MAX);
499 up = utf8_str = xmalloc (alloc);
500 p = (unsigned char *)str;
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 memcpy (up, p, bytes_written);
507 up += bytes_written;
508 up += sprintf (up, "\\%03o", p[bytes_written]);
509 p += bytes_written + 1;
510 g_error_free (err);
511 err = NULL;
514 if (cp)
516 strcpy (up, cp);
517 g_free (cp);
519 if (err)
521 g_error_free (err);
522 err = NULL;
525 return utf8_str;
528 /* Check for special colors used in face spec for region face.
529 The colors are fetched from the Gtk+ theme.
530 Return true if color was found, false if not. */
532 bool
533 xg_check_special_colors (struct frame *f,
534 const char *color_name,
535 XColor *color)
537 bool success_p = 0;
538 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
539 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
541 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
542 return success_p;
544 block_input ();
546 #ifdef HAVE_GTK3
547 GtkStyleContext *gsty
548 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
549 GdkRGBA col;
550 char buf[sizeof "rgb://rrrr/gggg/bbbb"];
551 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
552 if (get_fg)
553 gtk_style_context_get_color (gsty, state, &col);
554 else
555 gtk_style_context_get_background_color (gsty, state, &col);
557 sprintf (buf, "rgb:%04x/%04x/%04x",
558 (unsigned) (col.red * 65535),
559 (unsigned) (col.green * 65535),
560 (unsigned) (col.blue * 65535));
561 success_p = x_parse_color (f, buf, color) != 0;
562 #else
563 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
564 GdkColor *grgb = get_bg
565 ? &gsty->bg[GTK_STATE_SELECTED]
566 : &gsty->fg[GTK_STATE_SELECTED];
568 color->red = grgb->red;
569 color->green = grgb->green;
570 color->blue = grgb->blue;
571 color->pixel = grgb->pixel;
572 success_p = 1;
573 #endif
576 unblock_input ();
577 return success_p;
582 /***********************************************************************
583 Tooltips
584 ***********************************************************************/
585 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
586 We use that to pop down the tooltip. This happens if Gtk+ for some
587 reason wants to change or hide the tooltip. */
589 #ifdef USE_GTK_TOOLTIP
591 static void
592 hierarchy_ch_cb (GtkWidget *widget,
593 GtkWidget *previous_toplevel,
594 gpointer user_data)
596 struct frame *f = user_data;
597 struct x_output *x = f->output_data.x;
598 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
600 if (! top || ! GTK_IS_WINDOW (top))
601 gtk_widget_hide (previous_toplevel);
604 /* Callback called when Gtk+ thinks a tooltip should be displayed.
605 We use it to get the tooltip window and the tooltip widget so
606 we can manipulate the ourselves.
608 Return FALSE ensures that the tooltip is not shown. */
610 static gboolean
611 qttip_cb (GtkWidget *widget,
612 gint xpos,
613 gint ypos,
614 gboolean keyboard_mode,
615 GtkTooltip *tooltip,
616 gpointer user_data)
618 struct frame *f = user_data;
619 struct x_output *x = f->output_data.x;
620 if (x->ttip_widget == NULL)
622 GtkWidget *p;
623 GList *list, *iter;
625 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
626 x->ttip_widget = tooltip;
627 g_object_ref (G_OBJECT (tooltip));
628 x->ttip_lbl = gtk_label_new ("");
629 g_object_ref (G_OBJECT (x->ttip_lbl));
630 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
631 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
633 /* Change stupid Gtk+ default line wrapping. */
634 p = gtk_widget_get_parent (x->ttip_lbl);
635 list = gtk_container_get_children (GTK_CONTAINER (p));
636 for (iter = list; iter; iter = g_list_next (iter))
638 GtkWidget *w = GTK_WIDGET (iter->data);
639 if (GTK_IS_LABEL (w))
640 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
642 g_list_free (list);
644 /* ATK needs an empty title for some reason. */
645 gtk_window_set_title (x->ttip_window, "");
646 /* Realize so we can safely get screen later on. */
647 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
648 gtk_widget_realize (x->ttip_lbl);
650 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
651 G_CALLBACK (hierarchy_ch_cb), f);
653 return FALSE;
656 #endif /* USE_GTK_TOOLTIP */
658 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
659 Return true if a system tooltip is available. */
661 bool
662 xg_prepare_tooltip (struct frame *f,
663 Lisp_Object string,
664 int *width,
665 int *height)
667 #ifndef USE_GTK_TOOLTIP
668 return 0;
669 #else
670 struct x_output *x = f->output_data.x;
671 GtkWidget *widget;
672 GdkWindow *gwin;
673 GdkScreen *screen;
674 GtkSettings *settings;
675 gboolean tt_enabled = TRUE;
676 GtkRequisition req;
677 Lisp_Object encoded_string;
679 if (!x->ttip_lbl) return 0;
681 block_input ();
682 encoded_string = ENCODE_UTF_8 (string);
683 widget = GTK_WIDGET (x->ttip_lbl);
684 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
685 screen = gdk_window_get_screen (gwin);
686 settings = gtk_settings_get_for_screen (screen);
687 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
688 if (tt_enabled)
690 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
691 /* Record that we disabled it so it can be enabled again. */
692 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
693 (gpointer)f);
696 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
697 g_object_set_data (G_OBJECT
698 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
699 "gdk-display-current-tooltip", NULL);
701 /* Put our dummy widget in so we can get callbacks for unrealize and
702 hierarchy-changed. */
703 gtk_tooltip_set_custom (x->ttip_widget, widget);
704 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
705 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
706 if (width) *width = req.width;
707 if (height) *height = req.height;
709 unblock_input ();
711 return 1;
712 #endif /* USE_GTK_TOOLTIP */
715 /* Show the tooltip at ROOT_X and ROOT_Y.
716 xg_prepare_tooltip must have been called before this function. */
718 void
719 xg_show_tooltip (struct frame *f, int root_x, int root_y)
721 #ifdef USE_GTK_TOOLTIP
722 struct x_output *x = f->output_data.x;
723 if (x->ttip_window)
725 block_input ();
726 gtk_window_move (x->ttip_window, root_x, root_y);
727 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
728 unblock_input ();
730 #endif
733 /* Hide tooltip if shown. Do nothing if not shown.
734 Return true if tip was hidden, false if not (i.e. not using
735 system tooltips). */
737 bool
738 xg_hide_tooltip (struct frame *f)
740 bool ret = 0;
741 #ifdef USE_GTK_TOOLTIP
742 if (f->output_data.x->ttip_window)
744 GtkWindow *win = f->output_data.x->ttip_window;
745 block_input ();
746 gtk_widget_hide (GTK_WIDGET (win));
748 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
750 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
751 GdkScreen *screen = gdk_window_get_screen (gwin);
752 GtkSettings *settings = gtk_settings_get_for_screen (screen);
753 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
755 unblock_input ();
757 ret = 1;
759 #endif
760 return ret;
764 /***********************************************************************
765 General functions for creating widgets, resizing, events, e.t.c.
766 ***********************************************************************/
768 static void
769 my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
770 const gchar *msg, gpointer user_data)
772 if (!strstr (msg, "visible children"))
773 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
776 /* Make a geometry string and pass that to GTK. It seems this is the
777 only way to get geometry position right if the user explicitly
778 asked for a position when starting Emacs.
779 F is the frame we shall set geometry for. */
781 static void
782 xg_set_geometry (struct frame *f)
784 if (f->size_hint_flags & (USPosition | PPosition))
786 if (x_gtk_use_window_move)
788 /* Handle negative positions without consulting
789 gtk_window_parse_geometry (Bug#25851). The position will
790 be off by scrollbar width + window manager decorations. */
791 if (f->size_hint_flags & XNegative)
792 f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f))
793 - FRAME_PIXEL_WIDTH (f) + f->left_pos);
795 if (f->size_hint_flags & YNegative)
796 f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f))
797 - FRAME_PIXEL_HEIGHT (f) + f->top_pos);
799 gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
800 f->left_pos, f->top_pos);
802 /* Reset size hint flags. */
803 f->size_hint_flags &= ~ (XNegative | YNegative);
805 else
807 int left = f->left_pos;
808 int xneg = f->size_hint_flags & XNegative;
809 int top = f->top_pos;
810 int yneg = f->size_hint_flags & YNegative;
811 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
812 guint id;
814 if (xneg)
815 left = -left;
816 if (yneg)
817 top = -top;
819 sprintf (geom_str, "=%dx%d%c%d%c%d",
820 FRAME_PIXEL_WIDTH (f),
821 FRAME_PIXEL_HEIGHT (f),
822 (xneg ? '-' : '+'), left,
823 (yneg ? '-' : '+'), top);
825 /* Silence warning about visible children. */
826 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
827 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
829 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
830 geom_str))
831 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
833 g_log_remove_handler ("Gtk", id);
838 /* Clear under internal border if any. As we use a mix of Gtk+ and X calls
839 and use a GtkFixed widget, this doesn't happen automatically. */
841 void
842 xg_clear_under_internal_border (struct frame *f)
844 if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
846 x_clear_area (f, 0, 0,
847 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
849 x_clear_area (f, 0, 0,
850 FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
852 x_clear_area (f, 0,
853 FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
854 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
856 x_clear_area (f,
857 FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
858 0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
862 static int
863 xg_get_gdk_scale (void)
865 const char *sscale = getenv ("GDK_SCALE");
867 if (sscale)
869 long scale = atol (sscale);
870 if (0 < scale)
871 return min (scale, INT_MAX);
874 return 1;
877 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
878 and a Gtk+ menu bar, we get resize events for the edit part of the
879 frame only. We let Gtk+ deal with the Gtk+ parts.
880 F is the frame to resize.
881 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
883 void
884 xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
886 int width, height;
888 if (pixelwidth == -1 && pixelheight == -1)
890 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
891 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
892 0, 0, &pixelwidth, &pixelheight);
893 else
894 return;
897 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
898 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
900 frame_size_history_add
901 (f, Qxg_frame_resized, width, height, Qnil);
903 if (width != FRAME_TEXT_WIDTH (f)
904 || height != FRAME_TEXT_HEIGHT (f)
905 || pixelwidth != FRAME_PIXEL_WIDTH (f)
906 || pixelheight != FRAME_PIXEL_HEIGHT (f))
908 xg_clear_under_internal_border (f);
909 change_frame_size (f, width, height, 0, 1, 0, 1);
910 SET_FRAME_GARBAGED (f);
911 cancel_mouse_face (f);
915 /* Resize the outer window of frame F after changing the height.
916 COLUMNS/ROWS is the size the edit area shall have after the resize. */
918 void
919 xg_frame_set_char_size (struct frame *f, int width, int height)
921 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
922 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
923 Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
924 gint gwidth, gheight;
925 int totalheight
926 = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
927 int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
929 if (FRAME_PIXEL_HEIGHT (f) == 0)
930 return;
932 gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
933 &gwidth, &gheight);
935 /* Do this before resize, as we don't know yet if we will be resized. */
936 xg_clear_under_internal_border (f);
938 if (FRAME_VISIBLE_P (f))
940 int scale = xg_get_gdk_scale ();
941 totalheight /= scale;
942 totalwidth /= scale;
945 x_wm_set_size_hint (f, 0, 0);
947 /* Resize the top level widget so rows and columns remain constant.
949 When the frame is fullheight and we only want to change the width
950 or it is fullwidth and we only want to change the height we should
951 be able to preserve the fullscreen property. However, due to the
952 fact that we have to send a resize request anyway, the window
953 manager will abolish it. At least the respective size should
954 remain unchanged but giving the frame back its normal size will
955 be broken ... */
956 if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
958 frame_size_history_add
959 (f, Qxg_frame_set_char_size_1, width, height,
960 list2 (make_number (gheight), make_number (totalheight)));
962 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
963 gwidth, totalheight);
965 else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
967 frame_size_history_add
968 (f, Qxg_frame_set_char_size_2, width, height,
969 list2 (make_number (gwidth), make_number (totalwidth)));
971 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
972 totalwidth, gheight);
974 else
976 frame_size_history_add
977 (f, Qxg_frame_set_char_size_3, width, height,
978 list2 (make_number (totalwidth), make_number (totalheight)));
980 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
981 totalwidth, totalheight);
982 fullscreen = Qnil;
985 SET_FRAME_GARBAGED (f);
986 cancel_mouse_face (f);
988 /* We can not call change_frame_size for a mapped frame,
989 we can not set pixel width/height either. The window manager may
990 override our resize request, XMonad does this all the time.
991 The best we can do is try to sync, so lisp code sees the updated
992 size as fast as possible.
993 For unmapped windows, we can set rows/cols. When
994 the frame is mapped again we will (hopefully) get the correct size. */
995 if (FRAME_VISIBLE_P (f))
997 /* Must call this to flush out events */
998 (void)gtk_events_pending ();
999 gdk_flush ();
1000 x_wait_for_event (f, ConfigureNotify);
1002 if (!NILP (fullscreen))
1003 /* Try to restore fullscreen state. */
1005 store_frame_param (f, Qfullscreen, fullscreen);
1006 x_set_fullscreen (f, fullscreen, fullscreen);
1009 else
1010 adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
1014 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
1015 The policy is to keep the number of editable lines. */
1017 #if 0
1018 static void
1019 xg_height_or_width_changed (struct frame *f)
1021 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1022 FRAME_TOTAL_PIXEL_WIDTH (f),
1023 FRAME_TOTAL_PIXEL_HEIGHT (f));
1024 f->output_data.x->hint_flags = 0;
1025 x_wm_set_size_hint (f, 0, 0);
1027 #endif
1029 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1030 Must be done like this, because GtkWidget:s can have "hidden"
1031 X Window that aren't accessible.
1033 Return 0 if no widget match WDESC. */
1035 GtkWidget *
1036 xg_win_to_widget (Display *dpy, Window wdesc)
1038 gpointer gdkwin;
1039 GtkWidget *gwdesc = 0;
1041 block_input ();
1043 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1044 wdesc);
1045 if (gdkwin)
1047 GdkEvent event;
1048 event.any.window = gdkwin;
1049 event.any.type = GDK_NOTHING;
1050 gwdesc = gtk_get_event_widget (&event);
1053 unblock_input ();
1054 return gwdesc;
1057 /* Set the background of widget W to PIXEL. */
1059 static void
1060 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
1062 #ifdef HAVE_GTK3
1063 GdkRGBA bg;
1064 XColor xbg;
1065 xbg.pixel = pixel;
1066 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1068 bg.red = (double)xbg.red/65535.0;
1069 bg.green = (double)xbg.green/65535.0;
1070 bg.blue = (double)xbg.blue/65535.0;
1071 bg.alpha = 1.0;
1072 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1074 #else
1075 GdkColor bg;
1076 GdkColormap *map = gtk_widget_get_colormap (w);
1077 gdk_colormap_query_color (map, pixel, &bg);
1078 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1079 #endif
1082 /* Callback called when the gtk theme changes.
1083 We notify lisp code so it can fix faces used for region for example. */
1085 static void
1086 style_changed_cb (GObject *go,
1087 GParamSpec *spec,
1088 gpointer user_data)
1090 struct input_event event;
1091 GdkDisplay *gdpy = user_data;
1092 const char *display_name = gdk_display_get_name (gdpy);
1093 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1095 EVENT_INIT (event);
1096 event.kind = CONFIG_CHANGED_EVENT;
1097 event.frame_or_window = build_string (display_name);
1098 /* Theme doesn't change often, so intern is called seldom. */
1099 event.arg = intern ("theme-name");
1100 kbd_buffer_store_event (&event);
1102 update_theme_scrollbar_width ();
1103 update_theme_scrollbar_height ();
1105 /* If scroll bar width changed, we need set the new size on all frames
1106 on this display. */
1107 if (dpy)
1109 Lisp_Object rest, frame;
1110 FOR_EACH_FRAME (rest, frame)
1112 struct frame *f = XFRAME (frame);
1113 if (FRAME_LIVE_P (f)
1114 && FRAME_X_P (f)
1115 && FRAME_X_DISPLAY (f) == dpy)
1117 x_set_scroll_bar_default_width (f);
1118 x_set_scroll_bar_default_height (f);
1119 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
1125 /* Called when a delete-event occurs on WIDGET. */
1127 static gboolean
1128 delete_cb (GtkWidget *widget,
1129 GdkEvent *event,
1130 gpointer user_data)
1132 return TRUE;
1135 /* Create and set up the GTK widgets for frame F.
1136 Return true if creation succeeded. */
1138 bool
1139 xg_create_frame_widgets (struct frame *f)
1141 GtkWidget *wtop;
1142 GtkWidget *wvbox, *whbox;
1143 GtkWidget *wfixed;
1144 #ifndef HAVE_GTK3
1145 GtkRcStyle *style;
1146 #endif
1147 char *title = 0;
1149 block_input ();
1151 if (FRAME_X_EMBEDDED_P (f))
1153 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1154 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1156 else
1157 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1159 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1160 has backported it to Gtk+ 2.0 and they add the resize grip for
1161 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1162 forever, so disable the grip. */
1163 #if (! GTK_CHECK_VERSION (3, 0, 0) \
1164 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1165 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1166 #endif
1168 xg_set_screen (wtop, f);
1170 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1171 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1172 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1173 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1175 #ifdef HAVE_GTK3
1176 wfixed = emacs_fixed_new (f);
1177 #else
1178 wfixed = gtk_fixed_new ();
1179 #endif
1181 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1183 if (wtop) gtk_widget_destroy (wtop);
1184 if (wvbox) gtk_widget_destroy (wvbox);
1185 if (whbox) gtk_widget_destroy (whbox);
1186 if (wfixed) gtk_widget_destroy (wfixed);
1188 unblock_input ();
1189 return 0;
1192 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1193 gtk_widget_set_name (wtop, EMACS_CLASS);
1194 gtk_widget_set_name (wvbox, "pane");
1195 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1197 /* If this frame has a title or name, set it in the title bar. */
1198 if (! NILP (f->title))
1199 title = SSDATA (ENCODE_UTF_8 (f->title));
1200 else if (! NILP (f->name))
1201 title = SSDATA (ENCODE_UTF_8 (f->name));
1203 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
1205 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1206 FRAME_GTK_WIDGET (f) = wfixed;
1207 f->output_data.x->vbox_widget = wvbox;
1208 f->output_data.x->hbox_widget = whbox;
1210 gtk_widget_set_has_window (wfixed, TRUE);
1212 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1213 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1214 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1216 if (FRAME_EXTERNAL_TOOL_BAR (f))
1217 update_frame_tool_bar (f);
1219 /* We don't want this widget double buffered, because we draw on it
1220 with regular X drawing primitives, so from a GTK/GDK point of
1221 view, the widget is totally blank. When an expose comes, this
1222 will make the widget blank, and then Emacs redraws it. This flickers
1223 a lot, so we turn off double buffering. */
1224 gtk_widget_set_double_buffered (wfixed, FALSE);
1226 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1227 SSDATA (Vx_resource_name),
1228 SSDATA (Vx_resource_class));
1230 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1231 GTK is to destroy the widget. We want Emacs to do that instead. */
1232 g_signal_connect (G_OBJECT (wtop), "delete-event",
1233 G_CALLBACK (delete_cb), f);
1235 /* Convert our geometry parameters into a geometry string
1236 and specify it.
1237 GTK will itself handle calculating the real position this way. */
1238 xg_set_geometry (f);
1239 f->win_gravity
1240 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1242 gtk_widget_add_events (wfixed,
1243 GDK_POINTER_MOTION_MASK
1244 | GDK_EXPOSURE_MASK
1245 | GDK_BUTTON_PRESS_MASK
1246 | GDK_BUTTON_RELEASE_MASK
1247 | GDK_KEY_PRESS_MASK
1248 | GDK_ENTER_NOTIFY_MASK
1249 | GDK_LEAVE_NOTIFY_MASK
1250 | GDK_FOCUS_CHANGE_MASK
1251 | GDK_STRUCTURE_MASK
1252 | GDK_VISIBILITY_NOTIFY_MASK);
1254 /* Must realize the windows so the X window gets created. It is used
1255 by callers of this function. */
1256 gtk_widget_realize (wfixed);
1257 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1258 initial_set_up_x_back_buffer (f);
1260 /* Since GTK clears its window by filling with the background color,
1261 we must keep X and GTK background in sync. */
1262 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1264 #ifndef HAVE_GTK3
1265 /* Also, do not let any background pixmap to be set, this looks very
1266 bad as Emacs overwrites the background pixmap with its own idea
1267 of background color. */
1268 style = gtk_widget_get_modifier_style (wfixed);
1270 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1271 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1272 gtk_widget_modify_style (wfixed, style);
1273 #else
1274 gtk_widget_set_can_focus (wfixed, TRUE);
1275 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1276 #endif
1278 #ifdef USE_GTK_TOOLTIP
1279 /* Steal a tool tip window we can move ourselves. */
1280 f->output_data.x->ttip_widget = 0;
1281 f->output_data.x->ttip_lbl = 0;
1282 f->output_data.x->ttip_window = 0;
1283 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1284 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1285 #endif
1288 GdkScreen *screen = gtk_widget_get_screen (wtop);
1289 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1290 /* Only connect this signal once per screen. */
1291 if (! g_signal_handler_find (G_OBJECT (gs),
1292 G_SIGNAL_MATCH_FUNC,
1293 0, 0, 0,
1294 (gpointer) G_CALLBACK (style_changed_cb),
1297 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1298 G_CALLBACK (style_changed_cb),
1299 gdk_screen_get_display (screen));
1303 unblock_input ();
1305 return 1;
1308 void
1309 xg_free_frame_widgets (struct frame *f)
1311 if (FRAME_GTK_OUTER_WIDGET (f))
1313 #ifdef USE_GTK_TOOLTIP
1314 struct x_output *x = f->output_data.x;
1315 #endif
1316 struct xg_frame_tb_info *tbinfo
1317 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1318 TB_INFO_KEY);
1319 if (tbinfo)
1320 xfree (tbinfo);
1322 /* x_free_frame_resources should have taken care of it */
1323 eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
1324 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1325 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1326 FRAME_X_RAW_DRAWABLE (f) = 0;
1327 FRAME_GTK_OUTER_WIDGET (f) = 0;
1328 #ifdef USE_GTK_TOOLTIP
1329 if (x->ttip_lbl)
1330 gtk_widget_destroy (x->ttip_lbl);
1331 if (x->ttip_widget)
1332 g_object_unref (G_OBJECT (x->ttip_widget));
1333 #endif
1337 /* Set the normal size hints for the window manager, for frame F.
1338 FLAGS is the flags word to use--or 0 meaning preserve the flags
1339 that the window now has.
1340 If USER_POSITION, set the User Position
1341 flag (this is useful when FLAGS is 0). */
1343 void
1344 x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
1346 /* Must use GTK routines here, otherwise GTK resets the size hints
1347 to its own defaults. */
1348 GdkGeometry size_hints;
1349 gint hint_flags = 0;
1350 int base_width, base_height;
1351 int min_rows = 0, min_cols = 0;
1352 int win_gravity = f->win_gravity;
1353 Lisp_Object fs_state, frame;
1354 int scale = xg_get_gdk_scale ();
1356 /* Don't set size hints during initialization; that apparently leads
1357 to a race condition. See the thread at
1358 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1359 if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
1360 return;
1362 XSETFRAME (frame, f);
1363 fs_state = Fframe_parameter (frame, Qfullscreen);
1364 if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
1365 (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
1366 x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
1368 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1369 Gtk3 don't get along and the frame shrinks (!).
1371 return;
1374 if (flags)
1376 memset (&size_hints, 0, sizeof (size_hints));
1377 f->output_data.x->size_hints = size_hints;
1378 f->output_data.x->hint_flags = hint_flags;
1380 else
1381 flags = f->size_hint_flags;
1383 size_hints = f->output_data.x->size_hints;
1384 hint_flags = f->output_data.x->hint_flags;
1386 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1387 size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
1388 size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
1390 hint_flags |= GDK_HINT_BASE_SIZE;
1391 /* Use one row/col here so base_height/width does not become zero.
1392 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
1393 Obviously this makes the row/col value displayed off by 1. */
1394 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1395 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1396 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1398 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1399 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1401 size_hints.base_width = base_width;
1402 size_hints.base_height = base_height;
1403 size_hints.min_width = base_width + min_cols * FRAME_COLUMN_WIDTH (f);
1404 size_hints.min_height = base_height + min_rows * FRAME_LINE_HEIGHT (f);
1406 /* These currently have a one to one mapping with the X values, but I
1407 don't think we should rely on that. */
1408 hint_flags |= GDK_HINT_WIN_GRAVITY;
1409 size_hints.win_gravity = 0;
1410 if (win_gravity == NorthWestGravity)
1411 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1412 else if (win_gravity == NorthGravity)
1413 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1414 else if (win_gravity == NorthEastGravity)
1415 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1416 else if (win_gravity == WestGravity)
1417 size_hints.win_gravity = GDK_GRAVITY_WEST;
1418 else if (win_gravity == CenterGravity)
1419 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1420 else if (win_gravity == EastGravity)
1421 size_hints.win_gravity = GDK_GRAVITY_EAST;
1422 else if (win_gravity == SouthWestGravity)
1423 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1424 else if (win_gravity == SouthGravity)
1425 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1426 else if (win_gravity == SouthEastGravity)
1427 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1428 else if (win_gravity == StaticGravity)
1429 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1431 if (x_gtk_use_window_move)
1433 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
1434 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
1435 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
1438 if (user_position)
1440 hint_flags &= ~GDK_HINT_POS;
1441 hint_flags |= GDK_HINT_USER_POS;
1444 size_hints.base_width /= scale;
1445 size_hints.base_height /= scale;
1446 size_hints.width_inc /= scale;
1447 size_hints.height_inc /= scale;
1449 if (hint_flags != f->output_data.x->hint_flags
1450 || memcmp (&size_hints,
1451 &f->output_data.x->size_hints,
1452 sizeof (size_hints)) != 0)
1454 block_input ();
1455 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1456 NULL, &size_hints, hint_flags);
1457 f->output_data.x->size_hints = size_hints;
1458 f->output_data.x->hint_flags = hint_flags;
1459 unblock_input ();
1463 /* Change background color of a frame.
1464 Since GTK uses the background color to clear the window, we must
1465 keep the GTK and X colors in sync.
1466 F is the frame to change,
1467 BG is the pixel value to change to. */
1469 void
1470 xg_set_background_color (struct frame *f, unsigned long bg)
1472 if (FRAME_GTK_WIDGET (f))
1474 block_input ();
1475 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1477 Lisp_Object bar;
1478 for (bar = FRAME_SCROLL_BARS (f);
1479 !NILP (bar);
1480 bar = XSCROLL_BAR (bar)->next)
1482 GtkWidget *scrollbar =
1483 xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window);
1484 GtkWidget *webox = gtk_widget_get_parent (scrollbar);
1485 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
1488 unblock_input ();
1493 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1494 functions so GTK does not overwrite the icon. */
1496 void
1497 xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1499 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1500 icon_pixmap,
1501 icon_mask);
1502 if (gp)
1503 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1508 /***********************************************************************
1509 Dialog functions
1510 ***********************************************************************/
1511 /* Return the dialog title to use for a dialog of type KEY.
1512 This is the encoding used by lwlib. We use the same for GTK. */
1514 static const char *
1515 get_dialog_title (char key)
1517 const char *title = "";
1519 switch (key) {
1520 case 'E': case 'e':
1521 title = "Error";
1522 break;
1524 case 'I': case 'i':
1525 title = "Information";
1526 break;
1528 case 'L': case 'l':
1529 title = "Prompt";
1530 break;
1532 case 'P': case 'p':
1533 title = "Prompt";
1534 break;
1536 case 'Q': case 'q':
1537 title = "Question";
1538 break;
1541 return title;
1544 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1545 the dialog, but return TRUE so the event does not propagate further
1546 in GTK. This prevents GTK from destroying the dialog widget automatically
1547 and we can always destroy the widget manually, regardless of how
1548 it was popped down (button press or WM_DELETE_WINDOW).
1549 W is the dialog widget.
1550 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1551 user_data is NULL (not used).
1553 Returns TRUE to end propagation of event. */
1555 static gboolean
1556 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1558 gtk_widget_unmap (w);
1559 return TRUE;
1562 /* Create a popup dialog window. See also xg_create_widget below.
1563 WV is a widget_value describing the dialog.
1564 SELECT_CB is the callback to use when a button has been pressed.
1565 DEACTIVATE_CB is the callback to use when the dialog pops down.
1567 Returns the GTK dialog widget. */
1569 static GtkWidget *
1570 create_dialog (widget_value *wv,
1571 GCallback select_cb,
1572 GCallback deactivate_cb)
1574 const char *title = get_dialog_title (wv->name[0]);
1575 int total_buttons = wv->name[1] - '0';
1576 int right_buttons = wv->name[4] - '0';
1577 int left_buttons;
1578 int button_nr = 0;
1579 int button_spacing = 10;
1580 GtkWidget *wdialog = gtk_dialog_new ();
1581 GtkDialog *wd = GTK_DIALOG (wdialog);
1582 widget_value *item;
1583 GtkWidget *whbox_down;
1585 /* If the number of buttons is greater than 4, make two rows of buttons
1586 instead. This looks better. */
1587 bool make_two_rows = total_buttons > 4;
1589 #if GTK_CHECK_VERSION (3, 12, 0)
1590 GtkBuilder *gbld = gtk_builder_new ();
1591 GObject *go = gtk_buildable_get_internal_child (GTK_BUILDABLE (wd),
1592 gbld,
1593 "action_area");
1594 GtkBox *cur_box = GTK_BOX (go);
1595 g_object_unref (G_OBJECT (gbld));
1596 #else
1597 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1598 #endif
1600 if (right_buttons == 0) right_buttons = total_buttons/2;
1601 left_buttons = total_buttons - right_buttons;
1603 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1604 gtk_widget_set_name (wdialog, "emacs-dialog");
1607 if (make_two_rows)
1609 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1610 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1611 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1612 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1613 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1614 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1616 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1617 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1618 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1620 cur_box = GTK_BOX (whbox_up);
1623 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1624 G_CALLBACK (dialog_delete_callback), 0);
1626 if (deactivate_cb)
1628 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1629 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1632 for (item = wv->contents; item; item = item->next)
1634 char *utf8_label = get_utf8_string (item->value);
1635 GtkWidget *w;
1636 GtkRequisition req;
1638 if (item->name && strcmp (item->name, "message") == 0)
1640 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1641 /* This is the text part of the dialog. */
1642 w = gtk_label_new (utf8_label);
1643 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1644 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1645 #if GTK_CHECK_VERSION (3, 14, 0)
1646 gtk_widget_set_halign (w, GTK_ALIGN_START);
1647 gtk_widget_set_valign (w, GTK_ALIGN_CENTER);
1648 #else
1649 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1650 #endif
1651 /* Try to make dialog look better. Must realize first so
1652 the widget can calculate the size it needs. */
1653 gtk_widget_realize (w);
1654 gtk_widget_get_preferred_size (w, NULL, &req);
1655 gtk_box_set_spacing (wvbox, req.height);
1656 if (item->value && strlen (item->value) > 0)
1657 button_spacing = 2*req.width/strlen (item->value);
1658 if (button_spacing < 10) button_spacing = 10;
1660 else
1662 /* This is one button to add to the dialog. */
1663 w = gtk_button_new_with_label (utf8_label);
1664 if (! item->enabled)
1665 gtk_widget_set_sensitive (w, FALSE);
1666 if (select_cb)
1667 g_signal_connect (G_OBJECT (w), "clicked",
1668 select_cb, item->call_data);
1670 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1671 if (++button_nr == left_buttons)
1673 if (make_two_rows)
1674 cur_box = GTK_BOX (whbox_down);
1678 if (utf8_label)
1679 g_free (utf8_label);
1682 return wdialog;
1685 struct xg_dialog_data
1687 GMainLoop *loop;
1688 int response;
1689 GtkWidget *w;
1690 guint timerid;
1693 /* Function that is called when the file or font dialogs pop down.
1694 W is the dialog widget, RESPONSE is the response code.
1695 USER_DATA is what we passed in to g_signal_connect. */
1697 static void
1698 xg_dialog_response_cb (GtkDialog *w,
1699 gint response,
1700 gpointer user_data)
1702 struct xg_dialog_data *dd = user_data;
1703 dd->response = response;
1704 g_main_loop_quit (dd->loop);
1708 /* Destroy the dialog. This makes it pop down. */
1710 static void
1711 pop_down_dialog (void *arg)
1713 struct xg_dialog_data *dd = arg;
1715 block_input ();
1716 if (dd->w) gtk_widget_destroy (dd->w);
1717 if (dd->timerid != 0) g_source_remove (dd->timerid);
1719 g_main_loop_quit (dd->loop);
1720 g_main_loop_unref (dd->loop);
1722 unblock_input ();
1725 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1726 Pass DATA as gpointer so we can use this as a callback. */
1728 static gboolean
1729 xg_maybe_add_timer (gpointer data)
1731 struct xg_dialog_data *dd = data;
1732 struct timespec next_time = timer_check ();
1734 dd->timerid = 0;
1736 if (timespec_valid_p (next_time))
1738 time_t s = next_time.tv_sec;
1739 int per_ms = TIMESPEC_RESOLUTION / 1000;
1740 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
1741 if (s <= ((guint) -1 - ms) / 1000)
1742 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1744 return FALSE;
1748 /* Pops up a modal dialog W and waits for response.
1749 We don't use gtk_dialog_run because we want to process emacs timers.
1750 The dialog W is not destroyed when this function returns. */
1752 static int
1753 xg_dialog_run (struct frame *f, GtkWidget *w)
1755 ptrdiff_t count = SPECPDL_INDEX ();
1756 struct xg_dialog_data dd;
1758 xg_set_screen (w, f);
1759 gtk_window_set_transient_for (GTK_WINDOW (w),
1760 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1761 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1762 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1764 dd.loop = g_main_loop_new (NULL, FALSE);
1765 dd.response = GTK_RESPONSE_CANCEL;
1766 dd.w = w;
1767 dd.timerid = 0;
1769 g_signal_connect (G_OBJECT (w),
1770 "response",
1771 G_CALLBACK (xg_dialog_response_cb),
1772 &dd);
1773 /* Don't destroy the widget if closed by the window manager close button. */
1774 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1775 gtk_widget_show (w);
1777 record_unwind_protect_ptr (pop_down_dialog, &dd);
1779 (void) xg_maybe_add_timer (&dd);
1780 g_main_loop_run (dd.loop);
1782 dd.w = 0;
1783 unbind_to (count, Qnil);
1785 return dd.response;
1789 /***********************************************************************
1790 File dialog functions
1791 ***********************************************************************/
1792 /* Return true if the old file selection dialog is being used. */
1794 bool
1795 xg_uses_old_file_dialog (void)
1797 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1798 return x_gtk_use_old_file_dialog;
1799 #else
1800 return 0;
1801 #endif
1805 typedef char * (*xg_get_file_func) (GtkWidget *);
1807 /* Return the selected file for file chooser dialog W.
1808 The returned string must be free:d. */
1810 static char *
1811 xg_get_file_name_from_chooser (GtkWidget *w)
1813 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1816 /* Callback called when the "Show hidden files" toggle is pressed.
1817 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1819 static void
1820 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1822 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1823 gboolean visible;
1824 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1825 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1829 /* Callback called when a property changes in a file chooser.
1830 GOBJECT is the file chooser dialog, ARG1 describes the property.
1831 USER_DATA is the toggle widget in the file chooser dialog.
1832 We use this to update the "Show hidden files" toggle when the user
1833 changes that property by right clicking in the file list. */
1835 static void
1836 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1838 if (strcmp (arg1->name, "show-hidden") == 0)
1840 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1841 gboolean visible, toggle_on;
1843 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1844 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1846 if (!!visible != !!toggle_on)
1848 gpointer cb = (gpointer) G_CALLBACK (xg_toggle_visibility_cb);
1849 g_signal_handlers_block_by_func (G_OBJECT (wtoggle), cb, gobject);
1850 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1851 g_signal_handlers_unblock_by_func (G_OBJECT (wtoggle), cb, gobject);
1853 x_gtk_show_hidden_files = visible;
1857 /* Read a file name from the user using a file chooser dialog.
1858 F is the current frame.
1859 PROMPT is a prompt to show to the user. May not be NULL.
1860 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1861 If MUSTMATCH_P, the returned file name must be an existing
1862 file. (Actually, this only has cosmetic effects, the user can
1863 still enter a non-existing file.) *FUNC is set to a function that
1864 can be used to retrieve the selected file name from the returned widget.
1866 Returns the created widget. */
1868 static GtkWidget *
1869 xg_get_file_with_chooser (struct frame *f,
1870 char *prompt,
1871 char *default_filename,
1872 bool mustmatch_p, bool only_dir_p,
1873 xg_get_file_func *func)
1875 char msgbuf[1024];
1877 GtkWidget *filewin, *wtoggle, *wbox;
1878 GtkWidget *wmessage UNINIT;
1879 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1880 GtkFileChooserAction action = (mustmatch_p ?
1881 GTK_FILE_CHOOSER_ACTION_OPEN :
1882 GTK_FILE_CHOOSER_ACTION_SAVE);
1884 if (only_dir_p)
1885 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1887 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1888 XG_TEXT_CANCEL, GTK_RESPONSE_CANCEL,
1889 (mustmatch_p || only_dir_p ?
1890 XG_TEXT_OPEN : XG_TEXT_OK),
1891 GTK_RESPONSE_OK,
1892 NULL);
1893 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
1895 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1896 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
1897 gtk_widget_show (wbox);
1898 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
1900 if (x_gtk_show_hidden_files)
1902 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
1903 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
1905 gtk_widget_show (wtoggle);
1906 g_signal_connect (G_OBJECT (wtoggle), "clicked",
1907 G_CALLBACK (xg_toggle_visibility_cb), filewin);
1908 g_signal_connect (G_OBJECT (filewin), "notify",
1909 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
1911 if (x_gtk_file_dialog_help_text)
1913 char *z = msgbuf;
1914 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
1915 Show the C-l help text only for versions < 2.10. */
1916 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
1917 z = stpcpy (z, "\nType C-l to display a file name text entry box.\n");
1918 strcpy (z, "\nIf you don't like this file selector, use the "
1919 "corresponding\nkey binding or customize "
1920 "use-file-dialog to turn it off.");
1922 wmessage = gtk_label_new (msgbuf);
1923 gtk_widget_show (wmessage);
1926 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
1927 if (x_gtk_file_dialog_help_text)
1928 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
1929 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
1931 if (default_filename)
1933 Lisp_Object file;
1934 char *utf8_filename;
1936 file = build_string (default_filename);
1938 /* File chooser does not understand ~/... in the file name. It must be
1939 an absolute name starting with /. */
1940 if (default_filename[0] != '/')
1941 file = Fexpand_file_name (file, Qnil);
1943 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
1944 if (! NILP (Ffile_directory_p (file)))
1945 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
1946 utf8_filename);
1947 else
1949 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1950 utf8_filename);
1951 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
1953 char *cp = strrchr (utf8_filename, '/');
1954 if (cp) ++cp;
1955 else cp = utf8_filename;
1956 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
1961 *func = xg_get_file_name_from_chooser;
1962 return filewin;
1965 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1967 /* Return the selected file for file selector dialog W.
1968 The returned string must be free:d. */
1970 static char *
1971 xg_get_file_name_from_selector (GtkWidget *w)
1973 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
1974 return xstrdup (gtk_file_selection_get_filename (filesel));
1977 /* Create a file selection dialog.
1978 F is the current frame.
1979 PROMPT is a prompt to show to the user. May not be NULL.
1980 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1981 If MUSTMATCH_P, the returned file name must be an existing
1982 file. *FUNC is set to a function that can be used to retrieve the
1983 selected file name from the returned widget.
1985 Returns the created widget. */
1987 static GtkWidget *
1988 xg_get_file_with_selection (struct frame *f,
1989 char *prompt,
1990 char *default_filename,
1991 bool mustmatch_p, bool only_dir_p,
1992 xg_get_file_func *func)
1994 GtkWidget *filewin;
1995 GtkFileSelection *filesel;
1997 filewin = gtk_file_selection_new (prompt);
1998 filesel = GTK_FILE_SELECTION (filewin);
2000 if (default_filename)
2001 gtk_file_selection_set_filename (filesel, default_filename);
2003 if (mustmatch_p)
2005 /* The selection_entry part of filesel is not documented. */
2006 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
2007 gtk_file_selection_hide_fileop_buttons (filesel);
2010 *func = xg_get_file_name_from_selector;
2012 return filewin;
2014 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
2016 /* Read a file name from the user using a file dialog, either the old
2017 file selection dialog, or the new file chooser dialog. Which to use
2018 depends on what the GTK version used has, and what the value of
2019 gtk-use-old-file-dialog.
2020 F is the current frame.
2021 PROMPT is a prompt to show to the user. May not be NULL.
2022 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
2023 If MUSTMATCH_P, the returned file name must be an existing
2024 file.
2026 Returns a file name or NULL if no file was selected.
2027 The returned string must be freed by the caller. */
2029 char *
2030 xg_get_file_name (struct frame *f,
2031 char *prompt,
2032 char *default_filename,
2033 bool mustmatch_p,
2034 bool only_dir_p)
2036 GtkWidget *w = 0;
2037 char *fn = 0;
2038 int filesel_done = 0;
2039 xg_get_file_func func;
2041 #ifdef HAVE_GTK_FILE_SELECTION_NEW
2043 if (xg_uses_old_file_dialog ())
2044 w = xg_get_file_with_selection (f, prompt, default_filename,
2045 mustmatch_p, only_dir_p, &func);
2046 else
2047 w = xg_get_file_with_chooser (f, prompt, default_filename,
2048 mustmatch_p, only_dir_p, &func);
2050 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
2051 w = xg_get_file_with_chooser (f, prompt, default_filename,
2052 mustmatch_p, only_dir_p, &func);
2053 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
2055 gtk_widget_set_name (w, "emacs-filedialog");
2057 filesel_done = xg_dialog_run (f, w);
2058 if (filesel_done == GTK_RESPONSE_OK)
2059 fn = (*func) (w);
2061 gtk_widget_destroy (w);
2062 return fn;
2065 /***********************************************************************
2066 GTK font chooser
2067 ***********************************************************************/
2069 #ifdef HAVE_FREETYPE
2071 #if USE_NEW_GTK_FONT_CHOOSER
2073 #define XG_WEIGHT_TO_SYMBOL(w) \
2074 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2075 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2076 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2077 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2078 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2079 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2080 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2081 : Qultra_bold)
2083 #define XG_STYLE_TO_SYMBOL(s) \
2084 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2085 : s == PANGO_STYLE_ITALIC ? Qitalic \
2086 : Qnormal)
2088 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2091 static char *x_last_font_name;
2093 /* Pop up a GTK font selector and return the name of the font the user
2094 selects, as a C string. The returned font name follows GTK's own
2095 format:
2097 `FAMILY [VALUE1 VALUE2] SIZE'
2099 This can be parsed using font_parse_fcname in font.c.
2100 DEFAULT_NAME, if non-zero, is the default font name. */
2102 Lisp_Object
2103 xg_get_font (struct frame *f, const char *default_name)
2105 GtkWidget *w;
2106 int done = 0;
2107 Lisp_Object font = Qnil;
2109 w = gtk_font_chooser_dialog_new
2110 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2112 if (default_name)
2114 /* Convert fontconfig names to Gtk names, i.e. remove - before
2115 number */
2116 char *p = strrchr (default_name, '-');
2117 if (p)
2119 char *ep = p+1;
2120 while (c_isdigit (*ep))
2121 ++ep;
2122 if (*ep == '\0') *p = ' ';
2125 else if (x_last_font_name)
2126 default_name = x_last_font_name;
2128 if (default_name)
2129 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2131 gtk_widget_set_name (w, "emacs-fontdialog");
2132 done = xg_dialog_run (f, w);
2133 if (done == GTK_RESPONSE_OK)
2135 #if USE_NEW_GTK_FONT_CHOOSER
2136 /* Use the GTK3 font chooser. */
2137 PangoFontDescription *desc
2138 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2140 if (desc)
2142 const char *name = pango_font_description_get_family (desc);
2143 gint size = pango_font_description_get_size (desc);
2144 PangoWeight weight = pango_font_description_get_weight (desc);
2145 PangoStyle style = pango_font_description_get_style (desc);
2147 #ifdef USE_CAIRO
2148 #define FONT_TYPE_WANTED (Qftcr)
2149 #else
2150 #define FONT_TYPE_WANTED (Qxft)
2151 #endif
2152 font = CALLN (Ffont_spec,
2153 QCname, build_string (name),
2154 QCsize, make_float (pango_units_to_double (size)),
2155 QCweight, XG_WEIGHT_TO_SYMBOL (weight),
2156 QCslant, XG_STYLE_TO_SYMBOL (style),
2157 QCtype,
2158 FONT_TYPE_WANTED);
2160 pango_font_description_free (desc);
2161 dupstring (&x_last_font_name, name);
2164 #else /* Use old font selector, which just returns the font name. */
2166 char *font_name
2167 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2169 if (font_name)
2171 font = build_string (font_name);
2172 g_free (x_last_font_name);
2173 x_last_font_name = font_name;
2175 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2178 gtk_widget_destroy (w);
2179 return font;
2181 #endif /* HAVE_FREETYPE */
2185 /***********************************************************************
2186 Menu functions.
2187 ***********************************************************************/
2189 /* The name of menu items that can be used for customization. Since GTK
2190 RC files are very crude and primitive, we have to set this on all
2191 menu item names so a user can easily customize menu items. */
2193 #define MENU_ITEM_NAME "emacs-menuitem"
2196 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2197 during GC. The next member points to the items. */
2198 static xg_list_node xg_menu_cb_list;
2200 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2201 during GC. The next member points to the items. */
2202 static xg_list_node xg_menu_item_cb_list;
2204 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2205 F is the frame CL_DATA will be initialized for.
2206 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2208 The menu bar and all sub menus under the menu bar in a frame
2209 share the same structure, hence the reference count.
2211 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2212 allocated xg_menu_cb_data if CL_DATA is NULL. */
2214 static xg_menu_cb_data *
2215 make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
2217 if (! cl_data)
2219 cl_data = xmalloc (sizeof *cl_data);
2220 cl_data->f = f;
2221 cl_data->menu_bar_vector = f->menu_bar_vector;
2222 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2223 cl_data->highlight_cb = highlight_cb;
2224 cl_data->ref_count = 0;
2226 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2229 cl_data->ref_count++;
2231 return cl_data;
2234 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2235 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2237 When the menu bar is updated, menu items may have been added and/or
2238 removed, so menu_bar_vector and menu_bar_items_used change. We must
2239 then update CL_DATA since it is used to determine which menu
2240 item that is invoked in the menu.
2241 HIGHLIGHT_CB could change, there is no check that the same
2242 function is given when modifying a menu bar as was given when
2243 creating the menu bar. */
2245 static void
2246 update_cl_data (xg_menu_cb_data *cl_data,
2247 struct frame *f,
2248 GCallback highlight_cb)
2250 if (cl_data)
2252 cl_data->f = f;
2253 cl_data->menu_bar_vector = f->menu_bar_vector;
2254 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2255 cl_data->highlight_cb = highlight_cb;
2259 /* Decrease reference count for CL_DATA.
2260 If reference count is zero, free CL_DATA. */
2262 static void
2263 unref_cl_data (xg_menu_cb_data *cl_data)
2265 if (cl_data && cl_data->ref_count > 0)
2267 cl_data->ref_count--;
2268 if (cl_data->ref_count == 0)
2270 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2271 xfree (cl_data);
2276 /* Function that marks all lisp data during GC. */
2278 void
2279 xg_mark_data (void)
2281 xg_list_node *iter;
2282 Lisp_Object rest, frame;
2284 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2285 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2287 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2289 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2291 if (! NILP (cb_data->help))
2292 mark_object (cb_data->help);
2295 FOR_EACH_FRAME (rest, frame)
2297 struct frame *f = XFRAME (frame);
2299 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2301 struct xg_frame_tb_info *tbinfo
2302 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2303 TB_INFO_KEY);
2304 if (tbinfo)
2306 mark_object (tbinfo->last_tool_bar);
2307 mark_object (tbinfo->style);
2313 /* Callback called when a menu item is destroyed. Used to free data.
2314 W is the widget that is being destroyed (not used).
2315 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2317 static void
2318 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2320 if (client_data)
2322 xg_menu_item_cb_data *data = client_data;
2323 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2324 xfree (data);
2328 /* Callback called when the pointer enters/leaves a menu item.
2329 W is the parent of the menu item.
2330 EVENT is either an enter event or leave event.
2331 CLIENT_DATA is not used.
2333 Returns FALSE to tell GTK to keep processing this event. */
2335 static gboolean
2336 menuitem_highlight_callback (GtkWidget *w,
2337 GdkEventCrossing *event,
2338 gpointer client_data)
2340 GdkEvent ev;
2341 GtkWidget *subwidget;
2342 xg_menu_item_cb_data *data;
2344 ev.crossing = *event;
2345 subwidget = gtk_get_event_widget (&ev);
2346 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
2347 if (data)
2349 if (! NILP (data->help) && data->cl_data->highlight_cb)
2351 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2352 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2353 (*func) (subwidget, call_data);
2357 return FALSE;
2360 /* Callback called when a menu is destroyed. Used to free data.
2361 W is the widget that is being destroyed (not used).
2362 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2364 static void
2365 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2367 unref_cl_data (client_data);
2370 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2371 must be non-NULL) and can be inserted into a menu item.
2373 Returns the GtkHBox. */
2375 static GtkWidget *
2376 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2378 GtkWidget *wlbl;
2379 GtkWidget *wkey;
2380 GtkWidget *wbox;
2382 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2383 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2384 wlbl = gtk_label_new (utf8_label);
2385 wkey = gtk_label_new (utf8_key);
2387 #if GTK_CHECK_VERSION (3, 14, 0)
2388 gtk_widget_set_halign (wlbl, GTK_ALIGN_START);
2389 gtk_widget_set_valign (wlbl, GTK_ALIGN_CENTER);
2390 gtk_widget_set_halign (wkey, GTK_ALIGN_START);
2391 gtk_widget_set_valign (wkey, GTK_ALIGN_CENTER);
2392 #else
2393 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2394 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2395 #endif
2396 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2397 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2399 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2400 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2401 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2403 return wbox;
2406 /* Make and return a menu item widget with the key to the right.
2407 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2408 UTF8_KEY is the text representing the key binding.
2409 ITEM is the widget_value describing the menu item.
2411 GROUP is an in/out parameter. If the menu item to be created is not
2412 part of any radio menu group, *GROUP contains NULL on entry and exit.
2413 If the menu item to be created is part of a radio menu group, on entry
2414 *GROUP contains the group to use, or NULL if this is the first item
2415 in the group. On exit, *GROUP contains the radio item group.
2417 Unfortunately, keys don't line up as nicely as in Motif,
2418 but the macOS version doesn't either, so I guess that is OK. */
2420 static GtkWidget *
2421 make_menu_item (const char *utf8_label,
2422 const char *utf8_key,
2423 widget_value *item,
2424 GSList **group)
2426 GtkWidget *w;
2427 GtkWidget *wtoadd = 0;
2429 /* It has been observed that some menu items have a NULL name field.
2430 This will lead to this function being called with a NULL utf8_label.
2431 GTK crashes on that so we set a blank label. Why there is a NULL
2432 name remains to be investigated. */
2433 if (! utf8_label) utf8_label = " ";
2435 if (utf8_key)
2436 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2438 if (item->button_type == BUTTON_TYPE_TOGGLE)
2440 *group = NULL;
2441 if (utf8_key) w = gtk_check_menu_item_new ();
2442 else w = gtk_check_menu_item_new_with_label (utf8_label);
2443 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2445 else if (item->button_type == BUTTON_TYPE_RADIO)
2447 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2448 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2449 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2450 if (item->selected)
2451 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2453 else
2455 *group = NULL;
2456 if (utf8_key) w = gtk_menu_item_new ();
2457 else w = gtk_menu_item_new_with_label (utf8_label);
2460 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2461 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2463 return w;
2466 /* Create a menu item widget, and connect the callbacks.
2467 ITEM describes the menu item.
2468 F is the frame the created menu belongs to.
2469 SELECT_CB is the callback to use when a menu item is selected.
2470 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2471 CL_DATA points to the callback data to be used for this menu.
2472 GROUP is an in/out parameter. If the menu item to be created is not
2473 part of any radio menu group, *GROUP contains NULL on entry and exit.
2474 If the menu item to be created is part of a radio menu group, on entry
2475 *GROUP contains the group to use, or NULL if this is the first item
2476 in the group. On exit, *GROUP contains the radio item group.
2478 Returns the created GtkWidget. */
2480 static GtkWidget *
2481 xg_create_one_menuitem (widget_value *item,
2482 struct frame *f,
2483 GCallback select_cb,
2484 GCallback highlight_cb,
2485 xg_menu_cb_data *cl_data,
2486 GSList **group)
2488 char *utf8_label;
2489 char *utf8_key;
2490 GtkWidget *w;
2491 xg_menu_item_cb_data *cb_data;
2493 utf8_label = get_utf8_string (item->name);
2494 utf8_key = get_utf8_string (item->key);
2496 w = make_menu_item (utf8_label, utf8_key, item, group);
2498 if (utf8_label) g_free (utf8_label);
2499 if (utf8_key) g_free (utf8_key);
2501 cb_data = xmalloc (sizeof *cb_data);
2503 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2505 cb_data->select_id = 0;
2506 cb_data->help = item->help;
2507 cb_data->cl_data = cl_data;
2508 cb_data->call_data = item->call_data;
2510 g_signal_connect (G_OBJECT (w),
2511 "destroy",
2512 G_CALLBACK (menuitem_destroy_callback),
2513 cb_data);
2515 /* Put cb_data in widget, so we can get at it when modifying menubar */
2516 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2518 /* final item, not a submenu */
2519 if (item->call_data && ! item->contents)
2521 if (select_cb)
2522 cb_data->select_id
2523 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2526 return w;
2529 /* Create a full menu tree specified by DATA.
2530 F is the frame the created menu belongs to.
2531 SELECT_CB is the callback to use when a menu item is selected.
2532 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2533 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2534 If POP_UP_P, create a popup menu.
2535 If MENU_BAR_P, create a menu bar.
2536 TOPMENU is the topmost GtkWidget that others shall be placed under.
2537 It may be NULL, in that case we create the appropriate widget
2538 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2539 CL_DATA is the callback data we shall use for this menu, or NULL
2540 if we haven't set the first callback yet.
2541 NAME is the name to give to the top level menu if this function
2542 creates it. May be NULL to not set any name.
2544 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2545 not NULL.
2547 This function calls itself to create submenus. */
2549 static GtkWidget *
2550 create_menus (widget_value *data,
2551 struct frame *f,
2552 GCallback select_cb,
2553 GCallback deactivate_cb,
2554 GCallback highlight_cb,
2555 bool pop_up_p,
2556 bool menu_bar_p,
2557 GtkWidget *topmenu,
2558 xg_menu_cb_data *cl_data,
2559 const char *name)
2561 widget_value *item;
2562 GtkWidget *wmenu = topmenu;
2563 GSList *group = NULL;
2565 if (! topmenu)
2567 if (! menu_bar_p)
2569 wmenu = gtk_menu_new ();
2570 xg_set_screen (wmenu, f);
2571 /* Connect this to the menu instead of items so we get enter/leave for
2572 disabled items also. TODO: Still does not get enter/leave for
2573 disabled items in detached menus. */
2574 g_signal_connect (G_OBJECT (wmenu),
2575 "enter-notify-event",
2576 G_CALLBACK (menuitem_highlight_callback),
2577 NULL);
2578 g_signal_connect (G_OBJECT (wmenu),
2579 "leave-notify-event",
2580 G_CALLBACK (menuitem_highlight_callback),
2581 NULL);
2583 else
2585 wmenu = gtk_menu_bar_new ();
2586 /* Set width of menu bar to a small value so it doesn't enlarge
2587 a small initial frame size. The width will be set to the
2588 width of the frame later on when it is added to a container.
2589 height -1: Natural height. */
2590 gtk_widget_set_size_request (wmenu, 1, -1);
2593 /* Put cl_data on the top menu for easier access. */
2594 cl_data = make_cl_data (cl_data, f, highlight_cb);
2595 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2596 g_signal_connect (G_OBJECT (wmenu), "destroy",
2597 G_CALLBACK (menu_destroy_callback), cl_data);
2599 if (name)
2600 gtk_widget_set_name (wmenu, name);
2602 if (deactivate_cb)
2603 g_signal_connect (G_OBJECT (wmenu),
2604 "selection-done", deactivate_cb, 0);
2607 for (item = data; item; item = item->next)
2609 GtkWidget *w;
2611 if (pop_up_p && !item->contents && !item->call_data
2612 && !menu_separator_name_p (item->name))
2614 char *utf8_label;
2615 /* A title for a popup. We do the same as GTK does when
2616 creating titles, but it does not look good. */
2617 group = NULL;
2618 utf8_label = get_utf8_string (item->name);
2620 w = gtk_menu_item_new_with_label (utf8_label);
2621 gtk_widget_set_sensitive (w, FALSE);
2622 if (utf8_label) g_free (utf8_label);
2624 else if (menu_separator_name_p (item->name))
2626 group = NULL;
2627 /* GTK only have one separator type. */
2628 w = gtk_separator_menu_item_new ();
2630 else
2632 w = xg_create_one_menuitem (item,
2634 item->contents ? 0 : select_cb,
2635 highlight_cb,
2636 cl_data,
2637 &group);
2639 /* Create a possibly empty submenu for menu bar items, since some
2640 themes don't highlight items correctly without it. */
2641 if (item->contents || menu_bar_p)
2643 GtkWidget *submenu = create_menus (item->contents,
2645 select_cb,
2646 deactivate_cb,
2647 highlight_cb,
2651 cl_data,
2653 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2657 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2658 gtk_widget_set_name (w, MENU_ITEM_NAME);
2661 return wmenu;
2664 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2665 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2666 with some text and buttons.
2667 F is the frame the created item belongs to.
2668 NAME is the name to use for the top widget.
2669 VAL is a widget_value structure describing items to be created.
2670 SELECT_CB is the callback to use when a menu item is selected or
2671 a dialog button is pressed.
2672 DEACTIVATE_CB is the callback to use when an item is deactivated.
2673 For a menu, when a sub menu is not shown anymore, for a dialog it is
2674 called when the dialog is popped down.
2675 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2677 Returns the widget created. */
2679 GtkWidget *
2680 xg_create_widget (const char *type, const char *name, struct frame *f,
2681 widget_value *val, GCallback select_cb,
2682 GCallback deactivate_cb, GCallback highlight_cb)
2684 GtkWidget *w = 0;
2685 bool menu_bar_p = strcmp (type, "menubar") == 0;
2686 bool pop_up_p = strcmp (type, "popup") == 0;
2688 if (strcmp (type, "dialog") == 0)
2690 w = create_dialog (val, select_cb, deactivate_cb);
2691 xg_set_screen (w, f);
2692 gtk_window_set_transient_for (GTK_WINDOW (w),
2693 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2694 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2695 gtk_widget_set_name (w, "emacs-dialog");
2696 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2698 else if (menu_bar_p || pop_up_p)
2700 w = create_menus (val->contents,
2702 select_cb,
2703 deactivate_cb,
2704 highlight_cb,
2705 pop_up_p,
2706 menu_bar_p,
2709 name);
2711 /* Set the cursor to an arrow for popup menus when they are mapped.
2712 This is done by default for menu bar menus. */
2713 if (pop_up_p)
2715 /* Must realize so the GdkWindow inside the widget is created. */
2716 gtk_widget_realize (w);
2717 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
2720 else
2722 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2723 type);
2726 return w;
2729 /* Return the label for menu item WITEM. */
2731 static const char *
2732 xg_get_menu_item_label (GtkMenuItem *witem)
2734 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2735 return gtk_label_get_label (wlabel);
2738 /* Return true if the menu item WITEM has the text LABEL. */
2740 static bool
2741 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2743 bool is_same = 0;
2744 char *utf8_label = get_utf8_string (label);
2745 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2747 if (! old_label && ! utf8_label)
2748 is_same = 1;
2749 else if (old_label && utf8_label)
2750 is_same = strcmp (utf8_label, old_label) == 0;
2752 if (utf8_label) g_free (utf8_label);
2754 return is_same;
2757 /* Destroy widgets in LIST. */
2759 static void
2760 xg_destroy_widgets (GList *list)
2762 GList *iter;
2764 for (iter = list; iter; iter = g_list_next (iter))
2766 GtkWidget *w = GTK_WIDGET (iter->data);
2768 /* Destroying the widget will remove it from the container it is in. */
2769 gtk_widget_destroy (w);
2773 /* Update the top level names in MENUBAR (i.e. not submenus).
2774 F is the frame the menu bar belongs to.
2775 *LIST is a list with the current menu bar names (menu item widgets).
2776 ITER is the item within *LIST that shall be updated.
2777 POS is the numerical position, starting at 0, of ITER in *LIST.
2778 VAL describes what the menu bar shall look like after the update.
2779 SELECT_CB is the callback to use when a menu item is selected.
2780 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2781 CL_DATA points to the callback data to be used for this menu bar.
2783 This function calls itself to walk through the menu bar names. */
2785 static void
2786 xg_update_menubar (GtkWidget *menubar,
2787 struct frame *f,
2788 GList **list,
2789 GList *iter,
2790 int pos,
2791 widget_value *val,
2792 GCallback select_cb,
2793 GCallback deactivate_cb,
2794 GCallback highlight_cb,
2795 xg_menu_cb_data *cl_data)
2797 if (! iter && ! val)
2798 return;
2799 else if (iter && ! val)
2801 /* Item(s) have been removed. Remove all remaining items. */
2802 xg_destroy_widgets (iter);
2804 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2805 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2806 gtk_menu_item_new_with_label (""),
2808 /* All updated. */
2809 val = 0;
2810 iter = 0;
2812 else if (! iter && val)
2814 /* Item(s) added. Add all new items in one call. */
2815 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2816 0, 1, menubar, cl_data, 0);
2818 /* All updated. */
2819 val = 0;
2820 iter = 0;
2822 /* Below this neither iter or val is NULL */
2823 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2825 /* This item is still the same, check next item. */
2826 val = val->next;
2827 iter = g_list_next (iter);
2828 ++pos;
2830 else /* This item is changed. */
2832 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2833 GtkMenuItem *witem2 = 0;
2834 bool val_in_menubar = 0;
2835 bool iter_in_new_menubar = 0;
2836 GList *iter2;
2837 widget_value *cur;
2839 /* See if the changed entry (val) is present later in the menu bar */
2840 for (iter2 = iter;
2841 iter2 && ! val_in_menubar;
2842 iter2 = g_list_next (iter2))
2844 witem2 = GTK_MENU_ITEM (iter2->data);
2845 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2848 /* See if the current entry (iter) is present later in the
2849 specification for the new menu bar. */
2850 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2851 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2853 if (val_in_menubar && ! iter_in_new_menubar)
2855 int nr = pos;
2857 /* This corresponds to:
2858 Current: A B C
2859 New: A C
2860 Remove B. */
2862 g_object_ref (G_OBJECT (witem));
2863 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2864 gtk_widget_destroy (GTK_WIDGET (witem));
2866 /* Must get new list since the old changed. */
2867 g_list_free (*list);
2868 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2869 while (nr-- > 0) iter = g_list_next (iter);
2871 else if (! val_in_menubar && ! iter_in_new_menubar)
2873 /* This corresponds to:
2874 Current: A B C
2875 New: A X C
2876 Rename B to X. This might seem to be a strange thing to do,
2877 since if there is a menu under B it will be totally wrong for X.
2878 But consider editing a C file. Then there is a C-mode menu
2879 (corresponds to B above).
2880 If then doing C-x C-f the minibuf menu (X above) replaces the
2881 C-mode menu. When returning from the minibuffer, we get
2882 back the C-mode menu. Thus we do:
2883 Rename B to X (C-mode to minibuf menu)
2884 Rename X to B (minibuf to C-mode menu).
2885 If the X menu hasn't been invoked, the menu under B
2886 is up to date when leaving the minibuffer. */
2887 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2888 char *utf8_label = get_utf8_string (val->name);
2890 /* GTK menu items don't notice when their labels have been
2891 changed from underneath them, so we have to explicitly
2892 use g_object_notify to tell listeners (e.g., a GMenuModel
2893 bridge that might be loaded) that the item's label has
2894 changed. */
2895 gtk_label_set_text (wlabel, utf8_label);
2896 #if GTK_CHECK_VERSION (2, 16, 0)
2897 g_object_notify (G_OBJECT (witem), "label");
2898 #endif
2899 if (utf8_label) g_free (utf8_label);
2900 iter = g_list_next (iter);
2901 val = val->next;
2902 ++pos;
2904 else if (! val_in_menubar && iter_in_new_menubar)
2906 /* This corresponds to:
2907 Current: A B C
2908 New: A X B C
2909 Insert X. */
2911 int nr = pos;
2912 GSList *group = 0;
2913 GtkWidget *w = xg_create_one_menuitem (val,
2915 select_cb,
2916 highlight_cb,
2917 cl_data,
2918 &group);
2920 /* Create a possibly empty submenu for menu bar items, since some
2921 themes don't highlight items correctly without it. */
2922 GtkWidget *submenu = create_menus (NULL, f,
2923 select_cb, deactivate_cb,
2924 highlight_cb,
2925 0, 0, 0, cl_data, 0);
2927 gtk_widget_set_name (w, MENU_ITEM_NAME);
2928 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2929 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2931 g_list_free (*list);
2932 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2933 while (nr-- > 0) iter = g_list_next (iter);
2934 iter = g_list_next (iter);
2935 val = val->next;
2936 ++pos;
2938 else /* if (val_in_menubar && iter_in_new_menubar) */
2940 int nr = pos;
2941 /* This corresponds to:
2942 Current: A B C
2943 New: A C B
2944 Move C before B */
2946 g_object_ref (G_OBJECT (witem2));
2947 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2948 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2949 GTK_WIDGET (witem2), pos);
2950 g_object_unref (G_OBJECT (witem2));
2952 g_list_free (*list);
2953 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2954 while (nr-- > 0) iter = g_list_next (iter);
2955 if (iter) iter = g_list_next (iter);
2956 val = val->next;
2957 ++pos;
2961 /* Update the rest of the menu bar. */
2962 xg_update_menubar (menubar, f, list, iter, pos, val,
2963 select_cb, deactivate_cb, highlight_cb, cl_data);
2966 /* Update the menu item W so it corresponds to VAL.
2967 SELECT_CB is the callback to use when a menu item is selected.
2968 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2969 CL_DATA is the data to set in the widget for menu invocation. */
2971 static void
2972 xg_update_menu_item (widget_value *val,
2973 GtkWidget *w,
2974 GCallback select_cb,
2975 GCallback highlight_cb,
2976 xg_menu_cb_data *cl_data)
2978 GtkWidget *wchild;
2979 GtkLabel *wlbl = 0;
2980 GtkLabel *wkey = 0;
2981 char *utf8_label;
2982 char *utf8_key;
2983 const char *old_label = 0;
2984 const char *old_key = 0;
2985 xg_menu_item_cb_data *cb_data;
2986 bool label_changed = false;
2988 wchild = XG_BIN_CHILD (w);
2989 utf8_label = get_utf8_string (val->name);
2990 utf8_key = get_utf8_string (val->key);
2992 /* See if W is a menu item with a key. See make_menu_item above. */
2993 if (GTK_IS_BOX (wchild))
2995 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2997 wlbl = GTK_LABEL (list->data);
2998 wkey = GTK_LABEL (list->next->data);
2999 g_list_free (list);
3001 if (! utf8_key)
3003 /* Remove the key and keep just the label. */
3004 g_object_ref (G_OBJECT (wlbl));
3005 gtk_container_remove (GTK_CONTAINER (w), wchild);
3006 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
3007 g_object_unref (G_OBJECT (wlbl));
3008 wkey = 0;
3012 else /* Just a label. */
3014 wlbl = GTK_LABEL (wchild);
3016 /* Check if there is now a key. */
3017 if (utf8_key)
3019 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3020 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
3022 wlbl = GTK_LABEL (list->data);
3023 wkey = GTK_LABEL (list->next->data);
3024 g_list_free (list);
3026 gtk_container_remove (GTK_CONTAINER (w), wchild);
3027 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3031 if (wkey) old_key = gtk_label_get_label (wkey);
3032 if (wlbl) old_label = gtk_label_get_label (wlbl);
3034 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3036 label_changed = true;
3037 gtk_label_set_text (wkey, utf8_key);
3040 if (! old_label || strcmp (utf8_label, old_label) != 0)
3042 label_changed = true;
3043 gtk_label_set_text (wlbl, utf8_label);
3046 if (utf8_key) g_free (utf8_key);
3047 if (utf8_label) g_free (utf8_label);
3049 if (! val->enabled && gtk_widget_get_sensitive (w))
3050 gtk_widget_set_sensitive (w, FALSE);
3051 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3052 gtk_widget_set_sensitive (w, TRUE);
3054 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
3055 if (cb_data)
3057 cb_data->call_data = val->call_data;
3058 cb_data->help = val->help;
3059 cb_data->cl_data = cl_data;
3061 /* We assume the callback functions don't change. */
3062 if (val->call_data && ! val->contents)
3064 /* This item shall have a select callback. */
3065 if (! cb_data->select_id)
3066 cb_data->select_id
3067 = g_signal_connect (G_OBJECT (w), "activate",
3068 select_cb, cb_data);
3070 else if (cb_data->select_id)
3072 g_signal_handler_disconnect (w, cb_data->select_id);
3073 cb_data->select_id = 0;
3077 #if GTK_CHECK_VERSION (2, 16, 0)
3078 if (label_changed) /* See comment in xg_update_menubar. */
3079 g_object_notify (G_OBJECT (w), "label");
3080 #endif
3083 /* Update the toggle menu item W so it corresponds to VAL. */
3085 static void
3086 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3088 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3091 /* Update the radio menu item W so it corresponds to VAL. */
3093 static void
3094 xg_update_radio_item (widget_value *val, GtkWidget *w)
3096 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3099 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3100 SUBMENU may be NULL, in that case a new menu is created.
3101 F is the frame the menu bar belongs to.
3102 VAL describes the contents of the menu bar.
3103 SELECT_CB is the callback to use when a menu item is selected.
3104 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3105 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3106 CL_DATA is the call back data to use for any newly created items.
3108 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3109 was NULL. */
3111 static GtkWidget *
3112 xg_update_submenu (GtkWidget *submenu,
3113 struct frame *f,
3114 widget_value *val,
3115 GCallback select_cb,
3116 GCallback deactivate_cb,
3117 GCallback highlight_cb,
3118 xg_menu_cb_data *cl_data)
3120 GtkWidget *newsub = submenu;
3121 GList *list = 0;
3122 GList *iter;
3123 widget_value *cur;
3124 GList *first_radio = 0;
3126 if (submenu)
3127 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3129 for (cur = val, iter = list;
3130 cur && iter;
3131 iter = g_list_next (iter), cur = cur->next)
3133 GtkWidget *w = GTK_WIDGET (iter->data);
3135 /* Remember first radio button in a group. If we get a mismatch in
3136 a radio group we must rebuild the whole group so that the connections
3137 in GTK becomes correct. */
3138 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3139 first_radio = iter;
3140 else if (cur->button_type != BUTTON_TYPE_RADIO
3141 && ! GTK_IS_RADIO_MENU_ITEM (w))
3142 first_radio = 0;
3144 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3146 if (! menu_separator_name_p (cur->name))
3147 break;
3149 else if (GTK_IS_CHECK_MENU_ITEM (w))
3151 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3152 break;
3153 xg_update_toggle_item (cur, w);
3154 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3156 else if (GTK_IS_RADIO_MENU_ITEM (w))
3158 if (cur->button_type != BUTTON_TYPE_RADIO)
3159 break;
3160 xg_update_radio_item (cur, w);
3161 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3163 else if (GTK_IS_MENU_ITEM (w))
3165 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3166 GtkWidget *sub;
3168 if (cur->button_type != BUTTON_TYPE_NONE ||
3169 menu_separator_name_p (cur->name))
3170 break;
3172 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3174 sub = gtk_menu_item_get_submenu (witem);
3175 if (sub && ! cur->contents)
3177 /* Not a submenu anymore. */
3178 g_object_ref (G_OBJECT (sub));
3179 remove_submenu (witem);
3180 gtk_widget_destroy (sub);
3182 else if (cur->contents)
3184 GtkWidget *nsub;
3186 nsub = xg_update_submenu (sub, f, cur->contents,
3187 select_cb, deactivate_cb,
3188 highlight_cb, cl_data);
3190 /* If this item just became a submenu, we must set it. */
3191 if (nsub != sub)
3192 gtk_menu_item_set_submenu (witem, nsub);
3195 else
3197 /* Structural difference. Remove everything from here and down
3198 in SUBMENU. */
3199 break;
3203 /* Remove widgets from first structural change. */
3204 if (iter)
3206 /* If we are adding new menu items below, we must remove from
3207 first radio button so that radio groups become correct. */
3208 if (cur && first_radio) xg_destroy_widgets (first_radio);
3209 else xg_destroy_widgets (iter);
3212 if (cur)
3214 /* More items added. Create them. */
3215 newsub = create_menus (cur,
3217 select_cb,
3218 deactivate_cb,
3219 highlight_cb,
3222 submenu,
3223 cl_data,
3227 if (list) g_list_free (list);
3229 return newsub;
3232 /* Update the MENUBAR.
3233 F is the frame the menu bar belongs to.
3234 VAL describes the contents of the menu bar.
3235 If DEEP_P, rebuild all but the top level menu names in
3236 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3237 SELECT_CB is the callback to use when a menu item is selected.
3238 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3239 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3241 void
3242 xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3243 widget_value *val, bool deep_p,
3244 GCallback select_cb, GCallback deactivate_cb,
3245 GCallback highlight_cb)
3247 xg_menu_cb_data *cl_data;
3248 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3250 if (! list) return;
3252 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
3254 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3255 select_cb, deactivate_cb, highlight_cb, cl_data);
3257 if (deep_p)
3259 widget_value *cur;
3261 /* Update all sub menus.
3262 We must keep the submenus (GTK menu item widgets) since the
3263 X Window in the XEvent that activates the menu are those widgets. */
3265 /* Update cl_data, menu_item things in F may have changed. */
3266 update_cl_data (cl_data, f, highlight_cb);
3268 for (cur = val->contents; cur; cur = cur->next)
3270 GList *iter;
3271 GtkWidget *sub = 0;
3272 GtkWidget *newsub;
3273 GtkMenuItem *witem = 0;
3275 /* Find sub menu that corresponds to val and update it. */
3276 for (iter = list ; iter; iter = g_list_next (iter))
3278 witem = GTK_MENU_ITEM (iter->data);
3279 if (xg_item_label_same_p (witem, cur->name))
3281 sub = gtk_menu_item_get_submenu (witem);
3282 break;
3286 newsub = xg_update_submenu (sub,
3288 cur->contents,
3289 select_cb,
3290 deactivate_cb,
3291 highlight_cb,
3292 cl_data);
3293 /* sub may still be NULL. If we just updated non deep and added
3294 a new menu bar item, it has no sub menu yet. So we set the
3295 newly created sub menu under witem. */
3296 if (newsub != sub && witem != 0)
3298 xg_set_screen (newsub, f);
3299 gtk_menu_item_set_submenu (witem, newsub);
3304 g_list_free (list);
3305 gtk_widget_show_all (menubar);
3308 /* Callback called when the menu bar W is mapped.
3309 Used to find the height of the menu bar if we didn't get it
3310 after showing the widget. */
3312 static void
3313 menubar_map_cb (GtkWidget *w, gpointer user_data)
3315 GtkRequisition req;
3316 struct frame *f = user_data;
3317 gtk_widget_get_preferred_size (w, NULL, &req);
3318 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3320 FRAME_MENUBAR_HEIGHT (f) = req.height;
3321 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3325 /* Recompute all the widgets of frame F, when the menu bar has been
3326 changed. */
3328 void
3329 xg_update_frame_menubar (struct frame *f)
3331 struct x_output *x = f->output_data.x;
3332 GtkRequisition req;
3334 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3335 return;
3337 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3338 return; /* Already done this, happens for frames created invisible. */
3340 block_input ();
3342 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3343 FALSE, FALSE, 0);
3344 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3346 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3347 gtk_widget_show_all (x->menubar_widget);
3348 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3350 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3352 FRAME_MENUBAR_HEIGHT (f) = req.height;
3353 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3355 unblock_input ();
3358 /* Get rid of the menu bar of frame F, and free its storage.
3359 This is used when deleting a frame, and when turning off the menu bar. */
3361 void
3362 free_frame_menubar (struct frame *f)
3364 struct x_output *x = f->output_data.x;
3366 if (x->menubar_widget)
3368 block_input ();
3370 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3371 /* The menubar and its children shall be deleted when removed from
3372 the container. */
3373 x->menubar_widget = 0;
3374 FRAME_MENUBAR_HEIGHT (f) = 0;
3375 adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
3376 unblock_input ();
3380 bool
3381 xg_event_is_for_menubar (struct frame *f, const XEvent *event)
3383 struct x_output *x = f->output_data.x;
3384 GList *iter;
3385 GdkRectangle rec;
3386 GList *list;
3387 GdkDisplay *gdpy;
3388 GdkWindow *gw;
3389 GdkEvent gevent;
3390 GtkWidget *gwdesc;
3392 if (! x->menubar_widget) return 0;
3394 if (! (event->xbutton.x >= 0
3395 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3396 && event->xbutton.y >= 0
3397 && event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)
3398 && event->xbutton.same_screen))
3399 return 0;
3401 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3402 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3403 if (! gw) return 0;
3404 gevent.any.window = gw;
3405 gevent.any.type = GDK_NOTHING;
3406 gwdesc = gtk_get_event_widget (&gevent);
3407 if (! gwdesc) return 0;
3408 if (! GTK_IS_MENU_BAR (gwdesc)
3409 && ! GTK_IS_MENU_ITEM (gwdesc)
3410 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3411 return 0;
3413 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3414 if (! list) return 0;
3415 rec.x = event->xbutton.x;
3416 rec.y = event->xbutton.y;
3417 rec.width = 1;
3418 rec.height = 1;
3420 for (iter = list ; iter; iter = g_list_next (iter))
3422 GtkWidget *w = GTK_WIDGET (iter->data);
3423 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3424 break;
3426 g_list_free (list);
3427 return iter != 0;
3432 /***********************************************************************
3433 Scroll bar functions
3434 ***********************************************************************/
3437 /* Setting scroll bar values invokes the callback. Use this variable
3438 to indicate that callback should do nothing. */
3440 bool xg_ignore_gtk_scrollbar;
3442 /* Width and height of scroll bars for the current theme. */
3443 static int scroll_bar_width_for_theme;
3444 static int scroll_bar_height_for_theme;
3446 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3447 may be larger than 32 bits. Keep a mapping from integer index to widget
3448 pointers to get around the 32 bit limitation. */
3450 static struct
3452 GtkWidget **widgets;
3453 ptrdiff_t max_size;
3454 ptrdiff_t used;
3455 } id_to_widget;
3457 /* Grow this much every time we need to allocate more */
3459 #define ID_TO_WIDGET_INCR 32
3461 /* Store the widget pointer W in id_to_widget and return the integer index. */
3463 static ptrdiff_t
3464 xg_store_widget_in_map (GtkWidget *w)
3466 ptrdiff_t i;
3468 if (id_to_widget.max_size == id_to_widget.used)
3470 ptrdiff_t new_size;
3471 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3472 memory_full (SIZE_MAX);
3474 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3475 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3476 new_size, sizeof (GtkWidget *));
3478 for (i = id_to_widget.max_size; i < new_size; ++i)
3479 id_to_widget.widgets[i] = 0;
3480 id_to_widget.max_size = new_size;
3483 /* Just loop over the array and find a free place. After all,
3484 how many scroll bars are we creating? Should be a small number.
3485 The check above guarantees we will find a free place. */
3486 for (i = 0; i < id_to_widget.max_size; ++i)
3488 if (! id_to_widget.widgets[i])
3490 id_to_widget.widgets[i] = w;
3491 ++id_to_widget.used;
3493 return i;
3497 /* Should never end up here */
3498 emacs_abort ();
3501 /* Remove pointer at IDX from id_to_widget.
3502 Called when scroll bar is destroyed. */
3504 static void
3505 xg_remove_widget_from_map (ptrdiff_t idx)
3507 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3509 id_to_widget.widgets[idx] = 0;
3510 --id_to_widget.used;
3514 /* Get the widget pointer at IDX from id_to_widget. */
3516 static GtkWidget *
3517 xg_get_widget_from_map (ptrdiff_t idx)
3519 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3520 return id_to_widget.widgets[idx];
3522 return 0;
3525 static void
3526 update_theme_scrollbar_width (void)
3528 #ifdef HAVE_GTK3
3529 GtkAdjustment *vadj;
3530 #else
3531 GtkObject *vadj;
3532 #endif
3533 GtkWidget *wscroll;
3534 int w = 0, b = 0;
3536 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3537 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3538 g_object_ref_sink (G_OBJECT (wscroll));
3539 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3540 gtk_widget_destroy (wscroll);
3541 g_object_unref (G_OBJECT (wscroll));
3542 w += 2*b;
3543 #ifndef HAVE_GTK3
3544 if (w < 16) w = 16;
3545 #endif
3546 scroll_bar_width_for_theme = w;
3549 static void
3550 update_theme_scrollbar_height (void)
3552 #ifdef HAVE_GTK3
3553 GtkAdjustment *hadj;
3554 #else
3555 GtkObject *hadj;
3556 #endif
3557 GtkWidget *wscroll;
3558 int w = 0, b = 0;
3560 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX, 0.1, 0.1, 0.1);
3561 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3562 g_object_ref_sink (G_OBJECT (wscroll));
3563 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3564 gtk_widget_destroy (wscroll);
3565 g_object_unref (G_OBJECT (wscroll));
3566 w += 2*b;
3567 if (w < 12) w = 12;
3568 scroll_bar_height_for_theme = w;
3572 xg_get_default_scrollbar_width (void)
3574 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3578 xg_get_default_scrollbar_height (void)
3580 /* Apparently there's no default height for themes. */
3581 return scroll_bar_width_for_theme * xg_get_gdk_scale ();
3584 /* Return the scrollbar id for X Window WID on display DPY.
3585 Return -1 if WID not in id_to_widget. */
3587 ptrdiff_t
3588 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3590 ptrdiff_t idx;
3591 GtkWidget *w;
3593 w = xg_win_to_widget (dpy, wid);
3595 if (w)
3597 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3598 if (id_to_widget.widgets[idx] == w)
3599 return idx;
3602 return -1;
3605 /* Callback invoked when scroll bar WIDGET is destroyed.
3606 DATA is the index into id_to_widget for WIDGET.
3607 We free pointer to last scroll bar values here and remove the index. */
3609 static void
3610 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3612 intptr_t id = (intptr_t) data;
3613 xg_remove_widget_from_map (id);
3616 static void
3617 xg_finish_scroll_bar_creation (struct frame *f,
3618 GtkWidget *wscroll,
3619 struct scroll_bar *bar,
3620 GCallback scroll_callback,
3621 GCallback end_callback,
3622 const char *scroll_bar_name)
3624 GtkWidget *webox = gtk_event_box_new ();
3626 gtk_widget_set_name (wscroll, scroll_bar_name);
3627 #ifndef HAVE_GTK3
3628 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3629 #endif
3630 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3632 ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll);
3634 g_signal_connect (G_OBJECT (wscroll),
3635 "destroy",
3636 G_CALLBACK (xg_gtk_scroll_destroy),
3637 (gpointer) scroll_id);
3638 g_signal_connect (G_OBJECT (wscroll),
3639 "change-value",
3640 scroll_callback,
3641 (gpointer) bar);
3642 g_signal_connect (G_OBJECT (wscroll),
3643 "button-release-event",
3644 end_callback,
3645 (gpointer) bar);
3647 /* The scroll bar widget does not draw on a window of its own. Instead
3648 it draws on the parent window, in this case the edit widget. So
3649 whenever the edit widget is cleared, the scroll bar needs to redraw
3650 also, which causes flicker. Put an event box between the edit widget
3651 and the scroll bar, so the scroll bar instead draws itself on the
3652 event box window. */
3653 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3654 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3656 xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
3658 /* N.B. The event box doesn't become a real X11 window until we ask
3659 for its XID via GTK_WIDGET_TO_X_WIN. If the event box is not a
3660 real X window, it and its scroll-bar child try to draw on the
3661 Emacs main window, which we draw over using Xlib. */
3662 gtk_widget_realize (webox);
3663 GTK_WIDGET_TO_X_WIN (webox);
3665 /* Set the cursor to an arrow. */
3666 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
3668 bar->x_window = scroll_id;
3671 /* Create a scroll bar widget for frame F. Store the scroll bar
3672 in BAR.
3673 SCROLL_CALLBACK is the callback to invoke when the value of the
3674 bar changes.
3675 END_CALLBACK is the callback to invoke when scrolling ends.
3676 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3677 to set resources for the widget. */
3679 void
3680 xg_create_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 #ifdef HAVE_GTK3
3688 GtkAdjustment *vadj;
3689 #else
3690 GtkObject *vadj;
3691 #endif
3693 /* Page, step increment values are not so important here, they
3694 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3695 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3696 0.1, 0.1, 0.1);
3698 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3700 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3701 end_callback, scroll_bar_name);
3702 bar->horizontal = 0;
3705 /* Create a horizontal scroll bar widget for frame F. Store the scroll
3706 bar in BAR. SCROLL_CALLBACK is the callback to invoke when the value
3707 of the bar changes. END_CALLBACK is the callback to invoke when
3708 scrolling ends. SCROLL_BAR_NAME is the name we use for the scroll
3709 bar. Can be used to set resources for the widget. */
3711 void
3712 xg_create_horizontal_scroll_bar (struct frame *f,
3713 struct scroll_bar *bar,
3714 GCallback scroll_callback,
3715 GCallback end_callback,
3716 const char *scroll_bar_name)
3718 GtkWidget *wscroll;
3719 #ifdef HAVE_GTK3
3720 GtkAdjustment *hadj;
3721 #else
3722 GtkObject *hadj;
3723 #endif
3725 /* Page, step increment values are not so important here, they
3726 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3727 hadj = gtk_adjustment_new (YG_SB_MIN, YG_SB_MIN, YG_SB_MAX,
3728 0.1, 0.1, 0.1);
3730 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj));
3732 xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
3733 end_callback, scroll_bar_name);
3734 bar->horizontal = 1;
3737 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3739 void
3740 xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
3742 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3743 if (w)
3745 GtkWidget *wparent = gtk_widget_get_parent (w);
3746 gtk_widget_destroy (w);
3747 gtk_widget_destroy (wparent);
3748 SET_FRAME_GARBAGED (f);
3752 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3753 in frame F.
3754 TOP/LEFT are the new pixel positions where the bar shall appear.
3755 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3757 void
3758 xg_update_scrollbar_pos (struct frame *f,
3759 ptrdiff_t scrollbar_id,
3760 int top,
3761 int left,
3762 int width,
3763 int height)
3765 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3766 if (wscroll)
3768 GtkWidget *wfixed = f->output_data.x->edit_widget;
3769 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3770 gint msl;
3771 int scale = xg_get_gdk_scale ();
3773 top /= scale;
3774 left /= scale;
3775 height /= scale;
3776 left -= (scale - 1) * ((width / scale) >> 1);
3778 /* Clear out old position. */
3779 int oldx = -1, oldy = -1, oldw, oldh;
3780 if (gtk_widget_get_parent (wparent) == wfixed)
3782 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3783 "x", &oldx, "y", &oldy, NULL);
3784 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3787 /* Move and resize to new values. */
3788 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3789 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3790 if (msl > height)
3792 /* No room. Hide scroll bar as some themes output a warning if
3793 the height is less than the min size. */
3794 gtk_widget_hide (wparent);
3795 gtk_widget_hide (wscroll);
3797 else
3799 gtk_widget_show_all (wparent);
3800 gtk_widget_set_size_request (wscroll, width, height);
3802 if (oldx != -1 && oldw > 0 && oldh > 0)
3804 /* Clear under old scroll bar position. */
3805 oldw += (scale - 1) * oldw;
3806 oldx -= (scale - 1) * oldw;
3807 x_clear_area (f, oldx, oldy, oldw, oldh);
3810 /* GTK does not redraw until the main loop is entered again, but
3811 if there are no X events pending we will not enter it. So we sync
3812 here to get some events. */
3814 x_sync (f);
3815 SET_FRAME_GARBAGED (f);
3816 cancel_mouse_face (f);
3821 /* Update the position of the horizontal scroll bar represented by SCROLLBAR_ID
3822 in frame F.
3823 TOP/LEFT are the new pixel positions where the bar shall appear.
3824 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3826 void
3827 xg_update_horizontal_scrollbar_pos (struct frame *f,
3828 ptrdiff_t scrollbar_id,
3829 int top,
3830 int left,
3831 int width,
3832 int height)
3835 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3837 if (wscroll)
3839 GtkWidget *wfixed = f->output_data.x->edit_widget;
3840 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3841 gint msl;
3843 /* Clear out old position. */
3844 int oldx = -1, oldy = -1, oldw, oldh;
3845 if (gtk_widget_get_parent (wparent) == wfixed)
3847 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3848 "x", &oldx, "y", &oldy, NULL);
3849 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3852 /* Move and resize to new values. */
3853 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3854 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3855 if (msl > width)
3857 /* No room. Hide scroll bar as some themes output a warning if
3858 the width is less than the min size. */
3859 gtk_widget_hide (wparent);
3860 gtk_widget_hide (wscroll);
3862 else
3864 gtk_widget_show_all (wparent);
3865 gtk_widget_set_size_request (wscroll, width, height);
3867 if (oldx != -1 && oldw > 0 && oldh > 0)
3868 /* Clear under old scroll bar position. */
3869 x_clear_area (f, oldx, oldy, oldw, oldh);
3871 /* GTK does not redraw until the main loop is entered again, but
3872 if there are no X events pending we will not enter it. So we sync
3873 here to get some events. */
3875 x_sync (f);
3876 SET_FRAME_GARBAGED (f);
3877 cancel_mouse_face (f);
3882 /* Get the current value of the range, truncated to an integer. */
3884 static int
3885 int_gtk_range_get_value (GtkRange *range)
3887 return gtk_range_get_value (range);
3891 /* Set the thumb size and position of scroll bar BAR. We are currently
3892 displaying PORTION out of a whole WHOLE, and our position POSITION. */
3894 void
3895 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
3896 int portion,
3897 int position,
3898 int whole)
3900 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3902 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
3904 if (wscroll && bar->dragging == -1)
3906 GtkAdjustment *adj;
3907 gdouble shown;
3908 gdouble top;
3909 int size, value;
3910 int old_size;
3911 int new_step;
3912 bool changed = 0;
3914 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3916 if (scroll_bar_adjust_thumb_portion_p)
3918 /* We do the same as for MOTIF in xterm.c, use 30 chars per
3919 line rather than the real portion value. This makes the
3920 thumb less likely to resize and that looks better. */
3921 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
3923 /* When the thumb is at the bottom, position == whole.
3924 So we need to increase `whole' to make space for the thumb. */
3925 whole += portion;
3928 if (whole <= 0)
3929 top = 0, shown = 1;
3930 else
3932 top = (gdouble) position / whole;
3933 shown = (gdouble) portion / whole;
3936 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
3937 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
3939 /* Assume all lines are of equal size. */
3940 new_step = size / max (1, FRAME_LINES (f));
3942 old_size = gtk_adjustment_get_page_size (adj);
3943 if (old_size != size)
3945 int old_step = gtk_adjustment_get_step_increment (adj);
3946 if (old_step != new_step)
3948 gtk_adjustment_set_page_size (adj, size);
3949 gtk_adjustment_set_step_increment (adj, new_step);
3950 /* Assume a page increment is about 95% of the page size */
3951 gtk_adjustment_set_page_increment (adj, size - size / 20);
3952 changed = 1;
3956 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3958 block_input ();
3960 /* gtk_range_set_value invokes the callback. Set
3961 ignore_gtk_scrollbar to make the callback do nothing */
3962 xg_ignore_gtk_scrollbar = 1;
3964 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3965 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3966 else if (changed)
3967 gtk_adjustment_changed (adj);
3969 xg_ignore_gtk_scrollbar = 0;
3971 unblock_input ();
3976 /* Set the thumb size and position of horizontal scroll bar BAR. We are
3977 currently displaying PORTION out of a whole WHOLE, and our position
3978 POSITION. */
3979 void
3980 xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
3981 int portion,
3982 int position,
3983 int whole)
3985 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3987 if (wscroll && bar->dragging == -1)
3989 GtkAdjustment *adj;
3990 int lower = 0;
3991 int upper = max (whole - 1, 0);
3992 int pagesize = min (upper, max (portion, 0));
3993 int value = max (0, min (position, upper - pagesize));
3994 /* These should be set to something more <portion, whole>
3995 related. */
3996 int page_increment = 4;
3997 int step_increment = 1;
3999 block_input ();
4000 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
4001 gtk_adjustment_configure (adj, (gdouble) value, (gdouble) lower,
4002 (gdouble) upper, (gdouble) step_increment,
4003 (gdouble) page_increment, (gdouble) pagesize);
4004 gtk_adjustment_changed (adj);
4005 unblock_input ();
4009 /* Return true if EVENT is for a scroll bar in frame F.
4010 When the same X window is used for several Gtk+ widgets, we cannot
4011 say for sure based on the X window alone if an event is for the
4012 frame. This function does additional checks. */
4014 bool
4015 xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
4017 bool retval = 0;
4019 if (f && event->type == ButtonPress && event->xbutton.button < 4)
4021 /* Check if press occurred outside the edit widget. */
4022 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
4023 GdkWindow *gwin;
4024 #ifdef HAVE_GTK3
4025 GdkDevice *gdev = gdk_device_manager_get_client_pointer
4026 (gdk_display_get_device_manager (gdpy));
4027 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
4028 #else
4029 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
4030 #endif
4031 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
4033 else if (f
4034 && ((event->type == ButtonRelease && event->xbutton.button < 4)
4035 || event->type == MotionNotify))
4037 /* If we are releasing or moving the scroll bar, it has the grab. */
4038 GtkWidget *w = gtk_grab_get_current ();
4039 retval = w != 0 && GTK_IS_SCROLLBAR (w);
4042 return retval;
4046 /***********************************************************************
4047 Printing
4048 ***********************************************************************/
4049 #ifdef USE_CAIRO
4050 static GtkPrintSettings *print_settings = NULL;
4051 static GtkPageSetup *page_setup = NULL;
4053 void
4054 xg_page_setup_dialog (void)
4056 GtkPageSetup *new_page_setup = NULL;
4058 if (print_settings == NULL)
4059 print_settings = gtk_print_settings_new ();
4060 new_page_setup = gtk_print_run_page_setup_dialog (NULL, page_setup,
4061 print_settings);
4062 if (page_setup)
4063 g_object_unref (page_setup);
4064 page_setup = new_page_setup;
4067 Lisp_Object
4068 xg_get_page_setup (void)
4070 Lisp_Object orientation_symbol;
4072 if (page_setup == NULL)
4073 page_setup = gtk_page_setup_new ();
4075 switch (gtk_page_setup_get_orientation (page_setup))
4077 case GTK_PAGE_ORIENTATION_PORTRAIT:
4078 orientation_symbol = Qportrait;
4079 break;
4080 case GTK_PAGE_ORIENTATION_LANDSCAPE:
4081 orientation_symbol = Qlandscape;
4082 break;
4083 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
4084 orientation_symbol = Qreverse_portrait;
4085 break;
4086 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
4087 orientation_symbol = Qreverse_landscape;
4088 break;
4089 default:
4090 eassume (false);
4093 return listn (CONSTYPE_HEAP, 7,
4094 Fcons (Qorientation, orientation_symbol),
4095 #define MAKE_FLOAT_PAGE_SETUP(f) make_float (f (page_setup, GTK_UNIT_POINTS))
4096 Fcons (Qwidth,
4097 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_width)),
4098 Fcons (Qheight,
4099 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_page_height)),
4100 Fcons (Qleft_margin,
4101 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_left_margin)),
4102 Fcons (Qright_margin,
4103 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_right_margin)),
4104 Fcons (Qtop_margin,
4105 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_top_margin)),
4106 Fcons (Qbottom_margin,
4107 MAKE_FLOAT_PAGE_SETUP (gtk_page_setup_get_bottom_margin))
4108 #undef MAKE_FLOAT_PAGE_SETUP
4112 static void
4113 draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
4114 gint page_nr, gpointer user_data)
4116 Lisp_Object frames = *((Lisp_Object *) user_data);
4117 struct frame *f = XFRAME (Fnth (make_number (page_nr), frames));
4118 cairo_t *cr = gtk_print_context_get_cairo_context (context);
4120 x_cr_draw_frame (cr, f);
4123 void
4124 xg_print_frames_dialog (Lisp_Object frames)
4126 GtkPrintOperation *print;
4127 GtkPrintOperationResult res;
4129 print = gtk_print_operation_new ();
4130 if (print_settings != NULL)
4131 gtk_print_operation_set_print_settings (print, print_settings);
4132 if (page_setup != NULL)
4133 gtk_print_operation_set_default_page_setup (print, page_setup);
4134 gtk_print_operation_set_n_pages (print, XINT (Flength (frames)));
4135 g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), &frames);
4136 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
4137 NULL, NULL);
4138 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
4140 if (print_settings != NULL)
4141 g_object_unref (print_settings);
4142 print_settings =
4143 g_object_ref (gtk_print_operation_get_print_settings (print));
4145 g_object_unref (print);
4148 #endif /* USE_CAIRO */
4152 /***********************************************************************
4153 Tool bar functions
4154 ***********************************************************************/
4155 /* The key for the data we put in the GtkImage widgets. The data is
4156 the image used by Emacs. We use this to see if we need to update
4157 the GtkImage with a new image. */
4158 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
4160 /* The key for storing the latest modifiers so the activate callback can
4161 get them. */
4162 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
4164 /* The key for the data we put in the GtkImage widgets. The data is
4165 the stock name used by Emacs. We use this to see if we need to update
4166 the GtkImage with a new image. */
4167 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
4169 /* As above, but this is used for named theme widgets, as opposed to
4170 stock items. */
4171 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
4173 /* Callback function invoked when a tool bar item is pressed.
4174 W is the button widget in the tool bar that got pressed,
4175 CLIENT_DATA is an integer that is the index of the button in the
4176 tool bar. 0 is the first button. */
4178 static gboolean
4179 xg_tool_bar_button_cb (GtkWidget *widget,
4180 GdkEventButton *event,
4181 gpointer user_data)
4183 intptr_t state = event->state;
4184 gpointer ptr = (gpointer) state;
4185 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
4186 return FALSE;
4190 /* Callback function invoked when a tool bar item is pressed.
4191 W is the button widget in the tool bar that got pressed,
4192 CLIENT_DATA is an integer that is the index of the button in the
4193 tool bar. 0 is the first button. */
4195 static void
4196 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
4198 intptr_t idx = (intptr_t) client_data;
4199 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
4200 intptr_t mod = (intptr_t) gmod;
4202 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4203 Lisp_Object key, frame;
4204 struct input_event event;
4205 EVENT_INIT (event);
4207 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4208 return;
4210 idx *= TOOL_BAR_ITEM_NSLOTS;
4212 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
4213 XSETFRAME (frame, f);
4215 /* We generate two events here. The first one is to set the prefix
4216 to `(tool_bar)', see keyboard.c. */
4217 event.kind = TOOL_BAR_EVENT;
4218 event.frame_or_window = frame;
4219 event.arg = frame;
4220 kbd_buffer_store_event (&event);
4222 event.kind = TOOL_BAR_EVENT;
4223 event.frame_or_window = frame;
4224 event.arg = key;
4225 /* Convert between the modifier bits GDK uses and the modifier bits
4226 Emacs uses. This assumes GDK and X masks are the same, which they are when
4227 this is written. */
4228 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
4229 kbd_buffer_store_event (&event);
4231 /* Return focus to the frame after we have clicked on a detached
4232 tool bar button. */
4233 x_focus_frame (f);
4236 static GtkWidget *
4237 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4239 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
4240 GtkWidget *c1 = clist->data;
4241 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
4243 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4244 g_list_free (clist);
4245 return GTK_IS_LABEL (c1) ? c1 : c2;
4249 /* This callback is called when the mouse enters or leaves a tool bar item.
4250 It is used for displaying and hiding the help text.
4251 W is the tool bar item, a button.
4252 EVENT is either an enter event or leave event.
4253 CLIENT_DATA is an integer that is the index of the button in the
4254 tool bar. 0 is the first button.
4256 Returns FALSE to tell GTK to keep processing this event. */
4258 static gboolean
4259 xg_tool_bar_help_callback (GtkWidget *w,
4260 GdkEventCrossing *event,
4261 gpointer client_data)
4263 intptr_t idx = (intptr_t) client_data;
4264 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4265 Lisp_Object help, frame;
4267 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4268 return FALSE;
4270 if (event->type == GDK_ENTER_NOTIFY)
4272 idx *= TOOL_BAR_ITEM_NSLOTS;
4273 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4275 if (NILP (help))
4276 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4278 else
4279 help = Qnil;
4281 XSETFRAME (frame, f);
4282 kbd_buffer_store_help_event (frame, help);
4284 return FALSE;
4288 /* This callback is called when a tool bar item shall be redrawn.
4289 It modifies the expose event so that the GtkImage widget redraws the
4290 whole image. This to overcome a bug that makes GtkImage draw the image
4291 in the wrong place when it tries to redraw just a part of the image.
4292 W is the GtkImage to be redrawn.
4293 EVENT is the expose event for W.
4294 CLIENT_DATA is unused.
4296 Returns FALSE to tell GTK to keep processing this event. */
4298 #ifndef HAVE_GTK3
4299 static gboolean
4300 xg_tool_bar_item_expose_callback (GtkWidget *w,
4301 GdkEventExpose *event,
4302 gpointer client_data)
4304 gint width, height;
4306 gdk_drawable_get_size (event->window, &width, &height);
4307 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4308 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4310 event->area.x = max (0, event->area.x);
4311 event->area.y = max (0, event->area.y);
4313 event->area.width = max (width, event->area.width);
4314 event->area.height = max (height, event->area.height);
4316 return FALSE;
4318 #endif
4320 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4321 #define toolbar_set_orientation(w, o) \
4322 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4323 #else
4324 #define toolbar_set_orientation(w, o) \
4325 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4326 #endif
4328 /* Attach a tool bar to frame F. */
4330 static void
4331 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
4333 struct x_output *x = f->output_data.x;
4334 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4335 GtkWidget *top_widget = x->toolbar_widget;
4337 toolbar_set_orientation (x->toolbar_widget,
4338 into_hbox
4339 ? GTK_ORIENTATION_VERTICAL
4340 : GTK_ORIENTATION_HORIZONTAL);
4342 if (into_hbox)
4344 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
4345 FALSE, FALSE, 0);
4347 if (EQ (pos, Qleft))
4348 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4349 top_widget,
4351 x->toolbar_in_hbox = true;
4353 else
4355 bool vbox_pos = x->menubar_widget != 0;
4356 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
4357 FALSE, FALSE, 0);
4359 if (EQ (pos, Qtop))
4360 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4361 top_widget,
4362 vbox_pos);
4363 x->toolbar_in_hbox = false;
4365 x->toolbar_is_packed = true;
4368 static bool xg_update_tool_bar_sizes (struct frame *f);
4370 static void
4371 tb_size_cb (GtkWidget *widget,
4372 GdkRectangle *allocation,
4373 gpointer user_data)
4375 /* When tool bar is created it has one preferred size. But when size is
4376 allocated between widgets, it may get another. So we must update
4377 size hints if tool bar size changes. Seen on Fedora 18 at least. */
4378 struct frame *f = user_data;
4380 if (xg_update_tool_bar_sizes (f))
4382 frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
4383 adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
4387 /* Create a tool bar for frame F. */
4389 static void
4390 xg_create_tool_bar (struct frame *f)
4392 struct x_output *x = f->output_data.x;
4393 #if GTK_CHECK_VERSION (3, 3, 6)
4394 GtkStyleContext *gsty;
4395 #endif
4396 struct xg_frame_tb_info *tbinfo
4397 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4398 TB_INFO_KEY);
4399 if (! tbinfo)
4401 tbinfo = xmalloc (sizeof (*tbinfo));
4402 tbinfo->last_tool_bar = Qnil;
4403 tbinfo->style = Qnil;
4404 tbinfo->hmargin = tbinfo->vmargin = 0;
4405 tbinfo->dir = GTK_TEXT_DIR_NONE;
4406 tbinfo->n_last_items = 0;
4407 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4408 TB_INFO_KEY,
4409 tbinfo);
4412 x->toolbar_widget = gtk_toolbar_new ();
4414 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4416 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4417 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4418 g_signal_connect (x->toolbar_widget, "size-allocate",
4419 G_CALLBACK (tb_size_cb), f);
4420 #if GTK_CHECK_VERSION (3, 3, 6)
4421 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4422 gtk_style_context_add_class (gsty, "primary-toolbar");
4423 #endif
4427 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4429 /* Find the right-to-left image named by RTL in the tool bar images for F.
4430 Returns IMAGE if RTL is not found. */
4432 static Lisp_Object
4433 find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
4435 int i;
4436 Lisp_Object file, rtl_name;
4438 rtl_name = Ffile_name_nondirectory (rtl);
4440 for (i = 0; i < f->n_tool_bar_items; ++i)
4442 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4443 if (!NILP (file = file_for_image (rtl_image)))
4445 file = call1 (intern ("file-name-sans-extension"),
4446 Ffile_name_nondirectory (file));
4447 if (! NILP (Fequal (file, rtl_name)))
4449 image = rtl_image;
4450 break;
4455 return image;
4458 static GtkToolItem *
4459 xg_make_tool_item (struct frame *f,
4460 GtkWidget *wimage,
4461 GtkWidget **wbutton,
4462 const char *label,
4463 int i, bool horiz, bool text_image)
4465 GtkToolItem *ti = gtk_tool_item_new ();
4466 GtkWidget *vb = gtk_box_new (horiz
4467 ? GTK_ORIENTATION_HORIZONTAL
4468 : GTK_ORIENTATION_VERTICAL,
4470 GtkWidget *wb = gtk_button_new ();
4471 /* The eventbox is here so we can have tooltips on disabled items. */
4472 GtkWidget *weventbox = gtk_event_box_new ();
4473 #if GTK_CHECK_VERSION (3, 3, 6)
4474 GtkCssProvider *css_prov = gtk_css_provider_new ();
4475 GtkStyleContext *gsty;
4477 gtk_css_provider_load_from_data (css_prov,
4478 "GtkEventBox {"
4479 " background-color: transparent;"
4480 "}",
4481 -1, NULL);
4483 gsty = gtk_widget_get_style_context (weventbox);
4484 gtk_style_context_add_provider (gsty,
4485 GTK_STYLE_PROVIDER (css_prov),
4486 GTK_STYLE_PROVIDER_PRIORITY_USER);
4487 g_object_unref (css_prov);
4488 #endif
4490 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4492 if (wimage && !text_image)
4493 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4494 if (label)
4495 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4496 if (wimage && text_image)
4497 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4499 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4500 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4501 gtk_container_add (GTK_CONTAINER (wb), vb);
4502 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4503 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4505 if (wimage || label)
4507 intptr_t ii = i;
4508 gpointer gi = (gpointer) ii;
4510 g_signal_connect (G_OBJECT (wb), "clicked",
4511 G_CALLBACK (xg_tool_bar_callback),
4512 gi);
4514 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4516 #ifndef HAVE_GTK3
4517 /* Catch expose events to overcome an annoying redraw bug, see
4518 comment for xg_tool_bar_item_expose_callback. */
4519 g_signal_connect (G_OBJECT (ti),
4520 "expose-event",
4521 G_CALLBACK (xg_tool_bar_item_expose_callback),
4523 #endif
4524 gtk_tool_item_set_homogeneous (ti, FALSE);
4526 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4527 no distinction based on modifiers in the activate callback,
4528 so we have to do it ourselves. */
4529 g_signal_connect (wb, "button-release-event",
4530 G_CALLBACK (xg_tool_bar_button_cb),
4531 NULL);
4533 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4535 /* Use enter/leave notify to show help. We use the events
4536 rather than the GtkButton specific signals "enter" and
4537 "leave", so we can have only one callback. The event
4538 will tell us what kind of event it is. */
4539 g_signal_connect (G_OBJECT (weventbox),
4540 "enter-notify-event",
4541 G_CALLBACK (xg_tool_bar_help_callback),
4542 gi);
4543 g_signal_connect (G_OBJECT (weventbox),
4544 "leave-notify-event",
4545 G_CALLBACK (xg_tool_bar_help_callback),
4546 gi);
4549 if (wbutton) *wbutton = wb;
4551 return ti;
4554 static bool
4555 is_box_type (GtkWidget *vb, bool is_horizontal)
4557 #ifdef HAVE_GTK3
4558 bool ret = 0;
4559 if (GTK_IS_BOX (vb))
4561 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4562 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4563 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4565 return ret;
4566 #else
4567 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4568 #endif
4572 static bool
4573 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4574 const char *icon_name, const struct image *img,
4575 const char *label, bool horiz)
4577 gpointer old;
4578 GtkWidget *wimage;
4579 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4580 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4582 /* Check if the tool icon matches. */
4583 if (stock_name && wimage)
4585 old = g_object_get_data (G_OBJECT (wimage),
4586 XG_TOOL_BAR_STOCK_NAME);
4587 if (!old || strcmp (old, stock_name))
4588 return 1;
4590 else if (icon_name && wimage)
4592 old = g_object_get_data (G_OBJECT (wimage),
4593 XG_TOOL_BAR_ICON_NAME);
4594 if (!old || strcmp (old, icon_name))
4595 return 1;
4597 else if (wimage)
4599 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4600 XG_TOOL_BAR_IMAGE_DATA);
4601 Pixmap old_img = (Pixmap) gold_img;
4602 if (old_img != img->pixmap)
4603 return 1;
4606 /* Check button configuration and label. */
4607 if (is_box_type (vb, horiz)
4608 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4609 return 1;
4611 /* Ensure label is correct. */
4612 if (label && wlbl)
4613 gtk_label_set_text (GTK_LABEL (wlbl), label);
4614 return 0;
4617 static bool
4618 xg_update_tool_bar_sizes (struct frame *f)
4620 struct x_output *x = f->output_data.x;
4621 GtkRequisition req;
4622 int nl = 0, nr = 0, nt = 0, nb = 0;
4623 GtkWidget *top_widget = x->toolbar_widget;
4625 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
4626 if (x->toolbar_in_hbox)
4628 int pos;
4629 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4630 top_widget,
4631 "position", &pos, NULL);
4632 if (pos == 0) nl = req.width;
4633 else nr = req.width;
4635 else
4637 int pos;
4638 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4639 top_widget,
4640 "position", &pos, NULL);
4641 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4642 else nb = req.height;
4645 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4646 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4647 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4648 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4650 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4651 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4652 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4653 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4654 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4655 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4657 return true;
4659 else
4660 return false;
4663 static char *
4664 find_icon_from_name (char *name,
4665 GtkIconTheme *icon_theme,
4666 char **icon_name)
4668 #if ! GTK_CHECK_VERSION (3, 10, 0)
4669 GtkStockItem stock_item;
4670 #endif
4672 if (name[0] == 'n' && name[1] == ':')
4674 *icon_name = name + 2;
4675 name = NULL;
4677 if (! gtk_icon_theme_has_icon (icon_theme, *icon_name))
4678 *icon_name = NULL;
4681 #if ! GTK_CHECK_VERSION (3, 10, 0)
4682 else if (gtk_stock_lookup (name, &stock_item))
4683 *icon_name = NULL;
4684 #endif
4685 else if (gtk_icon_theme_has_icon (icon_theme, name))
4687 *icon_name = name;
4688 name = NULL;
4690 else
4692 name = NULL;
4693 *icon_name = NULL;
4696 return name;
4700 /* Update the tool bar for frame F. Add new buttons and remove old. */
4702 void
4703 update_frame_tool_bar (struct frame *f)
4705 int i, j;
4706 struct x_output *x = f->output_data.x;
4707 int hmargin = 0, vmargin = 0;
4708 GtkToolbar *wtoolbar;
4709 GtkToolItem *ti;
4710 GtkTextDirection dir;
4711 Lisp_Object style;
4712 bool text_image, horiz;
4713 struct xg_frame_tb_info *tbinfo;
4714 GdkScreen *screen;
4715 GtkIconTheme *icon_theme;
4718 if (! FRAME_GTK_WIDGET (f))
4719 return;
4721 block_input ();
4723 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4725 hmargin = XFASTINT (Vtool_bar_button_margin);
4726 vmargin = XFASTINT (Vtool_bar_button_margin);
4728 else if (CONSP (Vtool_bar_button_margin))
4730 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4731 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4733 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4734 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4737 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4738 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4739 i.e. zero. This means that margins less than
4740 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4741 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4742 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4744 if (! x->toolbar_widget)
4745 xg_create_tool_bar (f);
4747 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4748 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4750 style = Ftool_bar_get_system_style ();
4751 screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4752 icon_theme = gtk_icon_theme_get_for_screen (screen);
4754 /* Are we up to date? */
4755 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4756 TB_INFO_KEY);
4758 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4759 && tbinfo->n_last_items == f->n_tool_bar_items
4760 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4761 && tbinfo->dir == dir
4762 && ! NILP (Fequal (tbinfo->style, style))
4763 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4765 unblock_input ();
4766 return;
4769 tbinfo->last_tool_bar = f->tool_bar_items;
4770 tbinfo->n_last_items = f->n_tool_bar_items;
4771 tbinfo->style = style;
4772 tbinfo->hmargin = hmargin;
4773 tbinfo->vmargin = vmargin;
4774 tbinfo->dir = dir;
4776 text_image = EQ (style, Qtext_image_horiz);
4777 horiz = EQ (style, Qboth_horiz) || text_image;
4779 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4781 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4782 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4783 int idx;
4784 ptrdiff_t img_id;
4785 int icon_size = 0;
4786 struct image *img = NULL;
4787 Lisp_Object image;
4788 Lisp_Object stock = Qnil;
4789 char *stock_name = NULL;
4790 char *icon_name = NULL;
4791 Lisp_Object rtl;
4792 GtkWidget *wbutton = NULL;
4793 Lisp_Object specified_file;
4794 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4795 const char *label
4796 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4797 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4798 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4799 : "";
4801 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4803 /* If this is a separator, use a gtk separator item. */
4804 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4806 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4808 if (ti)
4809 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4810 GTK_WIDGET (ti));
4811 ti = gtk_separator_tool_item_new ();
4812 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4814 j++;
4815 continue;
4818 /* Otherwise, the tool-bar item is an ordinary button. */
4820 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4822 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4823 ti = NULL;
4826 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4828 /* Ignore invalid image specifications. */
4829 image = PROP (TOOL_BAR_ITEM_IMAGES);
4830 if (!valid_image_p (image))
4832 if (ti)
4833 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4834 GTK_WIDGET (ti));
4835 continue;
4838 specified_file = file_for_image (image);
4839 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4840 stock = call1 (Qx_gtk_map_stock, specified_file);
4842 if (CONSP (stock))
4844 Lisp_Object tem;
4845 for (tem = stock; CONSP (tem); tem = XCDR (tem))
4846 if (! NILP (tem) && STRINGP (XCAR (tem)))
4848 stock_name = find_icon_from_name (SSDATA (XCAR (tem)),
4849 icon_theme,
4850 &icon_name);
4851 if (stock_name || icon_name) break;
4854 else if (STRINGP (stock))
4856 stock_name = find_icon_from_name (SSDATA (stock),
4857 icon_theme,
4858 &icon_name);
4861 if (stock_name || icon_name)
4862 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4864 if (stock_name == NULL && icon_name == NULL)
4866 /* No stock image, or stock item not known. Try regular
4867 image. If image is a vector, choose it according to the
4868 button state. */
4869 if (dir == GTK_TEXT_DIR_RTL
4870 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4871 && STRINGP (rtl))
4872 image = find_rtl_image (f, image, rtl);
4874 if (VECTORP (image))
4876 if (enabled_p)
4877 idx = (selected_p
4878 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4879 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4880 else
4881 idx = (selected_p
4882 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4883 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
4885 eassert (ASIZE (image) >= idx);
4886 image = AREF (image, idx);
4888 else
4889 idx = -1;
4891 img_id = lookup_image (f, image);
4892 img = IMAGE_FROM_ID (f, img_id);
4893 prepare_image_for_display (f, img);
4895 if (img->load_failed_p || img->pixmap == None)
4897 if (ti)
4898 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4899 GTK_WIDGET (ti));
4900 continue;
4904 /* If there is an existing widget, check if it's stale; if so,
4905 remove it and make a new tool item from scratch. */
4906 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
4907 img, label, horiz))
4909 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4910 GTK_WIDGET (ti));
4911 ti = NULL;
4914 if (ti == NULL)
4916 GtkWidget *w;
4918 /* Save the image so we can see if an update is needed the
4919 next time we call xg_tool_item_match_p. */
4920 if (EQ (style, Qtext))
4921 w = NULL;
4922 else if (stock_name)
4925 #if GTK_CHECK_VERSION (3, 10, 0)
4926 w = gtk_image_new_from_icon_name (stock_name, icon_size);
4927 #else
4928 w = gtk_image_new_from_stock (stock_name, icon_size);
4929 #endif
4930 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
4931 (gpointer) xstrdup (stock_name),
4932 (GDestroyNotify) xfree);
4934 else if (icon_name)
4936 w = gtk_image_new_from_icon_name (icon_name, icon_size);
4937 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
4938 (gpointer) xstrdup (icon_name),
4939 (GDestroyNotify) xfree);
4941 else
4943 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
4944 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
4945 (gpointer)img->pixmap);
4948 #if GTK_CHECK_VERSION (3, 14, 0)
4949 if (w)
4951 gtk_widget_set_margin_start (w, hmargin);
4952 gtk_widget_set_margin_end (w, hmargin);
4953 gtk_widget_set_margin_top (w, vmargin);
4954 gtk_widget_set_margin_bottom (w, vmargin);
4956 #else
4957 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
4958 #endif
4959 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
4960 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4963 #undef PROP
4965 gtk_widget_set_sensitive (wbutton, enabled_p);
4966 j++;
4969 /* Remove buttons not longer needed. */
4972 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4973 if (ti)
4974 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4975 } while (ti != NULL);
4977 if (f->n_tool_bar_items != 0)
4979 if (! x->toolbar_is_packed)
4980 xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
4981 gtk_widget_show_all (x->toolbar_widget);
4982 if (xg_update_tool_bar_sizes (f))
4984 int inhibit
4985 = ((f->after_make_frame
4986 && !f->tool_bar_resized
4987 && (EQ (frame_inhibit_implied_resize, Qt)
4988 || (CONSP (frame_inhibit_implied_resize)
4989 && !NILP (Fmemq (Qtool_bar_lines,
4990 frame_inhibit_implied_resize))))
4991 /* This will probably fail to DTRT in the
4992 fullheight/-width cases. */
4993 && NILP (get_frame_param (f, Qfullscreen)))
4995 : 2);
4997 frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
4998 adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
5000 f->tool_bar_resized = f->tool_bar_redisplayed;
5003 unblock_input ();
5006 /* Deallocate all resources for the tool bar on frame F.
5007 Remove the tool bar. */
5009 void
5010 free_frame_tool_bar (struct frame *f)
5012 struct x_output *x = f->output_data.x;
5014 if (x->toolbar_widget)
5016 struct xg_frame_tb_info *tbinfo;
5017 GtkWidget *top_widget = x->toolbar_widget;
5019 block_input ();
5020 if (x->toolbar_is_packed)
5022 if (x->toolbar_in_hbox)
5023 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5024 top_widget);
5025 else
5026 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5027 top_widget);
5029 else
5030 gtk_widget_destroy (x->toolbar_widget);
5032 x->toolbar_widget = 0;
5033 x->toolbar_widget = 0;
5034 x->toolbar_is_packed = false;
5035 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
5036 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
5038 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5039 TB_INFO_KEY);
5040 if (tbinfo)
5042 xfree (tbinfo);
5043 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
5044 TB_INFO_KEY,
5045 NULL);
5048 frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
5049 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5051 unblock_input ();
5055 void
5056 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
5058 struct x_output *x = f->output_data.x;
5059 GtkWidget *top_widget = x->toolbar_widget;
5061 if (! x->toolbar_widget || ! top_widget)
5062 return;
5064 block_input ();
5065 g_object_ref (top_widget);
5066 if (x->toolbar_is_packed)
5068 if (x->toolbar_in_hbox)
5069 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5070 top_widget);
5071 else
5072 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5073 top_widget);
5076 xg_pack_tool_bar (f, pos);
5077 g_object_unref (top_widget);
5079 if (xg_update_tool_bar_sizes (f))
5081 frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
5082 adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
5086 unblock_input ();
5091 /***********************************************************************
5092 Initializing
5093 ***********************************************************************/
5094 void
5095 xg_initialize (void)
5097 GtkBindingSet *binding_set;
5098 GtkSettings *settings;
5100 #if HAVE_XFT
5101 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5102 we keep it permanently linked in. */
5103 XftInit (0);
5104 #endif
5106 gdpy_def = NULL;
5107 xg_ignore_gtk_scrollbar = 0;
5108 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5109 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5111 id_to_widget.max_size = id_to_widget.used = 0;
5112 id_to_widget.widgets = 0;
5114 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5115 (gdk_display_get_default ()));
5116 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5117 bindings. It doesn't seem to be any way to remove properties,
5118 so we set it to "" which in means "no key". */
5119 gtk_settings_set_string_property (settings,
5120 "gtk-menu-bar-accel",
5122 EMACS_CLASS);
5124 /* Make GTK text input widgets use Emacs style keybindings. This is
5125 Emacs after all. */
5126 gtk_settings_set_string_property (settings,
5127 "gtk-key-theme-name",
5128 "Emacs",
5129 EMACS_CLASS);
5131 /* Make dialogs close on C-g. Since file dialog inherits from
5132 dialog, this works for them also. */
5133 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
5134 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5135 "close", 0);
5137 /* Make menus close on C-g. */
5138 binding_set = gtk_binding_set_by_class (g_type_class_ref
5139 (GTK_TYPE_MENU_SHELL));
5140 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
5141 "cancel", 0);
5142 update_theme_scrollbar_width ();
5143 update_theme_scrollbar_height ();
5145 #ifdef HAVE_FREETYPE
5146 x_last_font_name = NULL;
5147 #endif
5150 #endif /* USE_GTK */