vc-hooks.el workaround for bug#11490
[emacs.git] / src / gtkutil.c
blob4367b534cb9ff5c4a2c301a1283688b6c8a95b77
1 /* Functions for creating and updating GTK widgets.
3 Copyright (C) 2003-2012 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #ifdef USE_GTK
23 #include <float.h>
24 #include <stdio.h>
26 #include <c-ctype.h>
28 #include "lisp.h"
29 #include "xterm.h"
30 #include "blockinput.h"
31 #include "syssignal.h"
32 #include "window.h"
33 #include "gtkutil.h"
34 #include "termhooks.h"
35 #include "keyboard.h"
36 #include "charset.h"
37 #include "coding.h"
38 #include <gdk/gdkkeysyms.h>
39 #include "xsettings.h"
41 #ifdef HAVE_XFT
42 #include <X11/Xft/Xft.h>
43 #endif
45 #ifdef HAVE_GTK3
46 #include <gtk/gtkx.h>
47 #include "emacsgtkfixed.h"
48 #endif
50 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
51 (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
53 #define FRAME_TOTAL_PIXEL_WIDTH(f) \
54 (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f))
56 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
57 #define gtk_widget_set_has_window(w, b) \
58 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
59 #endif
60 #ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
61 #define gtk_dialog_get_action_area(w) ((w)->action_area)
62 #define gtk_dialog_get_content_area(w) ((w)->vbox)
63 #endif
64 #ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
65 #define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
66 #endif
67 #ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
68 #define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
69 #define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
70 #define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
71 #define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
72 #endif
73 #if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 11
74 #define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
75 #else
76 #define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
77 #endif
79 #if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 2)
80 #define USE_NEW_GTK_FONT_CHOOSER 1
81 #else
82 #define USE_NEW_GTK_FONT_CHOOSER 0
83 #define gtk_font_chooser_dialog_new(x, y) \
84 gtk_font_selection_dialog_new (x)
85 #undef GTK_FONT_CHOOSER
86 #define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
87 #define gtk_font_chooser_set_font(x, y) \
88 gtk_font_selection_dialog_set_font_name (x, y)
89 #endif
91 #ifndef HAVE_GTK3
92 #ifdef USE_GTK_TOOLTIP
93 #define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
94 #endif
95 #define gdk_window_get_geometry(w, a, b, c, d) \
96 gdk_window_get_geometry (w, a, b, c, d, 0)
97 #define gdk_x11_window_lookup_for_display(d, w) \
98 gdk_xid_table_lookup_for_display (d, w)
99 #define gtk_box_new(ori, spacing) \
100 ((ori) == GTK_ORIENTATION_HORIZONTAL \
101 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
102 #define gtk_scrollbar_new(ori, spacing) \
103 ((ori) == GTK_ORIENTATION_HORIZONTAL \
104 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
105 #ifndef GDK_KEY_g
106 #define GDK_KEY_g GDK_g
107 #endif
108 #endif /* HAVE_GTK3 */
110 #define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
112 static void update_theme_scrollbar_width (void);
114 #define TB_INFO_KEY "xg_frame_tb_info"
115 struct xg_frame_tb_info
117 Lisp_Object last_tool_bar;
118 Lisp_Object style;
119 int n_last_items;
120 int hmargin, vmargin;
121 GtkTextDirection dir;
125 /***********************************************************************
126 Display handling functions
127 ***********************************************************************/
129 /* Keep track of the default display, or NULL if there is none. Emacs
130 may close all its displays. */
132 static GdkDisplay *gdpy_def;
134 /* When the GTK widget W is to be created on a display for F that
135 is not the default display, set the display for W.
136 W can be a GtkMenu or a GtkWindow widget. */
138 static void
139 xg_set_screen (GtkWidget *w, FRAME_PTR f)
141 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
143 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
144 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
146 if (GTK_IS_MENU (w))
147 gtk_menu_set_screen (GTK_MENU (w), gscreen);
148 else
149 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
154 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
155 *DPY is set to NULL if the display can't be opened.
157 Returns non-zero if display could be opened, zero if display could not
158 be opened, and less than zero if the GTK version doesn't support
159 multiple displays. */
161 void
162 xg_display_open (char *display_name, Display **dpy)
164 GdkDisplay *gdpy;
166 gdpy = gdk_display_open (display_name);
167 if (!gdpy_def && gdpy)
169 gdpy_def = gdpy;
170 gdk_display_manager_set_default_display (gdk_display_manager_get (),
171 gdpy);
174 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
178 /* Close display DPY. */
180 void
181 xg_display_close (Display *dpy)
183 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
185 /* If this is the default display, try to change it before closing.
186 If there is no other display to use, gdpy_def is set to NULL, and
187 the next call to xg_display_open resets the default display. */
188 if (gdk_display_get_default () == gdpy)
190 struct x_display_info *dpyinfo;
191 GdkDisplay *gdpy_new = NULL;
193 /* Find another display. */
194 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
195 if (dpyinfo->display != dpy)
197 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
198 gdk_display_manager_set_default_display (gdk_display_manager_get (),
199 gdpy_new);
200 break;
202 gdpy_def = gdpy_new;
205 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 10
206 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
207 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
208 can continue running, but there will be memory leaks. */
209 g_object_run_dispose (G_OBJECT (gdpy));
210 #else
211 /* This seems to be fixed in GTK 2.10. */
212 gdk_display_close (gdpy);
213 #endif
217 /***********************************************************************
218 Utility functions
219 ***********************************************************************/
220 /* The next two variables and functions are taken from lwlib. */
221 static widget_value *widget_value_free_list;
222 static int malloc_cpt;
224 /* Allocate a widget_value structure, either by taking one from the
225 widget_value_free_list or by malloc:ing a new one.
227 Return a pointer to the allocated structure. */
229 widget_value *
230 malloc_widget_value (void)
232 widget_value *wv;
233 if (widget_value_free_list)
235 wv = widget_value_free_list;
236 widget_value_free_list = wv->free_list;
237 wv->free_list = 0;
239 else
241 wv = xmalloc (sizeof *wv);
242 malloc_cpt++;
244 memset (wv, 0, sizeof (widget_value));
245 return wv;
248 /* This is analogous to free. It frees only what was allocated
249 by malloc_widget_value, and no substructures. */
251 void
252 free_widget_value (widget_value *wv)
254 if (wv->free_list)
255 emacs_abort ();
257 if (malloc_cpt > 25)
259 /* When the number of already allocated cells is too big,
260 We free it. */
261 xfree (wv);
262 malloc_cpt--;
264 else
266 wv->free_list = widget_value_free_list;
267 widget_value_free_list = wv;
272 /* Create and return the cursor to be used for popup menus and
273 scroll bars on display DPY. */
275 GdkCursor *
276 xg_create_default_cursor (Display *dpy)
278 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
279 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
282 static GdkPixbuf *
283 xg_get_pixbuf_from_pixmap (FRAME_PTR f, Pixmap pix)
285 int iunused;
286 GdkPixbuf *tmp_buf;
287 Window wunused;
288 unsigned int width, height, uunused;
289 XImage *xim;
291 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
292 &width, &height, &uunused, &uunused);
294 xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
295 ~0, XYPixmap);
296 if (!xim) return 0;
298 tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data,
299 GDK_COLORSPACE_RGB,
300 FALSE,
301 xim->bitmap_unit,
302 width,
303 height,
304 xim->bytes_per_line,
305 NULL,
306 NULL);
307 XDestroyImage (xim);
308 return tmp_buf;
311 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
313 static GdkPixbuf *
314 xg_get_pixbuf_from_pix_and_mask (FRAME_PTR f,
315 Pixmap pix,
316 Pixmap mask)
318 int width, height;
319 GdkPixbuf *icon_buf, *tmp_buf;
321 tmp_buf = xg_get_pixbuf_from_pixmap (f, pix);
322 icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
323 g_object_unref (G_OBJECT (tmp_buf));
325 width = gdk_pixbuf_get_width (icon_buf);
326 height = gdk_pixbuf_get_height (icon_buf);
328 if (mask)
330 GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask);
331 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
332 guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
333 int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
334 int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
335 int y;
337 for (y = 0; y < height; ++y)
339 guchar *iconptr, *maskptr;
340 int x;
342 iconptr = pixels + y * rowstride;
343 maskptr = mask_pixels + y * mask_rowstride;
345 for (x = 0; x < width; ++x)
347 /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
348 just R is sufficient. */
349 if (maskptr[0] == 0)
350 iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
352 iconptr += rowstride/width;
353 maskptr += mask_rowstride/width;
357 g_object_unref (G_OBJECT (mask_buf));
360 return icon_buf;
363 static Lisp_Object
364 file_for_image (Lisp_Object image)
366 Lisp_Object specified_file = Qnil;
367 Lisp_Object tail;
369 for (tail = XCDR (image);
370 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
371 tail = XCDR (XCDR (tail)))
372 if (EQ (XCAR (tail), QCfile))
373 specified_file = XCAR (XCDR (tail));
375 return specified_file;
378 /* For the image defined in IMG, make and return a GtkImage. For displays with
379 8 planes or less we must make a GdkPixbuf and apply the mask manually.
380 Otherwise the highlighting and dimming the tool bar code in GTK does
381 will look bad. For display with more than 8 planes we just use the
382 pixmap and mask directly. For monochrome displays, GTK doesn't seem
383 able to use external pixmaps, it looks bad whatever we do.
384 The image is defined on the display where frame F is.
385 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
386 If OLD_WIDGET is NULL, a new widget is constructed and returned.
387 If OLD_WIDGET is not NULL, that widget is modified. */
389 static GtkWidget *
390 xg_get_image_for_pixmap (FRAME_PTR f,
391 struct image *img,
392 GtkWidget *widget,
393 GtkImage *old_widget)
395 GdkPixbuf *icon_buf;
397 /* If we have a file, let GTK do all the image handling.
398 This seems to be the only way to make insensitive and activated icons
399 look good in all cases. */
400 Lisp_Object specified_file = file_for_image (img->spec);
401 Lisp_Object file;
403 /* We already loaded the image once before calling this
404 function, so this only fails if the image file has been removed.
405 In that case, use the pixmap already loaded. */
407 if (STRINGP (specified_file)
408 && STRINGP (file = x_find_image_file (specified_file)))
410 if (! old_widget)
411 old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
412 else
413 gtk_image_set_from_file (old_widget, SSDATA (file));
415 return GTK_WIDGET (old_widget);
418 /* No file, do the image handling ourselves. This will look very bad
419 on a monochrome display, and sometimes bad on all displays with
420 certain themes. */
422 /* This is a workaround to make icons look good on pseudo color
423 displays. Apparently GTK expects the images to have an alpha
424 channel. If they don't, insensitive and activated icons will
425 look bad. This workaround does not work on monochrome displays,
426 and is strictly not needed on true color/static color displays (i.e.
427 16 bits and higher). But we do it anyway so we get a pixbuf that is
428 not associated with the img->pixmap. The img->pixmap may be removed
429 by clearing the image cache and then the tool bar redraw fails, since
430 Gtk+ assumes the pixmap is always there. */
431 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
433 if (icon_buf)
435 if (! old_widget)
436 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
437 else
438 gtk_image_set_from_pixbuf (old_widget, icon_buf);
440 g_object_unref (G_OBJECT (icon_buf));
443 return GTK_WIDGET (old_widget);
447 /* Set CURSOR on W and all widgets W contain. We must do like this
448 for scroll bars and menu because they create widgets internally,
449 and it is those widgets that are visible. */
451 static void
452 xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
454 GdkWindow *window = gtk_widget_get_window (w);
455 GList *children = gdk_window_peek_children (window);
457 gdk_window_set_cursor (window, cursor);
459 /* The scroll bar widget has more than one GDK window (had to look at
460 the source to figure this out), and there is no way to set cursor
461 on widgets in GTK. So we must set the cursor for all GDK windows.
462 Ditto for menus. */
464 for ( ; children; children = g_list_next (children))
465 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
468 /* Insert NODE into linked LIST. */
470 static void
471 xg_list_insert (xg_list_node *list, xg_list_node *node)
473 xg_list_node *list_start = list->next;
475 if (list_start) list_start->prev = node;
476 node->next = list_start;
477 node->prev = 0;
478 list->next = node;
481 /* Remove NODE from linked LIST. */
483 static void
484 xg_list_remove (xg_list_node *list, xg_list_node *node)
486 xg_list_node *list_start = list->next;
487 if (node == list_start)
489 list->next = node->next;
490 if (list->next) list->next->prev = 0;
492 else
494 node->prev->next = node->next;
495 if (node->next) node->next->prev = node->prev;
499 /* Allocate and return a utf8 version of STR. If STR is already
500 utf8 or NULL, just return a copy of STR.
501 A new string is allocated and the caller must free the result
502 with g_free. */
504 static char *
505 get_utf8_string (const char *str)
507 char *utf8_str;
509 if (!str) return NULL;
511 /* If not UTF-8, try current locale. */
512 if (!g_utf8_validate (str, -1, NULL))
513 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
514 else
515 return g_strdup (str);
517 if (!utf8_str)
519 /* Probably some control characters in str. Escape them. */
520 ptrdiff_t len;
521 ptrdiff_t nr_bad = 0;
522 gsize bytes_read;
523 gsize bytes_written;
524 unsigned char *p = (unsigned char *)str;
525 char *cp, *up;
526 GError *err = NULL;
528 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
529 &bytes_written, &err))
530 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
532 ++nr_bad;
533 p += bytes_written+1;
534 g_error_free (err);
535 err = NULL;
538 if (err)
540 g_error_free (err);
541 err = NULL;
543 if (cp) g_free (cp);
545 len = strlen (str);
546 if ((min (PTRDIFF_MAX, SIZE_MAX) - len - 1) / 4 < nr_bad)
547 memory_full (SIZE_MAX);
548 up = utf8_str = xmalloc (len + nr_bad * 4 + 1);
549 p = (unsigned char *)str;
551 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
552 &bytes_written, &err))
553 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
555 memcpy (up, p, bytes_written);
556 sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
557 up += bytes_written+4;
558 p += bytes_written+1;
559 g_error_free (err);
560 err = NULL;
563 if (cp)
565 strcat (utf8_str, cp);
566 g_free (cp);
568 if (err)
570 g_error_free (err);
571 err = NULL;
574 return utf8_str;
577 /* Check for special colors used in face spec for region face.
578 The colors are fetched from the Gtk+ theme.
579 Return true if color was found, false if not. */
581 bool
582 xg_check_special_colors (struct frame *f,
583 const char *color_name,
584 XColor *color)
586 bool success_p = 0;
587 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
588 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
590 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
591 return success_p;
593 block_input ();
595 #ifdef HAVE_GTK3
596 GtkStyleContext *gsty
597 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
598 GdkRGBA col;
599 char buf[sizeof "rgbi://" + 3 * (DBL_MAX_10_EXP + sizeof "-1.000000" - 1)];
600 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
601 if (get_fg)
602 gtk_style_context_get_color (gsty, state, &col);
603 else
604 gtk_style_context_get_background_color (gsty, state, &col);
606 sprintf (buf, "rgbi:%lf/%lf/%lf", col.red, col.green, col.blue);
607 success_p = (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
608 buf, color)
609 != 0);
610 #else
611 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
612 GdkColor *grgb = get_bg
613 ? &gsty->bg[GTK_STATE_SELECTED]
614 : &gsty->fg[GTK_STATE_SELECTED];
616 color->red = grgb->red;
617 color->green = grgb->green;
618 color->blue = grgb->blue;
619 color->pixel = grgb->pixel;
620 success_p = 1;
621 #endif
624 unblock_input ();
625 return success_p;
630 /***********************************************************************
631 Tooltips
632 ***********************************************************************/
633 /* Gtk+ calls this callback when the parent of our tooltip dummy changes.
634 We use that to pop down the tooltip. This happens if Gtk+ for some
635 reason wants to change or hide the tooltip. */
637 #ifdef USE_GTK_TOOLTIP
639 static void
640 hierarchy_ch_cb (GtkWidget *widget,
641 GtkWidget *previous_toplevel,
642 gpointer user_data)
644 FRAME_PTR f = (FRAME_PTR) user_data;
645 struct x_output *x = f->output_data.x;
646 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
648 if (! top || ! GTK_IS_WINDOW (top))
649 gtk_widget_hide (previous_toplevel);
652 /* Callback called when Gtk+ thinks a tooltip should be displayed.
653 We use it to get the tooltip window and the tooltip widget so
654 we can manipulate the ourselves.
656 Return FALSE ensures that the tooltip is not shown. */
658 static gboolean
659 qttip_cb (GtkWidget *widget,
660 gint xpos,
661 gint ypos,
662 gboolean keyboard_mode,
663 GtkTooltip *tooltip,
664 gpointer user_data)
666 FRAME_PTR f = (FRAME_PTR) user_data;
667 struct x_output *x = f->output_data.x;
668 if (x->ttip_widget == NULL)
670 GtkWidget *p;
671 GList *list, *iter;
673 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
674 x->ttip_widget = tooltip;
675 g_object_ref (G_OBJECT (tooltip));
676 x->ttip_lbl = gtk_label_new ("");
677 g_object_ref (G_OBJECT (x->ttip_lbl));
678 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
679 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
681 /* Change stupid Gtk+ default line wrapping. */
682 p = gtk_widget_get_parent (x->ttip_lbl);
683 list = gtk_container_get_children (GTK_CONTAINER (p));
684 for (iter = list; iter; iter = g_list_next (iter))
686 GtkWidget *w = GTK_WIDGET (iter->data);
687 if (GTK_IS_LABEL (w))
688 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
690 g_list_free (list);
692 /* ATK needs an empty title for some reason. */
693 gtk_window_set_title (x->ttip_window, "");
694 /* Realize so we can safely get screen later on. */
695 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
696 gtk_widget_realize (x->ttip_lbl);
698 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
699 G_CALLBACK (hierarchy_ch_cb), f);
701 return FALSE;
704 #endif /* USE_GTK_TOOLTIP */
706 /* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
707 Return true if a system tooltip is available. */
709 bool
710 xg_prepare_tooltip (FRAME_PTR f,
711 Lisp_Object string,
712 int *width,
713 int *height)
715 #ifndef USE_GTK_TOOLTIP
716 return 0;
717 #else
718 struct x_output *x = f->output_data.x;
719 GtkWidget *widget;
720 GdkWindow *gwin;
721 GdkScreen *screen;
722 GtkSettings *settings;
723 gboolean tt_enabled = TRUE;
724 GtkRequisition req;
725 Lisp_Object encoded_string;
727 if (!x->ttip_lbl) return 0;
729 block_input ();
730 encoded_string = ENCODE_UTF_8 (string);
731 widget = GTK_WIDGET (x->ttip_lbl);
732 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
733 screen = gdk_window_get_screen (gwin);
734 settings = gtk_settings_get_for_screen (screen);
735 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
736 if (tt_enabled)
738 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
739 /* Record that we disabled it so it can be enabled again. */
740 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
741 (gpointer)f);
744 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
745 g_object_set_data (G_OBJECT
746 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
747 "gdk-display-current-tooltip", NULL);
749 /* Put our dummy widget in so we can get callbacks for unrealize and
750 hierarchy-changed. */
751 gtk_tooltip_set_custom (x->ttip_widget, widget);
752 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
753 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
754 if (width) *width = req.width;
755 if (height) *height = req.height;
757 unblock_input ();
759 return 1;
760 #endif /* USE_GTK_TOOLTIP */
763 /* Show the tooltip at ROOT_X and ROOT_Y.
764 xg_prepare_tooltip must have been called before this function. */
766 void
767 xg_show_tooltip (FRAME_PTR f, int root_x, int root_y)
769 #ifdef USE_GTK_TOOLTIP
770 struct x_output *x = f->output_data.x;
771 if (x->ttip_window)
773 block_input ();
774 gtk_window_move (x->ttip_window, root_x, root_y);
775 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
776 unblock_input ();
778 #endif
781 /* Hide tooltip if shown. Do nothing if not shown.
782 Return true if tip was hidden, false if not (i.e. not using
783 system tooltips). */
785 bool
786 xg_hide_tooltip (FRAME_PTR f)
788 bool ret = 0;
789 #ifdef USE_GTK_TOOLTIP
790 if (f->output_data.x->ttip_window)
792 GtkWindow *win = f->output_data.x->ttip_window;
793 block_input ();
794 gtk_widget_hide (GTK_WIDGET (win));
796 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
798 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
799 GdkScreen *screen = gdk_window_get_screen (gwin);
800 GtkSettings *settings = gtk_settings_get_for_screen (screen);
801 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
803 unblock_input ();
805 ret = 1;
807 #endif
808 return ret;
812 /***********************************************************************
813 General functions for creating widgets, resizing, events, e.t.c.
814 ***********************************************************************/
816 /* Make a geometry string and pass that to GTK. It seems this is the
817 only way to get geometry position right if the user explicitly
818 asked for a position when starting Emacs.
819 F is the frame we shall set geometry for. */
821 static void
822 xg_set_geometry (FRAME_PTR f)
824 if (f->size_hint_flags & (USPosition | PPosition))
826 int left = f->left_pos;
827 int xneg = f->size_hint_flags & XNegative;
828 int top = f->top_pos;
829 int yneg = f->size_hint_flags & YNegative;
830 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
832 if (xneg)
833 left = -left;
834 if (yneg)
835 top = -top;
837 sprintf (geom_str, "=%dx%d%c%d%c%d",
838 FRAME_PIXEL_WIDTH (f),
839 FRAME_PIXEL_HEIGHT (f),
840 (xneg ? '-' : '+'), left,
841 (yneg ? '-' : '+'), top);
843 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
844 geom_str))
845 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
849 /* Clear under internal border if any. As we use a mix of Gtk+ and X calls
850 and use a GtkFixed widget, this doesn't happen automatically. */
852 static void
853 xg_clear_under_internal_border (FRAME_PTR f)
855 if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
857 GtkWidget *wfixed = f->output_data.x->edit_widget;
858 gtk_widget_queue_draw (wfixed);
859 gdk_window_process_all_updates ();
860 x_clear_area (FRAME_X_DISPLAY (f),
861 FRAME_X_WINDOW (f),
862 0, 0,
863 FRAME_PIXEL_WIDTH (f),
864 FRAME_INTERNAL_BORDER_WIDTH (f), 0);
865 x_clear_area (FRAME_X_DISPLAY (f),
866 FRAME_X_WINDOW (f),
867 0, 0,
868 FRAME_INTERNAL_BORDER_WIDTH (f),
869 FRAME_PIXEL_HEIGHT (f), 0);
870 x_clear_area (FRAME_X_DISPLAY (f),
871 FRAME_X_WINDOW (f),
872 0, FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
873 FRAME_PIXEL_WIDTH (f),
874 FRAME_INTERNAL_BORDER_WIDTH (f), 0);
875 x_clear_area (FRAME_X_DISPLAY (f),
876 FRAME_X_WINDOW (f),
877 FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
879 FRAME_INTERNAL_BORDER_WIDTH (f),
880 FRAME_PIXEL_HEIGHT (f), 0);
884 /* Function to handle resize of our frame. As we have a Gtk+ tool bar
885 and a Gtk+ menu bar, we get resize events for the edit part of the
886 frame only. We let Gtk+ deal with the Gtk+ parts.
887 F is the frame to resize.
888 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
890 void
891 xg_frame_resized (FRAME_PTR f, int pixelwidth, int pixelheight)
893 int rows, columns;
895 if (pixelwidth == -1 && pixelheight == -1)
897 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
898 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
899 0, 0,
900 &pixelwidth, &pixelheight);
901 else return;
905 rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, pixelheight);
906 columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
908 if (columns != FRAME_COLS (f)
909 || rows != FRAME_LINES (f)
910 || pixelwidth != FRAME_PIXEL_WIDTH (f)
911 || pixelheight != FRAME_PIXEL_HEIGHT (f))
913 FRAME_PIXEL_WIDTH (f) = pixelwidth;
914 FRAME_PIXEL_HEIGHT (f) = pixelheight;
916 xg_clear_under_internal_border (f);
917 change_frame_size (f, rows, columns, 0, 1, 0);
918 SET_FRAME_GARBAGED (f);
919 cancel_mouse_face (f);
923 /* Resize the outer window of frame F after changing the height.
924 COLUMNS/ROWS is the size the edit area shall have after the resize. */
926 void
927 xg_frame_set_char_size (FRAME_PTR f, int cols, int rows)
929 int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
930 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
931 int pixelwidth;
933 if (FRAME_PIXEL_HEIGHT (f) == 0)
934 return;
936 /* Take into account the size of the scroll bar. Always use the
937 number of columns occupied by the scroll bar here otherwise we
938 might end up with a frame width that is not a multiple of the
939 frame's character width which is bad for vertically split
940 windows. */
941 f->scroll_bar_actual_width
942 = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
944 compute_fringe_widths (f, 0);
946 /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it
947 after calculating that value. */
948 pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols)
949 + FRAME_TOOLBAR_WIDTH (f);
952 /* Do this before resize, as we don't know yet if we will be resized. */
953 xg_clear_under_internal_border (f);
955 /* Must resize our top level widget. Font size may have changed,
956 but not rows/cols. */
957 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
958 pixelwidth, pixelheight);
959 x_wm_set_size_hint (f, 0, 0);
961 SET_FRAME_GARBAGED (f);
962 cancel_mouse_face (f);
964 /* We can not call change_frame_size for a mapped frame,
965 we can not set pixel width/height either. The window manager may
966 override our resize request, XMonad does this all the time.
967 The best we can do is try to sync, so lisp code sees the updated
968 size as fast as possible.
969 For unmapped windows, we can set rows/cols. When
970 the frame is mapped again we will (hopefully) get the correct size. */
971 if (f->async_visible)
973 /* Must call this to flush out events */
974 (void)gtk_events_pending ();
975 gdk_flush ();
976 x_wait_for_event (f, ConfigureNotify);
978 else
980 change_frame_size (f, rows, cols, 0, 1, 0);
981 FRAME_PIXEL_WIDTH (f) = pixelwidth;
982 FRAME_PIXEL_HEIGHT (f) = pixelheight;
986 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
987 The policy is to keep the number of editable lines. */
989 static void
990 xg_height_or_width_changed (FRAME_PTR f)
992 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
993 FRAME_TOTAL_PIXEL_WIDTH (f),
994 FRAME_TOTAL_PIXEL_HEIGHT (f));
995 f->output_data.x->hint_flags = 0;
996 x_wm_set_size_hint (f, 0, 0);
999 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
1000 Must be done like this, because GtkWidget:s can have "hidden"
1001 X Window that aren't accessible.
1003 Return 0 if no widget match WDESC. */
1005 GtkWidget *
1006 xg_win_to_widget (Display *dpy, Window wdesc)
1008 gpointer gdkwin;
1009 GtkWidget *gwdesc = 0;
1011 block_input ();
1013 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1014 wdesc);
1015 if (gdkwin)
1017 GdkEvent event;
1018 event.any.window = gdkwin;
1019 event.any.type = GDK_NOTHING;
1020 gwdesc = gtk_get_event_widget (&event);
1023 unblock_input ();
1024 return gwdesc;
1027 /* Set the background of widget W to PIXEL. */
1029 static void
1030 xg_set_widget_bg (FRAME_PTR f, GtkWidget *w, long unsigned int pixel)
1032 #ifdef HAVE_GTK3
1033 GdkRGBA bg;
1034 XColor xbg;
1035 xbg.pixel = pixel;
1036 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1038 bg.red = (double)xbg.red/65535.0;
1039 bg.green = (double)xbg.green/65535.0;
1040 bg.blue = (double)xbg.blue/65535.0;
1041 bg.alpha = 1.0;
1042 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1044 #else
1045 GdkColor bg;
1046 GdkColormap *map = gtk_widget_get_colormap (w);
1047 gdk_colormap_query_color (map, pixel, &bg);
1048 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1049 #endif
1052 /* Callback called when the gtk theme changes.
1053 We notify lisp code so it can fix faces used for region for example. */
1055 static void
1056 style_changed_cb (GObject *go,
1057 GParamSpec *spec,
1058 gpointer user_data)
1060 struct input_event event;
1061 GdkDisplay *gdpy = (GdkDisplay *) user_data;
1062 const char *display_name = gdk_display_get_name (gdpy);
1063 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
1065 EVENT_INIT (event);
1066 event.kind = CONFIG_CHANGED_EVENT;
1067 event.frame_or_window = build_string (display_name);
1068 /* Theme doesn't change often, so intern is called seldom. */
1069 event.arg = intern ("theme-name");
1070 kbd_buffer_store_event (&event);
1072 update_theme_scrollbar_width ();
1074 /* If scroll bar width changed, we need set the new size on all frames
1075 on this display. */
1076 if (dpy)
1078 Lisp_Object rest, frame;
1079 FOR_EACH_FRAME (rest, frame)
1081 FRAME_PTR f = XFRAME (frame);
1082 if (FRAME_X_DISPLAY (f) == dpy)
1084 x_set_scroll_bar_default_width (f);
1085 xg_frame_set_char_size (f, FRAME_COLS (f), FRAME_LINES (f));
1091 /* Called when a delete-event occurs on WIDGET. */
1093 static gboolean
1094 delete_cb (GtkWidget *widget,
1095 GdkEvent *event,
1096 gpointer user_data)
1098 #ifdef HAVE_GTK3
1099 /* The event doesn't arrive in the normal event loop. Send event
1100 here. */
1101 FRAME_PTR f = (FRAME_PTR) user_data;
1102 struct input_event ie;
1104 EVENT_INIT (ie);
1105 ie.kind = DELETE_WINDOW_EVENT;
1106 XSETFRAME (ie.frame_or_window, f);
1107 kbd_buffer_store_event (&ie);
1108 #endif
1110 return TRUE;
1113 /* Create and set up the GTK widgets for frame F.
1114 Return true if creation succeeded. */
1116 bool
1117 xg_create_frame_widgets (FRAME_PTR f)
1119 GtkWidget *wtop;
1120 GtkWidget *wvbox, *whbox;
1121 GtkWidget *wfixed;
1122 #ifndef HAVE_GTK3
1123 GtkRcStyle *style;
1124 #endif
1125 char *title = 0;
1127 block_input ();
1129 if (FRAME_X_EMBEDDED_P (f))
1131 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1132 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1134 else
1135 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1137 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1138 has backported it to Gtk+ 2.0 and they add the resize grip for
1139 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1140 forever, so disable the grip. */
1141 #if GTK_MAJOR_VERSION < 3 && defined (HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
1142 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1143 #endif
1145 xg_set_screen (wtop, f);
1147 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1148 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1149 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1150 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
1152 #ifdef HAVE_GTK3
1153 wfixed = emacs_fixed_new (f);
1154 #else
1155 wfixed = gtk_fixed_new ();
1156 #endif
1158 if (! wtop || ! wvbox || ! whbox || ! wfixed)
1160 if (wtop) gtk_widget_destroy (wtop);
1161 if (wvbox) gtk_widget_destroy (wvbox);
1162 if (whbox) gtk_widget_destroy (whbox);
1163 if (wfixed) gtk_widget_destroy (wfixed);
1165 unblock_input ();
1166 return 0;
1169 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1170 gtk_widget_set_name (wtop, EMACS_CLASS);
1171 gtk_widget_set_name (wvbox, "pane");
1172 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
1174 /* If this frame has a title or name, set it in the title bar. */
1175 if (! NILP (f->title))
1176 title = SSDATA (ENCODE_UTF_8 (f->title));
1177 else if (! NILP (f->name))
1178 title = SSDATA (ENCODE_UTF_8 (f->name));
1180 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
1182 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1183 FRAME_GTK_WIDGET (f) = wfixed;
1184 f->output_data.x->vbox_widget = wvbox;
1185 f->output_data.x->hbox_widget = whbox;
1187 gtk_widget_set_has_window (wfixed, TRUE);
1189 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
1190 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1191 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
1193 if (FRAME_EXTERNAL_TOOL_BAR (f))
1194 update_frame_tool_bar (f);
1196 /* We don't want this widget double buffered, because we draw on it
1197 with regular X drawing primitives, so from a GTK/GDK point of
1198 view, the widget is totally blank. When an expose comes, this
1199 will make the widget blank, and then Emacs redraws it. This flickers
1200 a lot, so we turn off double buffering. */
1201 gtk_widget_set_double_buffered (wfixed, FALSE);
1203 gtk_window_set_wmclass (GTK_WINDOW (wtop),
1204 SSDATA (Vx_resource_name),
1205 SSDATA (Vx_resource_class));
1207 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1208 GTK is to destroy the widget. We want Emacs to do that instead. */
1209 g_signal_connect (G_OBJECT (wtop), "delete-event",
1210 G_CALLBACK (delete_cb), f);
1212 /* Convert our geometry parameters into a geometry string
1213 and specify it.
1214 GTK will itself handle calculating the real position this way. */
1215 xg_set_geometry (f);
1216 f->win_gravity
1217 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1219 gtk_widget_add_events (wfixed,
1220 GDK_POINTER_MOTION_MASK
1221 | GDK_EXPOSURE_MASK
1222 | GDK_BUTTON_PRESS_MASK
1223 | GDK_BUTTON_RELEASE_MASK
1224 | GDK_KEY_PRESS_MASK
1225 | GDK_ENTER_NOTIFY_MASK
1226 | GDK_LEAVE_NOTIFY_MASK
1227 | GDK_FOCUS_CHANGE_MASK
1228 | GDK_STRUCTURE_MASK
1229 | GDK_VISIBILITY_NOTIFY_MASK);
1231 /* Must realize the windows so the X window gets created. It is used
1232 by callers of this function. */
1233 gtk_widget_realize (wfixed);
1234 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1236 /* Since GTK clears its window by filling with the background color,
1237 we must keep X and GTK background in sync. */
1238 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
1240 #ifndef HAVE_GTK3
1241 /* Also, do not let any background pixmap to be set, this looks very
1242 bad as Emacs overwrites the background pixmap with its own idea
1243 of background color. */
1244 style = gtk_widget_get_modifier_style (wfixed);
1246 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1247 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1248 gtk_widget_modify_style (wfixed, style);
1249 #else
1250 gtk_widget_set_can_focus (wfixed, TRUE);
1251 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
1252 #endif
1254 #ifdef USE_GTK_TOOLTIP
1255 /* Steal a tool tip window we can move ourselves. */
1256 f->output_data.x->ttip_widget = 0;
1257 f->output_data.x->ttip_lbl = 0;
1258 f->output_data.x->ttip_window = 0;
1259 gtk_widget_set_tooltip_text (wtop, "Dummy text");
1260 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
1261 #endif
1264 GdkScreen *screen = gtk_widget_get_screen (wtop);
1265 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1266 /* Only connect this signal once per screen. */
1267 if (! g_signal_handler_find (G_OBJECT (gs),
1268 G_SIGNAL_MATCH_FUNC,
1269 0, 0, 0,
1270 G_CALLBACK (style_changed_cb),
1273 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1274 G_CALLBACK (style_changed_cb),
1275 gdk_screen_get_display (screen));
1279 unblock_input ();
1281 return 1;
1284 void
1285 xg_free_frame_widgets (FRAME_PTR f)
1287 if (FRAME_GTK_OUTER_WIDGET (f))
1289 #ifdef USE_GTK_TOOLTIP
1290 struct x_output *x = f->output_data.x;
1291 #endif
1292 struct xg_frame_tb_info *tbinfo
1293 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1294 TB_INFO_KEY);
1295 if (tbinfo)
1296 xfree (tbinfo);
1298 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1299 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1300 FRAME_GTK_OUTER_WIDGET (f) = 0;
1301 #ifdef USE_GTK_TOOLTIP
1302 if (x->ttip_lbl)
1303 gtk_widget_destroy (x->ttip_lbl);
1304 if (x->ttip_widget)
1305 g_object_unref (G_OBJECT (x->ttip_widget));
1306 #endif
1310 /* Set the normal size hints for the window manager, for frame F.
1311 FLAGS is the flags word to use--or 0 meaning preserve the flags
1312 that the window now has.
1313 If USER_POSITION, set the User Position
1314 flag (this is useful when FLAGS is 0). */
1316 void
1317 x_wm_set_size_hint (FRAME_PTR f, long int flags, bool user_position)
1319 /* Must use GTK routines here, otherwise GTK resets the size hints
1320 to its own defaults. */
1321 GdkGeometry size_hints;
1322 gint hint_flags = 0;
1323 int base_width, base_height;
1324 int min_rows = 0, min_cols = 0;
1325 int win_gravity = f->win_gravity;
1327 /* Don't set size hints during initialization; that apparently leads
1328 to a race condition. See the thread at
1329 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1330 if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
1331 return;
1333 if (flags)
1335 memset (&size_hints, 0, sizeof (size_hints));
1336 f->output_data.x->size_hints = size_hints;
1337 f->output_data.x->hint_flags = hint_flags;
1339 else
1340 flags = f->size_hint_flags;
1342 size_hints = f->output_data.x->size_hints;
1343 hint_flags = f->output_data.x->hint_flags;
1345 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1346 size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
1347 size_hints.height_inc = FRAME_LINE_HEIGHT (f);
1349 hint_flags |= GDK_HINT_BASE_SIZE;
1350 /* Use one row/col here so base_height/width does not become zero.
1351 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it. */
1352 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
1353 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
1354 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1356 check_frame_size (f, &min_rows, &min_cols);
1357 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
1358 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
1360 size_hints.base_width = base_width;
1361 size_hints.base_height = base_height;
1362 size_hints.min_width = base_width + min_cols * size_hints.width_inc;
1363 size_hints.min_height = base_height + min_rows * size_hints.height_inc;
1365 /* These currently have a one to one mapping with the X values, but I
1366 don't think we should rely on that. */
1367 hint_flags |= GDK_HINT_WIN_GRAVITY;
1368 size_hints.win_gravity = 0;
1369 if (win_gravity == NorthWestGravity)
1370 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1371 else if (win_gravity == NorthGravity)
1372 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1373 else if (win_gravity == NorthEastGravity)
1374 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1375 else if (win_gravity == WestGravity)
1376 size_hints.win_gravity = GDK_GRAVITY_WEST;
1377 else if (win_gravity == CenterGravity)
1378 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1379 else if (win_gravity == EastGravity)
1380 size_hints.win_gravity = GDK_GRAVITY_EAST;
1381 else if (win_gravity == SouthWestGravity)
1382 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1383 else if (win_gravity == SouthGravity)
1384 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1385 else if (win_gravity == SouthEastGravity)
1386 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1387 else if (win_gravity == StaticGravity)
1388 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1390 if (user_position)
1392 hint_flags &= ~GDK_HINT_POS;
1393 hint_flags |= GDK_HINT_USER_POS;
1396 if (hint_flags != f->output_data.x->hint_flags
1397 || memcmp (&size_hints,
1398 &f->output_data.x->size_hints,
1399 sizeof (size_hints)) != 0)
1401 block_input ();
1402 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
1403 NULL, &size_hints, hint_flags);
1404 f->output_data.x->size_hints = size_hints;
1405 f->output_data.x->hint_flags = hint_flags;
1406 unblock_input ();
1410 /* Change background color of a frame.
1411 Since GTK uses the background color to clear the window, we must
1412 keep the GTK and X colors in sync.
1413 F is the frame to change,
1414 BG is the pixel value to change to. */
1416 void
1417 xg_set_background_color (FRAME_PTR f, long unsigned int bg)
1419 if (FRAME_GTK_WIDGET (f))
1421 block_input ();
1422 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
1423 unblock_input ();
1428 /* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1429 functions so GTK does not overwrite the icon. */
1431 void
1432 xg_set_frame_icon (FRAME_PTR f, Pixmap icon_pixmap, Pixmap icon_mask)
1434 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1435 icon_pixmap,
1436 icon_mask);
1437 if (gp)
1438 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1443 /***********************************************************************
1444 Dialog functions
1445 ***********************************************************************/
1446 /* Return the dialog title to use for a dialog of type KEY.
1447 This is the encoding used by lwlib. We use the same for GTK. */
1449 static const char *
1450 get_dialog_title (char key)
1452 const char *title = "";
1454 switch (key) {
1455 case 'E': case 'e':
1456 title = "Error";
1457 break;
1459 case 'I': case 'i':
1460 title = "Information";
1461 break;
1463 case 'L': case 'l':
1464 title = "Prompt";
1465 break;
1467 case 'P': case 'p':
1468 title = "Prompt";
1469 break;
1471 case 'Q': case 'q':
1472 title = "Question";
1473 break;
1476 return title;
1479 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1480 the dialog, but return TRUE so the event does not propagate further
1481 in GTK. This prevents GTK from destroying the dialog widget automatically
1482 and we can always destroy the widget manually, regardless of how
1483 it was popped down (button press or WM_DELETE_WINDOW).
1484 W is the dialog widget.
1485 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1486 user_data is NULL (not used).
1488 Returns TRUE to end propagation of event. */
1490 static gboolean
1491 dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
1493 gtk_widget_unmap (w);
1494 return TRUE;
1497 /* Create a popup dialog window. See also xg_create_widget below.
1498 WV is a widget_value describing the dialog.
1499 SELECT_CB is the callback to use when a button has been pressed.
1500 DEACTIVATE_CB is the callback to use when the dialog pops down.
1502 Returns the GTK dialog widget. */
1504 static GtkWidget *
1505 create_dialog (widget_value *wv,
1506 GCallback select_cb,
1507 GCallback deactivate_cb)
1509 const char *title = get_dialog_title (wv->name[0]);
1510 int total_buttons = wv->name[1] - '0';
1511 int right_buttons = wv->name[4] - '0';
1512 int left_buttons;
1513 int button_nr = 0;
1514 int button_spacing = 10;
1515 GtkWidget *wdialog = gtk_dialog_new ();
1516 GtkDialog *wd = GTK_DIALOG (wdialog);
1517 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
1518 widget_value *item;
1519 GtkWidget *whbox_down;
1521 /* If the number of buttons is greater than 4, make two rows of buttons
1522 instead. This looks better. */
1523 bool make_two_rows = total_buttons > 4;
1525 if (right_buttons == 0) right_buttons = total_buttons/2;
1526 left_buttons = total_buttons - right_buttons;
1528 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1529 gtk_widget_set_name (wdialog, "emacs-dialog");
1532 if (make_two_rows)
1534 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1535 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1536 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1537 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1538 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1539 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
1541 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1542 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1543 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1545 cur_box = GTK_BOX (whbox_up);
1548 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1549 G_CALLBACK (dialog_delete_callback), 0);
1551 if (deactivate_cb)
1553 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1554 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1557 for (item = wv->contents; item; item = item->next)
1559 char *utf8_label = get_utf8_string (item->value);
1560 GtkWidget *w;
1561 GtkRequisition req;
1563 if (item->name && strcmp (item->name, "message") == 0)
1565 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
1566 /* This is the text part of the dialog. */
1567 w = gtk_label_new (utf8_label);
1568 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1569 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
1570 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1572 /* Try to make dialog look better. Must realize first so
1573 the widget can calculate the size it needs. */
1574 gtk_widget_realize (w);
1575 gtk_widget_get_preferred_size (w, NULL, &req);
1576 gtk_box_set_spacing (wvbox, req.height);
1577 if (item->value && strlen (item->value) > 0)
1578 button_spacing = 2*req.width/strlen (item->value);
1580 else
1582 /* This is one button to add to the dialog. */
1583 w = gtk_button_new_with_label (utf8_label);
1584 if (! item->enabled)
1585 gtk_widget_set_sensitive (w, FALSE);
1586 if (select_cb)
1587 g_signal_connect (G_OBJECT (w), "clicked",
1588 select_cb, item->call_data);
1590 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1591 if (++button_nr == left_buttons)
1593 if (make_two_rows)
1594 cur_box = GTK_BOX (whbox_down);
1595 else
1596 gtk_box_pack_start (cur_box,
1597 gtk_label_new (""),
1598 TRUE, TRUE,
1599 button_spacing);
1603 if (utf8_label)
1604 g_free (utf8_label);
1607 return wdialog;
1610 struct xg_dialog_data
1612 GMainLoop *loop;
1613 int response;
1614 GtkWidget *w;
1615 guint timerid;
1618 /* Function that is called when the file or font dialogs pop down.
1619 W is the dialog widget, RESPONSE is the response code.
1620 USER_DATA is what we passed in to g_signal_connect. */
1622 static void
1623 xg_dialog_response_cb (GtkDialog *w,
1624 gint response,
1625 gpointer user_data)
1627 struct xg_dialog_data *dd = (struct xg_dialog_data *)user_data;
1628 dd->response = response;
1629 g_main_loop_quit (dd->loop);
1633 /* Destroy the dialog. This makes it pop down. */
1635 static Lisp_Object
1636 pop_down_dialog (Lisp_Object arg)
1638 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1639 struct xg_dialog_data *dd = (struct xg_dialog_data *) p->pointer;
1641 block_input ();
1642 if (dd->w) gtk_widget_destroy (dd->w);
1643 if (dd->timerid != 0) g_source_remove (dd->timerid);
1645 g_main_loop_quit (dd->loop);
1646 g_main_loop_unref (dd->loop);
1648 unblock_input ();
1650 return Qnil;
1653 /* If there are any emacs timers pending, add a timeout to main loop in DATA.
1654 We pass in DATA as gpointer* so we can use this as a callback. */
1656 static gboolean
1657 xg_maybe_add_timer (gpointer data)
1659 struct xg_dialog_data *dd = (struct xg_dialog_data *) data;
1660 EMACS_TIME next_time = timer_check ();
1662 dd->timerid = 0;
1664 if (EMACS_TIME_VALID_P (next_time))
1666 time_t s = EMACS_SECS (next_time);
1667 int per_ms = EMACS_TIME_RESOLUTION / 1000;
1668 int ms = (EMACS_NSECS (next_time) + per_ms - 1) / per_ms;
1669 if (s <= ((guint) -1 - ms) / 1000)
1670 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
1672 return FALSE;
1676 /* Pops up a modal dialog W and waits for response.
1677 We don't use gtk_dialog_run because we want to process emacs timers.
1678 The dialog W is not destroyed when this function returns. */
1680 static int
1681 xg_dialog_run (FRAME_PTR f, GtkWidget *w)
1683 ptrdiff_t count = SPECPDL_INDEX ();
1684 struct xg_dialog_data dd;
1686 xg_set_screen (w, f);
1687 gtk_window_set_transient_for (GTK_WINDOW (w),
1688 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1689 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1690 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1692 dd.loop = g_main_loop_new (NULL, FALSE);
1693 dd.response = GTK_RESPONSE_CANCEL;
1694 dd.w = w;
1695 dd.timerid = 0;
1697 g_signal_connect (G_OBJECT (w),
1698 "response",
1699 G_CALLBACK (xg_dialog_response_cb),
1700 &dd);
1701 /* Don't destroy the widget if closed by the window manager close button. */
1702 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1703 gtk_widget_show (w);
1705 record_unwind_protect (pop_down_dialog, make_save_value (&dd, 0));
1707 (void) xg_maybe_add_timer (&dd);
1708 g_main_loop_run (dd.loop);
1710 dd.w = 0;
1711 unbind_to (count, Qnil);
1713 return dd.response;
1717 /***********************************************************************
1718 File dialog functions
1719 ***********************************************************************/
1720 /* Return true if the old file selection dialog is being used. */
1722 bool
1723 xg_uses_old_file_dialog (void)
1725 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1726 return x_gtk_use_old_file_dialog;
1727 #else
1728 return 0;
1729 #endif
1733 typedef char * (*xg_get_file_func) (GtkWidget *);
1735 /* Return the selected file for file chooser dialog W.
1736 The returned string must be free:d. */
1738 static char *
1739 xg_get_file_name_from_chooser (GtkWidget *w)
1741 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1744 /* Callback called when the "Show hidden files" toggle is pressed.
1745 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1747 static void
1748 xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
1750 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1751 gboolean visible;
1752 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1753 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
1757 /* Callback called when a property changes in a file chooser.
1758 GOBJECT is the file chooser dialog, ARG1 describes the property.
1759 USER_DATA is the toggle widget in the file chooser dialog.
1760 We use this to update the "Show hidden files" toggle when the user
1761 changes that property by right clicking in the file list. */
1763 static void
1764 xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
1766 if (strcmp (arg1->name, "show-hidden") == 0)
1768 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1769 gboolean visible, toggle_on;
1771 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1772 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1774 if (!!visible != !!toggle_on)
1776 g_signal_handlers_block_by_func (G_OBJECT (wtoggle),
1777 G_CALLBACK (xg_toggle_visibility_cb),
1778 gobject);
1779 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1780 g_signal_handlers_unblock_by_func
1781 (G_OBJECT (wtoggle),
1782 G_CALLBACK (xg_toggle_visibility_cb),
1783 gobject);
1785 x_gtk_show_hidden_files = visible;
1789 /* Read a file name from the user using a file chooser dialog.
1790 F is the current frame.
1791 PROMPT is a prompt to show to the user. May not be NULL.
1792 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1793 If MUSTMATCH_P, the returned file name must be an existing
1794 file. (Actually, this only has cosmetic effects, the user can
1795 still enter a non-existing file.) *FUNC is set to a function that
1796 can be used to retrieve the selected file name from the returned widget.
1798 Returns the created widget. */
1800 static GtkWidget *
1801 xg_get_file_with_chooser (FRAME_PTR f,
1802 char *prompt,
1803 char *default_filename,
1804 bool mustmatch_p, bool only_dir_p,
1805 xg_get_file_func *func)
1807 char msgbuf[1024];
1809 GtkWidget *filewin, *wtoggle, *wbox, *wmessage IF_LINT (= NULL);
1810 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
1811 GtkFileChooserAction action = (mustmatch_p ?
1812 GTK_FILE_CHOOSER_ACTION_OPEN :
1813 GTK_FILE_CHOOSER_ACTION_SAVE);
1815 if (only_dir_p)
1816 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1818 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1819 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1820 (mustmatch_p || only_dir_p ?
1821 GTK_STOCK_OPEN : GTK_STOCK_OK),
1822 GTK_RESPONSE_OK,
1823 NULL);
1824 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
1826 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1827 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
1828 gtk_widget_show (wbox);
1829 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
1831 if (x_gtk_show_hidden_files)
1833 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
1834 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
1836 gtk_widget_show (wtoggle);
1837 g_signal_connect (G_OBJECT (wtoggle), "clicked",
1838 G_CALLBACK (xg_toggle_visibility_cb), filewin);
1839 g_signal_connect (G_OBJECT (filewin), "notify",
1840 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
1842 if (x_gtk_file_dialog_help_text)
1844 msgbuf[0] = '\0';
1845 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
1846 Show the C-l help text only for versions < 2.10. */
1847 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
1848 strcat (msgbuf, "\nType C-l to display a file name text entry box.\n");
1849 strcat (msgbuf, "\nIf you don't like this file selector, use the "
1850 "corresponding\nkey binding or customize "
1851 "use-file-dialog to turn it off.");
1853 wmessage = gtk_label_new (msgbuf);
1854 gtk_widget_show (wmessage);
1857 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
1858 if (x_gtk_file_dialog_help_text)
1859 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
1860 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
1862 if (default_filename)
1864 Lisp_Object file;
1865 struct gcpro gcpro1;
1866 char *utf8_filename;
1867 GCPRO1 (file);
1869 file = build_string (default_filename);
1871 /* File chooser does not understand ~/... in the file name. It must be
1872 an absolute name starting with /. */
1873 if (default_filename[0] != '/')
1874 file = Fexpand_file_name (file, Qnil);
1876 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
1877 if (! NILP (Ffile_directory_p (file)))
1878 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
1879 utf8_filename);
1880 else
1882 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1883 utf8_filename);
1884 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
1886 char *cp = strrchr (utf8_filename, '/');
1887 if (cp) ++cp;
1888 else cp = utf8_filename;
1889 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
1893 UNGCPRO;
1896 *func = xg_get_file_name_from_chooser;
1897 return filewin;
1900 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1902 /* Return the selected file for file selector dialog W.
1903 The returned string must be free:d. */
1905 static char *
1906 xg_get_file_name_from_selector (GtkWidget *w)
1908 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
1909 return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
1912 /* Create a file selection dialog.
1913 F is the current frame.
1914 PROMPT is a prompt to show to the user. May not be NULL.
1915 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1916 If MUSTMATCH_P, the returned file name must be an existing
1917 file. *FUNC is set to a function that can be used to retrieve the
1918 selected file name from the returned widget.
1920 Returns the created widget. */
1922 static GtkWidget *
1923 xg_get_file_with_selection (FRAME_PTR f,
1924 char *prompt,
1925 char *default_filename,
1926 bool mustmatch_p, bool only_dir_p,
1927 xg_get_file_func *func)
1929 GtkWidget *filewin;
1930 GtkFileSelection *filesel;
1932 filewin = gtk_file_selection_new (prompt);
1933 filesel = GTK_FILE_SELECTION (filewin);
1935 if (default_filename)
1936 gtk_file_selection_set_filename (filesel, default_filename);
1938 if (mustmatch_p)
1940 /* The selection_entry part of filesel is not documented. */
1941 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1942 gtk_file_selection_hide_fileop_buttons (filesel);
1945 *func = xg_get_file_name_from_selector;
1947 return filewin;
1949 #endif /* HAVE_GTK_FILE_SELECTION_NEW */
1951 /* Read a file name from the user using a file dialog, either the old
1952 file selection dialog, or the new file chooser dialog. Which to use
1953 depends on what the GTK version used has, and what the value of
1954 gtk-use-old-file-dialog.
1955 F is the current frame.
1956 PROMPT is a prompt to show to the user. May not be NULL.
1957 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1958 If MUSTMATCH_P, the returned file name must be an existing
1959 file.
1961 Returns a file name or NULL if no file was selected.
1962 The returned string must be freed by the caller. */
1964 char *
1965 xg_get_file_name (FRAME_PTR f,
1966 char *prompt,
1967 char *default_filename,
1968 bool mustmatch_p,
1969 bool only_dir_p)
1971 GtkWidget *w = 0;
1972 char *fn = 0;
1973 int filesel_done = 0;
1974 xg_get_file_func func;
1976 #ifdef HAVE_GTK_FILE_SELECTION_NEW
1978 if (xg_uses_old_file_dialog ())
1979 w = xg_get_file_with_selection (f, prompt, default_filename,
1980 mustmatch_p, only_dir_p, &func);
1981 else
1982 w = xg_get_file_with_chooser (f, prompt, default_filename,
1983 mustmatch_p, only_dir_p, &func);
1985 #else /* not HAVE_GTK_FILE_SELECTION_NEW */
1986 w = xg_get_file_with_chooser (f, prompt, default_filename,
1987 mustmatch_p, only_dir_p, &func);
1988 #endif /* not HAVE_GTK_FILE_SELECTION_NEW */
1990 gtk_widget_set_name (w, "emacs-filedialog");
1992 filesel_done = xg_dialog_run (f, w);
1993 if (filesel_done == GTK_RESPONSE_OK)
1994 fn = (*func) (w);
1996 gtk_widget_destroy (w);
1997 return fn;
2000 /***********************************************************************
2001 GTK font chooser
2002 ***********************************************************************/
2004 #ifdef HAVE_FREETYPE
2006 #if USE_NEW_GTK_FONT_CHOOSER
2008 #define XG_WEIGHT_TO_SYMBOL(w) \
2009 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2010 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2011 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2012 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2013 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2014 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2015 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2016 : Qultra_bold)
2018 #define XG_STYLE_TO_SYMBOL(s) \
2019 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2020 : s == PANGO_STYLE_ITALIC ? Qitalic \
2021 : Qnormal)
2023 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2026 static char *x_last_font_name;
2027 extern Lisp_Object Qxft;
2029 /* Pop up a GTK font selector and return the name of the font the user
2030 selects, as a C string. The returned font name follows GTK's own
2031 format:
2033 `FAMILY [VALUE1 VALUE2] SIZE'
2035 This can be parsed using font_parse_fcname in font.c.
2036 DEFAULT_NAME, if non-zero, is the default font name. */
2038 Lisp_Object
2039 xg_get_font (FRAME_PTR f, const char *default_name)
2041 GtkWidget *w;
2042 int done = 0;
2043 Lisp_Object font = Qnil;
2045 w = gtk_font_chooser_dialog_new
2046 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2048 if (default_name)
2050 /* Convert fontconfig names to Gtk names, i.e. remove - before
2051 number */
2052 char *p = strrchr (default_name, '-');
2053 if (p)
2055 char *ep = p+1;
2056 while (c_isdigit (*ep))
2057 ++ep;
2058 if (*ep == '\0') *p = ' ';
2061 else if (x_last_font_name)
2062 default_name = x_last_font_name;
2064 if (default_name)
2065 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
2067 gtk_widget_set_name (w, "emacs-fontdialog");
2068 done = xg_dialog_run (f, w);
2069 if (done == GTK_RESPONSE_OK)
2071 #if USE_NEW_GTK_FONT_CHOOSER
2072 /* Use the GTK3 font chooser. */
2073 PangoFontDescription *desc
2074 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2076 if (desc)
2078 Lisp_Object args[10];
2079 const char *name = pango_font_description_get_family (desc);
2080 gint size = pango_font_description_get_size (desc);
2081 PangoWeight weight = pango_font_description_get_weight (desc);
2082 PangoStyle style = pango_font_description_get_style (desc);
2084 args[0] = QCname;
2085 args[1] = build_string (name);
2087 args[2] = QCsize;
2088 args[3] = make_float (pango_units_to_double (size));
2090 args[4] = QCweight;
2091 args[5] = XG_WEIGHT_TO_SYMBOL (weight);
2093 args[6] = QCslant;
2094 args[7] = XG_STYLE_TO_SYMBOL (style);
2096 args[8] = QCtype;
2097 args[9] = Qxft;
2099 font = Ffont_spec (8, args);
2101 pango_font_description_free (desc);
2102 xfree (x_last_font_name);
2103 x_last_font_name = xstrdup (name);
2106 #else /* Use old font selector, which just returns the font name. */
2108 char *font_name
2109 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2111 if (font_name)
2113 font = build_string (font_name);
2114 g_free (x_last_font_name);
2115 x_last_font_name = font_name;
2117 #endif /* USE_NEW_GTK_FONT_CHOOSER */
2120 gtk_widget_destroy (w);
2121 return font;
2123 #endif /* HAVE_FREETYPE */
2127 /***********************************************************************
2128 Menu functions.
2129 ***********************************************************************/
2131 /* The name of menu items that can be used for customization. Since GTK
2132 RC files are very crude and primitive, we have to set this on all
2133 menu item names so a user can easily customize menu items. */
2135 #define MENU_ITEM_NAME "emacs-menuitem"
2138 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2139 during GC. The next member points to the items. */
2140 static xg_list_node xg_menu_cb_list;
2142 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2143 during GC. The next member points to the items. */
2144 static xg_list_node xg_menu_item_cb_list;
2146 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2147 F is the frame CL_DATA will be initialized for.
2148 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2150 The menu bar and all sub menus under the menu bar in a frame
2151 share the same structure, hence the reference count.
2153 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2154 allocated xg_menu_cb_data if CL_DATA is NULL. */
2156 static xg_menu_cb_data *
2157 make_cl_data (xg_menu_cb_data *cl_data, FRAME_PTR f, GCallback highlight_cb)
2159 if (! cl_data)
2161 cl_data = xmalloc (sizeof *cl_data);
2162 cl_data->f = f;
2163 cl_data->menu_bar_vector = f->menu_bar_vector;
2164 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2165 cl_data->highlight_cb = highlight_cb;
2166 cl_data->ref_count = 0;
2168 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2171 cl_data->ref_count++;
2173 return cl_data;
2176 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2177 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2179 When the menu bar is updated, menu items may have been added and/or
2180 removed, so menu_bar_vector and menu_bar_items_used change. We must
2181 then update CL_DATA since it is used to determine which menu
2182 item that is invoked in the menu.
2183 HIGHLIGHT_CB could change, there is no check that the same
2184 function is given when modifying a menu bar as was given when
2185 creating the menu bar. */
2187 static void
2188 update_cl_data (xg_menu_cb_data *cl_data,
2189 FRAME_PTR f,
2190 GCallback highlight_cb)
2192 if (cl_data)
2194 cl_data->f = f;
2195 cl_data->menu_bar_vector = f->menu_bar_vector;
2196 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2197 cl_data->highlight_cb = highlight_cb;
2201 /* Decrease reference count for CL_DATA.
2202 If reference count is zero, free CL_DATA. */
2204 static void
2205 unref_cl_data (xg_menu_cb_data *cl_data)
2207 if (cl_data && cl_data->ref_count > 0)
2209 cl_data->ref_count--;
2210 if (cl_data->ref_count == 0)
2212 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2213 xfree (cl_data);
2218 /* Function that marks all lisp data during GC. */
2220 void
2221 xg_mark_data (void)
2223 xg_list_node *iter;
2224 Lisp_Object rest, frame;
2226 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
2227 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
2229 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2231 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2233 if (! NILP (cb_data->help))
2234 mark_object (cb_data->help);
2237 FOR_EACH_FRAME (rest, frame)
2239 FRAME_PTR f = XFRAME (frame);
2241 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
2243 struct xg_frame_tb_info *tbinfo
2244 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2245 TB_INFO_KEY);
2246 if (tbinfo)
2248 mark_object (tbinfo->last_tool_bar);
2249 mark_object (tbinfo->style);
2256 /* Callback called when a menu item is destroyed. Used to free data.
2257 W is the widget that is being destroyed (not used).
2258 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
2260 static void
2261 menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
2263 if (client_data)
2265 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
2266 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2267 xfree (data);
2271 /* Callback called when the pointer enters/leaves a menu item.
2272 W is the parent of the menu item.
2273 EVENT is either an enter event or leave event.
2274 CLIENT_DATA is not used.
2276 Returns FALSE to tell GTK to keep processing this event. */
2278 static gboolean
2279 menuitem_highlight_callback (GtkWidget *w,
2280 GdkEventCrossing *event,
2281 gpointer client_data)
2283 GdkEvent ev;
2284 GtkWidget *subwidget;
2285 xg_menu_item_cb_data *data;
2287 ev.crossing = *event;
2288 subwidget = gtk_get_event_widget (&ev);
2289 data = (xg_menu_item_cb_data *) g_object_get_data (G_OBJECT (subwidget),
2290 XG_ITEM_DATA);
2291 if (data)
2293 if (! NILP (data->help) && data->cl_data->highlight_cb)
2295 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
2296 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
2297 (*func) (subwidget, call_data);
2301 return FALSE;
2304 /* Callback called when a menu is destroyed. Used to free data.
2305 W is the widget that is being destroyed (not used).
2306 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
2308 static void
2309 menu_destroy_callback (GtkWidget *w, gpointer client_data)
2311 unref_cl_data ((xg_menu_cb_data*) client_data);
2314 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2315 must be non-NULL) and can be inserted into a menu item.
2317 Returns the GtkHBox. */
2319 static GtkWidget *
2320 make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
2322 GtkWidget *wlbl;
2323 GtkWidget *wkey;
2324 GtkWidget *wbox;
2326 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2327 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
2328 wlbl = gtk_label_new (utf8_label);
2329 wkey = gtk_label_new (utf8_key);
2331 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2332 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
2334 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2335 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2337 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2338 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
2339 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
2341 return wbox;
2344 /* Make and return a menu item widget with the key to the right.
2345 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2346 UTF8_KEY is the text representing the key binding.
2347 ITEM is the widget_value describing the menu item.
2349 GROUP is an in/out parameter. If the menu item to be created is not
2350 part of any radio menu group, *GROUP contains NULL on entry and exit.
2351 If the menu item to be created is part of a radio menu group, on entry
2352 *GROUP contains the group to use, or NULL if this is the first item
2353 in the group. On exit, *GROUP contains the radio item group.
2355 Unfortunately, keys don't line up as nicely as in Motif,
2356 but the MacOS X version doesn't either, so I guess that is OK. */
2358 static GtkWidget *
2359 make_menu_item (const char *utf8_label,
2360 const char *utf8_key,
2361 widget_value *item,
2362 GSList **group)
2364 GtkWidget *w;
2365 GtkWidget *wtoadd = 0;
2367 /* It has been observed that some menu items have a NULL name field.
2368 This will lead to this function being called with a NULL utf8_label.
2369 GTK crashes on that so we set a blank label. Why there is a NULL
2370 name remains to be investigated. */
2371 if (! utf8_label) utf8_label = " ";
2373 if (utf8_key)
2374 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2376 if (item->button_type == BUTTON_TYPE_TOGGLE)
2378 *group = NULL;
2379 if (utf8_key) w = gtk_check_menu_item_new ();
2380 else w = gtk_check_menu_item_new_with_label (utf8_label);
2381 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2383 else if (item->button_type == BUTTON_TYPE_RADIO)
2385 if (utf8_key) w = gtk_radio_menu_item_new (*group);
2386 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
2387 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2388 if (item->selected)
2389 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2391 else
2393 *group = NULL;
2394 if (utf8_key) w = gtk_menu_item_new ();
2395 else w = gtk_menu_item_new_with_label (utf8_label);
2398 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2399 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2401 return w;
2404 static int xg_detached_menus;
2406 /* Return true if there are detached menus. */
2408 bool
2409 xg_have_tear_offs (void)
2411 return xg_detached_menus > 0;
2414 /* Callback invoked when a detached menu window is removed. Here we
2415 decrease the xg_detached_menus count.
2416 WIDGET is the top level window that is removed (the parent of the menu).
2417 CLIENT_DATA is not used. */
2419 static void
2420 tearoff_remove (GtkWidget *widget, gpointer client_data)
2422 if (xg_detached_menus > 0) --xg_detached_menus;
2425 /* Callback invoked when a menu is detached. It increases the
2426 xg_detached_menus count.
2427 WIDGET is the GtkTearoffMenuItem.
2428 CLIENT_DATA is not used. */
2430 static void
2431 tearoff_activate (GtkWidget *widget, gpointer client_data)
2433 GtkWidget *menu = gtk_widget_get_parent (widget);
2434 if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
2436 ++xg_detached_menus;
2437 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
2438 "destroy",
2439 G_CALLBACK (tearoff_remove), 0);
2444 /* Create a menu item widget, and connect the callbacks.
2445 ITEM describes the menu item.
2446 F is the frame the created menu belongs to.
2447 SELECT_CB is the callback to use when a menu item is selected.
2448 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2449 CL_DATA points to the callback data to be used for this menu.
2450 GROUP is an in/out parameter. If the menu item to be created is not
2451 part of any radio menu group, *GROUP contains NULL on entry and exit.
2452 If the menu item to be created is part of a radio menu group, on entry
2453 *GROUP contains the group to use, or NULL if this is the first item
2454 in the group. On exit, *GROUP contains the radio item group.
2456 Returns the created GtkWidget. */
2458 static GtkWidget *
2459 xg_create_one_menuitem (widget_value *item,
2460 FRAME_PTR f,
2461 GCallback select_cb,
2462 GCallback highlight_cb,
2463 xg_menu_cb_data *cl_data,
2464 GSList **group)
2466 char *utf8_label;
2467 char *utf8_key;
2468 GtkWidget *w;
2469 xg_menu_item_cb_data *cb_data;
2471 utf8_label = get_utf8_string (item->name);
2472 utf8_key = get_utf8_string (item->key);
2474 w = make_menu_item (utf8_label, utf8_key, item, group);
2476 if (utf8_label) g_free (utf8_label);
2477 if (utf8_key) g_free (utf8_key);
2479 cb_data = xmalloc (sizeof *cb_data);
2481 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2483 cb_data->select_id = 0;
2484 cb_data->help = item->help;
2485 cb_data->cl_data = cl_data;
2486 cb_data->call_data = item->call_data;
2488 g_signal_connect (G_OBJECT (w),
2489 "destroy",
2490 G_CALLBACK (menuitem_destroy_callback),
2491 cb_data);
2493 /* Put cb_data in widget, so we can get at it when modifying menubar */
2494 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2496 /* final item, not a submenu */
2497 if (item->call_data && ! item->contents)
2499 if (select_cb)
2500 cb_data->select_id
2501 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2504 return w;
2507 /* Create a full menu tree specified by DATA.
2508 F is the frame the created menu belongs to.
2509 SELECT_CB is the callback to use when a menu item is selected.
2510 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2511 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2512 If POP_UP_P, create a popup menu.
2513 If MENU_BAR_P, create a menu bar.
2514 If ADD_TEAROFF_P, add a tearoff menu item. Ignored if MENU_BAR_P.
2515 TOPMENU is the topmost GtkWidget that others shall be placed under.
2516 It may be NULL, in that case we create the appropriate widget
2517 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2518 CL_DATA is the callback data we shall use for this menu, or NULL
2519 if we haven't set the first callback yet.
2520 NAME is the name to give to the top level menu if this function
2521 creates it. May be NULL to not set any name.
2523 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2524 not NULL.
2526 This function calls itself to create submenus. */
2528 static GtkWidget *
2529 create_menus (widget_value *data,
2530 FRAME_PTR f,
2531 GCallback select_cb,
2532 GCallback deactivate_cb,
2533 GCallback highlight_cb,
2534 bool pop_up_p,
2535 bool menu_bar_p,
2536 bool add_tearoff_p,
2537 GtkWidget *topmenu,
2538 xg_menu_cb_data *cl_data,
2539 const char *name)
2541 widget_value *item;
2542 GtkWidget *wmenu = topmenu;
2543 GSList *group = NULL;
2545 if (! topmenu)
2547 if (! menu_bar_p)
2549 wmenu = gtk_menu_new ();
2550 xg_set_screen (wmenu, f);
2551 /* Connect this to the menu instead of items so we get enter/leave for
2552 disabled items also. TODO: Still does not get enter/leave for
2553 disabled items in detached menus. */
2554 g_signal_connect (G_OBJECT (wmenu),
2555 "enter-notify-event",
2556 G_CALLBACK (menuitem_highlight_callback),
2557 NULL);
2558 g_signal_connect (G_OBJECT (wmenu),
2559 "leave-notify-event",
2560 G_CALLBACK (menuitem_highlight_callback),
2561 NULL);
2563 else
2565 wmenu = gtk_menu_bar_new ();
2566 /* Set width of menu bar to a small value so it doesn't enlarge
2567 a small initial frame size. The width will be set to the
2568 width of the frame later on when it is added to a container.
2569 height -1: Natural height. */
2570 gtk_widget_set_size_request (wmenu, 1, -1);
2573 /* Put cl_data on the top menu for easier access. */
2574 cl_data = make_cl_data (cl_data, f, highlight_cb);
2575 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2576 g_signal_connect (G_OBJECT (wmenu), "destroy",
2577 G_CALLBACK (menu_destroy_callback), cl_data);
2579 if (name)
2580 gtk_widget_set_name (wmenu, name);
2582 if (deactivate_cb)
2583 g_signal_connect (G_OBJECT (wmenu),
2584 "selection-done", deactivate_cb, 0);
2587 if (! menu_bar_p && add_tearoff_p)
2589 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2590 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
2592 g_signal_connect (G_OBJECT (tearoff), "activate",
2593 G_CALLBACK (tearoff_activate), 0);
2596 for (item = data; item; item = item->next)
2598 GtkWidget *w;
2600 if (pop_up_p && !item->contents && !item->call_data
2601 && !menu_separator_name_p (item->name))
2603 char *utf8_label;
2604 /* A title for a popup. We do the same as GTK does when
2605 creating titles, but it does not look good. */
2606 group = NULL;
2607 utf8_label = get_utf8_string (item->name);
2609 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
2610 w = gtk_menu_item_new_with_label (utf8_label);
2611 gtk_widget_set_sensitive (w, FALSE);
2612 if (utf8_label) g_free (utf8_label);
2614 else if (menu_separator_name_p (item->name))
2616 group = NULL;
2617 /* GTK only have one separator type. */
2618 w = gtk_separator_menu_item_new ();
2620 else
2622 w = xg_create_one_menuitem (item,
2624 item->contents ? 0 : select_cb,
2625 highlight_cb,
2626 cl_data,
2627 &group);
2629 /* Create a possibly empty submenu for menu bar items, since some
2630 themes don't highlight items correctly without it. */
2631 if (item->contents || menu_bar_p)
2633 GtkWidget *submenu = create_menus (item->contents,
2635 select_cb,
2636 deactivate_cb,
2637 highlight_cb,
2640 add_tearoff_p,
2642 cl_data,
2644 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2648 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2649 gtk_widget_set_name (w, MENU_ITEM_NAME);
2652 return wmenu;
2655 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2656 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2657 with some text and buttons.
2658 F is the frame the created item belongs to.
2659 NAME is the name to use for the top widget.
2660 VAL is a widget_value structure describing items to be created.
2661 SELECT_CB is the callback to use when a menu item is selected or
2662 a dialog button is pressed.
2663 DEACTIVATE_CB is the callback to use when an item is deactivated.
2664 For a menu, when a sub menu is not shown anymore, for a dialog it is
2665 called when the dialog is popped down.
2666 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2668 Returns the widget created. */
2670 GtkWidget *
2671 xg_create_widget (const char *type, const char *name, FRAME_PTR f, widget_value *val,
2672 GCallback select_cb, GCallback deactivate_cb,
2673 GCallback highlight_cb)
2675 GtkWidget *w = 0;
2676 bool menu_bar_p = strcmp (type, "menubar") == 0;
2677 bool pop_up_p = strcmp (type, "popup") == 0;
2679 if (strcmp (type, "dialog") == 0)
2681 w = create_dialog (val, select_cb, deactivate_cb);
2682 xg_set_screen (w, f);
2683 gtk_window_set_transient_for (GTK_WINDOW (w),
2684 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2685 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
2686 gtk_widget_set_name (w, "emacs-dialog");
2687 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
2689 else if (menu_bar_p || pop_up_p)
2691 w = create_menus (val->contents,
2693 select_cb,
2694 deactivate_cb,
2695 highlight_cb,
2696 pop_up_p,
2697 menu_bar_p,
2698 menu_bar_p,
2701 name);
2703 /* Set the cursor to an arrow for popup menus when they are mapped.
2704 This is done by default for menu bar menus. */
2705 if (pop_up_p)
2707 /* Must realize so the GdkWindow inside the widget is created. */
2708 gtk_widget_realize (w);
2709 xg_set_cursor (w, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
2712 else
2714 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2715 type);
2718 return w;
2721 /* Return the label for menu item WITEM. */
2723 static const char *
2724 xg_get_menu_item_label (GtkMenuItem *witem)
2726 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2727 return gtk_label_get_label (wlabel);
2730 /* Return true if the menu item WITEM has the text LABEL. */
2732 static bool
2733 xg_item_label_same_p (GtkMenuItem *witem, const char *label)
2735 bool is_same = 0;
2736 char *utf8_label = get_utf8_string (label);
2737 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2739 if (! old_label && ! utf8_label)
2740 is_same = 1;
2741 else if (old_label && utf8_label)
2742 is_same = strcmp (utf8_label, old_label) == 0;
2744 if (utf8_label) g_free (utf8_label);
2746 return is_same;
2749 /* Destroy widgets in LIST. */
2751 static void
2752 xg_destroy_widgets (GList *list)
2754 GList *iter;
2756 for (iter = list; iter; iter = g_list_next (iter))
2758 GtkWidget *w = GTK_WIDGET (iter->data);
2760 /* Destroying the widget will remove it from the container it is in. */
2761 gtk_widget_destroy (w);
2765 /* Update the top level names in MENUBAR (i.e. not submenus).
2766 F is the frame the menu bar belongs to.
2767 *LIST is a list with the current menu bar names (menu item widgets).
2768 ITER is the item within *LIST that shall be updated.
2769 POS is the numerical position, starting at 0, of ITER in *LIST.
2770 VAL describes what the menu bar shall look like after the update.
2771 SELECT_CB is the callback to use when a menu item is selected.
2772 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2773 CL_DATA points to the callback data to be used for this menu bar.
2775 This function calls itself to walk through the menu bar names. */
2777 static void
2778 xg_update_menubar (GtkWidget *menubar,
2779 FRAME_PTR f,
2780 GList **list,
2781 GList *iter,
2782 int pos,
2783 widget_value *val,
2784 GCallback select_cb,
2785 GCallback deactivate_cb,
2786 GCallback highlight_cb,
2787 xg_menu_cb_data *cl_data)
2789 if (! iter && ! val)
2790 return;
2791 else if (iter && ! val)
2793 /* Item(s) have been removed. Remove all remaining items. */
2794 xg_destroy_widgets (iter);
2796 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2797 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2798 gtk_menu_item_new_with_label (""),
2800 /* All updated. */
2801 val = 0;
2802 iter = 0;
2804 else if (! iter && val)
2806 /* Item(s) added. Add all new items in one call. */
2807 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
2808 0, 1, 0, menubar, cl_data, 0);
2810 /* All updated. */
2811 val = 0;
2812 iter = 0;
2814 /* Below this neither iter or val is NULL */
2815 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
2817 /* This item is still the same, check next item. */
2818 val = val->next;
2819 iter = g_list_next (iter);
2820 ++pos;
2822 else /* This item is changed. */
2824 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
2825 GtkMenuItem *witem2 = 0;
2826 bool val_in_menubar = 0;
2827 bool iter_in_new_menubar = 0;
2828 GList *iter2;
2829 widget_value *cur;
2831 /* See if the changed entry (val) is present later in the menu bar */
2832 for (iter2 = iter;
2833 iter2 && ! val_in_menubar;
2834 iter2 = g_list_next (iter2))
2836 witem2 = GTK_MENU_ITEM (iter2->data);
2837 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2840 /* See if the current entry (iter) is present later in the
2841 specification for the new menu bar. */
2842 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2843 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2845 if (val_in_menubar && ! iter_in_new_menubar)
2847 int nr = pos;
2849 /* This corresponds to:
2850 Current: A B C
2851 New: A C
2852 Remove B. */
2854 g_object_ref (G_OBJECT (witem));
2855 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2856 gtk_widget_destroy (GTK_WIDGET (witem));
2858 /* Must get new list since the old changed. */
2859 g_list_free (*list);
2860 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2861 while (nr-- > 0) iter = g_list_next (iter);
2863 else if (! val_in_menubar && ! iter_in_new_menubar)
2865 /* This corresponds to:
2866 Current: A B C
2867 New: A X C
2868 Rename B to X. This might seem to be a strange thing to do,
2869 since if there is a menu under B it will be totally wrong for X.
2870 But consider editing a C file. Then there is a C-mode menu
2871 (corresponds to B above).
2872 If then doing C-x C-f the minibuf menu (X above) replaces the
2873 C-mode menu. When returning from the minibuffer, we get
2874 back the C-mode menu. Thus we do:
2875 Rename B to X (C-mode to minibuf menu)
2876 Rename X to B (minibuf to C-mode menu).
2877 If the X menu hasn't been invoked, the menu under B
2878 is up to date when leaving the minibuffer. */
2879 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
2880 char *utf8_label = get_utf8_string (val->name);
2881 GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
2883 gtk_label_set_text (wlabel, utf8_label);
2885 /* If this item has a submenu that has been detached, change
2886 the title in the WM decorations also. */
2887 if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
2888 /* Set the title of the detached window. */
2889 gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
2891 if (utf8_label) g_free (utf8_label);
2892 iter = g_list_next (iter);
2893 val = val->next;
2894 ++pos;
2896 else if (! val_in_menubar && iter_in_new_menubar)
2898 /* This corresponds to:
2899 Current: A B C
2900 New: A X B C
2901 Insert X. */
2903 int nr = pos;
2904 GSList *group = 0;
2905 GtkWidget *w = xg_create_one_menuitem (val,
2907 select_cb,
2908 highlight_cb,
2909 cl_data,
2910 &group);
2912 /* Create a possibly empty submenu for menu bar items, since some
2913 themes don't highlight items correctly without it. */
2914 GtkWidget *submenu = create_menus (NULL, f,
2915 select_cb, deactivate_cb,
2916 highlight_cb,
2917 0, 0, 0, 0, cl_data, 0);
2918 gtk_widget_set_name (w, MENU_ITEM_NAME);
2919 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2920 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2922 g_list_free (*list);
2923 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2924 while (nr-- > 0) iter = g_list_next (iter);
2925 iter = g_list_next (iter);
2926 val = val->next;
2927 ++pos;
2929 else /* if (val_in_menubar && iter_in_new_menubar) */
2931 int nr = pos;
2932 /* This corresponds to:
2933 Current: A B C
2934 New: A C B
2935 Move C before B */
2937 g_object_ref (G_OBJECT (witem2));
2938 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2939 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2940 GTK_WIDGET (witem2), pos);
2941 g_object_unref (G_OBJECT (witem2));
2943 g_list_free (*list);
2944 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2945 while (nr-- > 0) iter = g_list_next (iter);
2946 if (iter) iter = g_list_next (iter);
2947 val = val->next;
2948 ++pos;
2952 /* Update the rest of the menu bar. */
2953 xg_update_menubar (menubar, f, list, iter, pos, val,
2954 select_cb, deactivate_cb, highlight_cb, cl_data);
2957 /* Update the menu item W so it corresponds to VAL.
2958 SELECT_CB is the callback to use when a menu item is selected.
2959 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2960 CL_DATA is the data to set in the widget for menu invocation. */
2962 static void
2963 xg_update_menu_item (widget_value *val,
2964 GtkWidget *w,
2965 GCallback select_cb,
2966 GCallback highlight_cb,
2967 xg_menu_cb_data *cl_data)
2969 GtkWidget *wchild;
2970 GtkLabel *wlbl = 0;
2971 GtkLabel *wkey = 0;
2972 char *utf8_label;
2973 char *utf8_key;
2974 const char *old_label = 0;
2975 const char *old_key = 0;
2976 xg_menu_item_cb_data *cb_data;
2978 wchild = XG_BIN_CHILD (w);
2979 utf8_label = get_utf8_string (val->name);
2980 utf8_key = get_utf8_string (val->key);
2982 /* See if W is a menu item with a key. See make_menu_item above. */
2983 if (GTK_IS_BOX (wchild))
2985 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2987 wlbl = GTK_LABEL (list->data);
2988 wkey = GTK_LABEL (list->next->data);
2989 g_list_free (list);
2991 if (! utf8_key)
2993 /* Remove the key and keep just the label. */
2994 g_object_ref (G_OBJECT (wlbl));
2995 gtk_container_remove (GTK_CONTAINER (w), wchild);
2996 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
2997 g_object_unref (G_OBJECT (wlbl));
2998 wkey = 0;
3002 else /* Just a label. */
3004 wlbl = GTK_LABEL (wchild);
3006 /* Check if there is now a key. */
3007 if (utf8_key)
3009 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3010 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
3012 wlbl = GTK_LABEL (list->data);
3013 wkey = GTK_LABEL (list->next->data);
3014 g_list_free (list);
3016 gtk_container_remove (GTK_CONTAINER (w), wchild);
3017 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3022 if (wkey) old_key = gtk_label_get_label (wkey);
3023 if (wlbl) old_label = gtk_label_get_label (wlbl);
3025 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
3026 gtk_label_set_text (wkey, utf8_key);
3028 if (! old_label || strcmp (utf8_label, old_label) != 0)
3029 gtk_label_set_text (wlbl, utf8_label);
3031 if (utf8_key) g_free (utf8_key);
3032 if (utf8_label) g_free (utf8_label);
3034 if (! val->enabled && gtk_widget_get_sensitive (w))
3035 gtk_widget_set_sensitive (w, FALSE);
3036 else if (val->enabled && ! gtk_widget_get_sensitive (w))
3037 gtk_widget_set_sensitive (w, TRUE);
3039 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
3040 XG_ITEM_DATA);
3041 if (cb_data)
3043 cb_data->call_data = val->call_data;
3044 cb_data->help = val->help;
3045 cb_data->cl_data = cl_data;
3047 /* We assume the callback functions don't change. */
3048 if (val->call_data && ! val->contents)
3050 /* This item shall have a select callback. */
3051 if (! cb_data->select_id)
3052 cb_data->select_id
3053 = g_signal_connect (G_OBJECT (w), "activate",
3054 select_cb, cb_data);
3056 else if (cb_data->select_id)
3058 g_signal_handler_disconnect (w, cb_data->select_id);
3059 cb_data->select_id = 0;
3064 /* Update the toggle menu item W so it corresponds to VAL. */
3066 static void
3067 xg_update_toggle_item (widget_value *val, GtkWidget *w)
3069 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3072 /* Update the radio menu item W so it corresponds to VAL. */
3074 static void
3075 xg_update_radio_item (widget_value *val, GtkWidget *w)
3077 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3080 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3081 SUBMENU may be NULL, in that case a new menu is created.
3082 F is the frame the menu bar belongs to.
3083 VAL describes the contents of the menu bar.
3084 SELECT_CB is the callback to use when a menu item is selected.
3085 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3086 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3087 CL_DATA is the call back data to use for any newly created items.
3089 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3090 was NULL. */
3092 static GtkWidget *
3093 xg_update_submenu (GtkWidget *submenu,
3094 FRAME_PTR f,
3095 widget_value *val,
3096 GCallback select_cb,
3097 GCallback deactivate_cb,
3098 GCallback highlight_cb,
3099 xg_menu_cb_data *cl_data)
3101 GtkWidget *newsub = submenu;
3102 GList *list = 0;
3103 GList *iter;
3104 widget_value *cur;
3105 bool has_tearoff_p = 0;
3106 GList *first_radio = 0;
3108 if (submenu)
3109 list = gtk_container_get_children (GTK_CONTAINER (submenu));
3111 for (cur = val, iter = list;
3112 cur && iter;
3113 iter = g_list_next (iter), cur = cur->next)
3115 GtkWidget *w = GTK_WIDGET (iter->data);
3117 /* Skip tearoff items, they have no counterpart in val. */
3118 if (GTK_IS_TEAROFF_MENU_ITEM (w))
3120 has_tearoff_p = 1;
3121 iter = g_list_next (iter);
3122 if (iter) w = GTK_WIDGET (iter->data);
3123 else break;
3126 /* Remember first radio button in a group. If we get a mismatch in
3127 a radio group we must rebuild the whole group so that the connections
3128 in GTK becomes correct. */
3129 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3130 first_radio = iter;
3131 else if (cur->button_type != BUTTON_TYPE_RADIO
3132 && ! GTK_IS_RADIO_MENU_ITEM (w))
3133 first_radio = 0;
3135 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3137 if (! menu_separator_name_p (cur->name))
3138 break;
3140 else if (GTK_IS_CHECK_MENU_ITEM (w))
3142 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3143 break;
3144 xg_update_toggle_item (cur, w);
3145 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3147 else if (GTK_IS_RADIO_MENU_ITEM (w))
3149 if (cur->button_type != BUTTON_TYPE_RADIO)
3150 break;
3151 xg_update_radio_item (cur, w);
3152 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3154 else if (GTK_IS_MENU_ITEM (w))
3156 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3157 GtkWidget *sub;
3159 if (cur->button_type != BUTTON_TYPE_NONE ||
3160 menu_separator_name_p (cur->name))
3161 break;
3163 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3165 sub = gtk_menu_item_get_submenu (witem);
3166 if (sub && ! cur->contents)
3168 /* Not a submenu anymore. */
3169 g_object_ref (G_OBJECT (sub));
3170 remove_submenu (witem);
3171 gtk_widget_destroy (sub);
3173 else if (cur->contents)
3175 GtkWidget *nsub;
3177 nsub = xg_update_submenu (sub, f, cur->contents,
3178 select_cb, deactivate_cb,
3179 highlight_cb, cl_data);
3181 /* If this item just became a submenu, we must set it. */
3182 if (nsub != sub)
3183 gtk_menu_item_set_submenu (witem, nsub);
3186 else
3188 /* Structural difference. Remove everything from here and down
3189 in SUBMENU. */
3190 break;
3194 /* Remove widgets from first structural change. */
3195 if (iter)
3197 /* If we are adding new menu items below, we must remove from
3198 first radio button so that radio groups become correct. */
3199 if (cur && first_radio) xg_destroy_widgets (first_radio);
3200 else xg_destroy_widgets (iter);
3203 if (cur)
3205 /* More items added. Create them. */
3206 newsub = create_menus (cur,
3208 select_cb,
3209 deactivate_cb,
3210 highlight_cb,
3213 ! has_tearoff_p,
3214 submenu,
3215 cl_data,
3219 if (list) g_list_free (list);
3221 return newsub;
3224 /* Update the MENUBAR.
3225 F is the frame the menu bar belongs to.
3226 VAL describes the contents of the menu bar.
3227 If DEEP_P, rebuild all but the top level menu names in
3228 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3229 SELECT_CB is the callback to use when a menu item is selected.
3230 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3231 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
3233 void
3234 xg_modify_menubar_widgets (GtkWidget *menubar, FRAME_PTR f, widget_value *val,
3235 bool deep_p,
3236 GCallback select_cb, GCallback deactivate_cb,
3237 GCallback highlight_cb)
3239 xg_menu_cb_data *cl_data;
3240 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
3242 if (! list) return;
3244 cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
3245 XG_FRAME_DATA);
3247 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
3248 select_cb, deactivate_cb, highlight_cb, cl_data);
3250 if (deep_p)
3252 widget_value *cur;
3254 /* Update all sub menus.
3255 We must keep the submenus (GTK menu item widgets) since the
3256 X Window in the XEvent that activates the menu are those widgets. */
3258 /* Update cl_data, menu_item things in F may have changed. */
3259 update_cl_data (cl_data, f, highlight_cb);
3261 for (cur = val->contents; cur; cur = cur->next)
3263 GList *iter;
3264 GtkWidget *sub = 0;
3265 GtkWidget *newsub;
3266 GtkMenuItem *witem = 0;
3268 /* Find sub menu that corresponds to val and update it. */
3269 for (iter = list ; iter; iter = g_list_next (iter))
3271 witem = GTK_MENU_ITEM (iter->data);
3272 if (xg_item_label_same_p (witem, cur->name))
3274 sub = gtk_menu_item_get_submenu (witem);
3275 break;
3279 newsub = xg_update_submenu (sub,
3281 cur->contents,
3282 select_cb,
3283 deactivate_cb,
3284 highlight_cb,
3285 cl_data);
3286 /* sub may still be NULL. If we just updated non deep and added
3287 a new menu bar item, it has no sub menu yet. So we set the
3288 newly created sub menu under witem. */
3289 if (newsub != sub && witem != 0)
3291 xg_set_screen (newsub, f);
3292 gtk_menu_item_set_submenu (witem, newsub);
3297 g_list_free (list);
3298 gtk_widget_show_all (menubar);
3301 /* Callback called when the menu bar W is mapped.
3302 Used to find the height of the menu bar if we didn't get it
3303 after showing the widget. */
3305 static void
3306 menubar_map_cb (GtkWidget *w, gpointer user_data)
3308 GtkRequisition req;
3309 FRAME_PTR f = (FRAME_PTR) user_data;
3310 gtk_widget_get_preferred_size (w, NULL, &req);
3311 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3313 FRAME_MENUBAR_HEIGHT (f) = req.height;
3314 xg_height_or_width_changed (f);
3318 /* Recompute all the widgets of frame F, when the menu bar has been
3319 changed. */
3321 void
3322 xg_update_frame_menubar (FRAME_PTR f)
3324 struct x_output *x = f->output_data.x;
3325 GtkRequisition req;
3327 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
3328 return;
3330 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
3331 return; /* Already done this, happens for frames created invisible. */
3333 block_input ();
3335 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3336 FALSE, FALSE, 0);
3337 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3339 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
3340 gtk_widget_show_all (x->menubar_widget);
3341 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
3343 /* If menu bar doesn't know its height yet, cheat a little so the frame
3344 doesn't jump so much when resized later in menubar_map_cb. */
3345 if (req.height == 0)
3346 req.height = 23;
3348 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3350 FRAME_MENUBAR_HEIGHT (f) = req.height;
3351 xg_height_or_width_changed (f);
3353 unblock_input ();
3356 /* Get rid of the menu bar of frame F, and free its storage.
3357 This is used when deleting a frame, and when turning off the menu bar. */
3359 void
3360 free_frame_menubar (FRAME_PTR f)
3362 struct x_output *x = f->output_data.x;
3364 if (x->menubar_widget)
3366 block_input ();
3368 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3369 /* The menubar and its children shall be deleted when removed from
3370 the container. */
3371 x->menubar_widget = 0;
3372 FRAME_MENUBAR_HEIGHT (f) = 0;
3373 xg_height_or_width_changed (f);
3374 unblock_input ();
3378 bool
3379 xg_event_is_for_menubar (FRAME_PTR f, XEvent *event)
3381 struct x_output *x = f->output_data.x;
3382 GList *iter;
3383 GdkRectangle rec;
3384 GList *list;
3385 GdkDisplay *gdpy;
3386 GdkWindow *gw;
3387 GdkEvent gevent;
3388 GtkWidget *gwdesc;
3390 if (! x->menubar_widget) return 0;
3392 if (! (event->xbutton.x >= 0
3393 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3394 && event->xbutton.y >= 0
3395 && event->xbutton.y < f->output_data.x->menubar_height
3396 && event->xbutton.same_screen))
3397 return 0;
3399 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3400 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
3401 if (! gw) return 0;
3402 gevent.any.window = gw;
3403 gevent.any.type = GDK_NOTHING;
3404 gwdesc = gtk_get_event_widget (&gevent);
3405 if (! gwdesc) return 0;
3406 if (! GTK_IS_MENU_BAR (gwdesc)
3407 && ! GTK_IS_MENU_ITEM (gwdesc)
3408 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3409 return 0;
3411 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
3412 if (! list) return 0;
3413 rec.x = event->xbutton.x;
3414 rec.y = event->xbutton.y;
3415 rec.width = 1;
3416 rec.height = 1;
3418 for (iter = list ; iter; iter = g_list_next (iter))
3420 GtkWidget *w = GTK_WIDGET (iter->data);
3421 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
3422 break;
3424 g_list_free (list);
3425 return iter != 0;
3430 /***********************************************************************
3431 Scroll bar functions
3432 ***********************************************************************/
3435 /* Setting scroll bar values invokes the callback. Use this variable
3436 to indicate that callback should do nothing. */
3438 bool xg_ignore_gtk_scrollbar;
3440 /* The width of the scroll bar for the current theme. */
3442 static int scroll_bar_width_for_theme;
3444 /* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3445 may be larger than 32 bits. Keep a mapping from integer index to widget
3446 pointers to get around the 32 bit limitation. */
3448 static struct
3450 GtkWidget **widgets;
3451 ptrdiff_t max_size;
3452 ptrdiff_t used;
3453 } id_to_widget;
3455 /* Grow this much every time we need to allocate more */
3457 #define ID_TO_WIDGET_INCR 32
3459 /* Store the widget pointer W in id_to_widget and return the integer index. */
3461 static ptrdiff_t
3462 xg_store_widget_in_map (GtkWidget *w)
3464 ptrdiff_t i;
3466 if (id_to_widget.max_size == id_to_widget.used)
3468 ptrdiff_t new_size;
3469 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
3470 memory_full (SIZE_MAX);
3472 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
3473 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3474 new_size, sizeof (GtkWidget *));
3476 for (i = id_to_widget.max_size; i < new_size; ++i)
3477 id_to_widget.widgets[i] = 0;
3478 id_to_widget.max_size = new_size;
3481 /* Just loop over the array and find a free place. After all,
3482 how many scroll bars are we creating? Should be a small number.
3483 The check above guarantees we will find a free place. */
3484 for (i = 0; i < id_to_widget.max_size; ++i)
3486 if (! id_to_widget.widgets[i])
3488 id_to_widget.widgets[i] = w;
3489 ++id_to_widget.used;
3491 return i;
3495 /* Should never end up here */
3496 emacs_abort ();
3499 /* Remove pointer at IDX from id_to_widget.
3500 Called when scroll bar is destroyed. */
3502 static void
3503 xg_remove_widget_from_map (ptrdiff_t idx)
3505 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3507 id_to_widget.widgets[idx] = 0;
3508 --id_to_widget.used;
3512 /* Get the widget pointer at IDX from id_to_widget. */
3514 static GtkWidget *
3515 xg_get_widget_from_map (ptrdiff_t idx)
3517 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3518 return id_to_widget.widgets[idx];
3520 return 0;
3523 static void
3524 update_theme_scrollbar_width (void)
3526 #ifdef HAVE_GTK3
3527 GtkAdjustment *vadj;
3528 #else
3529 GtkObject *vadj;
3530 #endif
3531 GtkWidget *wscroll;
3532 int w = 0, b = 0;
3534 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
3535 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3536 g_object_ref_sink (G_OBJECT (wscroll));
3537 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3538 gtk_widget_destroy (wscroll);
3539 g_object_unref (G_OBJECT (wscroll));
3540 w += 2*b;
3541 if (w < 16) w = 16;
3542 scroll_bar_width_for_theme = w;
3546 xg_get_default_scrollbar_width (void)
3548 return scroll_bar_width_for_theme;
3551 /* Return the scrollbar id for X Window WID on display DPY.
3552 Return -1 if WID not in id_to_widget. */
3554 ptrdiff_t
3555 xg_get_scroll_id_for_window (Display *dpy, Window wid)
3557 ptrdiff_t idx;
3558 GtkWidget *w;
3560 w = xg_win_to_widget (dpy, wid);
3562 if (w)
3564 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3565 if (id_to_widget.widgets[idx] == w)
3566 return idx;
3569 return -1;
3572 /* Callback invoked when scroll bar WIDGET is destroyed.
3573 DATA is the index into id_to_widget for WIDGET.
3574 We free pointer to last scroll bar values here and remove the index. */
3576 static void
3577 xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
3579 intptr_t id = (intptr_t) data;
3580 xg_remove_widget_from_map (id);
3583 /* Create a scroll bar widget for frame F. Store the scroll bar
3584 in BAR.
3585 SCROLL_CALLBACK is the callback to invoke when the value of the
3586 bar changes.
3587 END_CALLBACK is the callback to invoke when scrolling ends.
3588 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3589 to set resources for the widget. */
3591 void
3592 xg_create_scroll_bar (FRAME_PTR f,
3593 struct scroll_bar *bar,
3594 GCallback scroll_callback,
3595 GCallback end_callback,
3596 const char *scroll_bar_name)
3598 GtkWidget *wscroll;
3599 GtkWidget *webox;
3600 intptr_t scroll_id;
3601 #ifdef HAVE_GTK3
3602 GtkAdjustment *vadj;
3603 #else
3604 GtkObject *vadj;
3605 #endif
3607 /* Page, step increment values are not so important here, they
3608 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3609 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3610 0.1, 0.1, 0.1);
3612 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
3613 webox = gtk_event_box_new ();
3614 gtk_widget_set_name (wscroll, scroll_bar_name);
3615 #ifndef HAVE_GTK3
3616 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
3617 #endif
3618 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
3620 scroll_id = xg_store_widget_in_map (wscroll);
3622 g_signal_connect (G_OBJECT (wscroll),
3623 "destroy",
3624 G_CALLBACK (xg_gtk_scroll_destroy),
3625 (gpointer) scroll_id);
3626 g_signal_connect (G_OBJECT (wscroll),
3627 "change-value",
3628 scroll_callback,
3629 (gpointer) bar);
3630 g_signal_connect (G_OBJECT (wscroll),
3631 "button-release-event",
3632 end_callback,
3633 (gpointer) bar);
3635 /* The scroll bar widget does not draw on a window of its own. Instead
3636 it draws on the parent window, in this case the edit widget. So
3637 whenever the edit widget is cleared, the scroll bar needs to redraw
3638 also, which causes flicker. Put an event box between the edit widget
3639 and the scroll bar, so the scroll bar instead draws itself on the
3640 event box window. */
3641 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3642 gtk_container_add (GTK_CONTAINER (webox), wscroll);
3645 /* Set the cursor to an arrow. */
3646 xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
3648 bar->x_window = scroll_id;
3651 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
3653 void
3654 xg_remove_scroll_bar (FRAME_PTR f, ptrdiff_t scrollbar_id)
3656 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3657 if (w)
3659 GtkWidget *wparent = gtk_widget_get_parent (w);
3660 gtk_widget_destroy (w);
3661 gtk_widget_destroy (wparent);
3662 SET_FRAME_GARBAGED (f);
3666 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3667 in frame F.
3668 TOP/LEFT are the new pixel positions where the bar shall appear.
3669 WIDTH, HEIGHT is the size in pixels the bar shall have. */
3671 void
3672 xg_update_scrollbar_pos (FRAME_PTR f,
3673 ptrdiff_t scrollbar_id,
3674 int top,
3675 int left,
3676 int width,
3677 int height)
3680 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
3682 if (wscroll)
3684 GtkWidget *wfixed = f->output_data.x->edit_widget;
3685 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
3686 gint msl;
3688 /* Clear out old position. */
3689 int oldx = -1, oldy = -1, oldw, oldh;
3690 if (gtk_widget_get_parent (wparent) == wfixed)
3692 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3693 "x", &oldx, "y", &oldy, NULL);
3694 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3697 /* Move and resize to new values. */
3698 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
3699 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3700 if (msl > height)
3702 /* No room. Hide scroll bar as some themes output a warning if
3703 the height is less than the min size. */
3704 gtk_widget_hide (wparent);
3705 gtk_widget_hide (wscroll);
3707 else
3709 gtk_widget_show_all (wparent);
3710 gtk_widget_set_size_request (wscroll, width, height);
3712 gtk_widget_queue_draw (wfixed);
3713 gdk_window_process_all_updates ();
3714 if (oldx != -1 && oldw > 0 && oldh > 0)
3716 /* Clear under old scroll bar position. This must be done after
3717 the gtk_widget_queue_draw and gdk_window_process_all_updates
3718 above. */
3719 x_clear_area (FRAME_X_DISPLAY (f),
3720 FRAME_X_WINDOW (f),
3721 oldx, oldy, oldw, oldh, 0);
3724 /* GTK does not redraw until the main loop is entered again, but
3725 if there are no X events pending we will not enter it. So we sync
3726 here to get some events. */
3728 x_sync (f);
3729 SET_FRAME_GARBAGED (f);
3730 cancel_mouse_face (f);
3734 /* Get the current value of the range, truncated to an integer. */
3736 static int
3737 int_gtk_range_get_value (GtkRange *range)
3739 return gtk_range_get_value (range);
3743 /* Set the thumb size and position of scroll bar BAR. We are currently
3744 displaying PORTION out of a whole WHOLE, and our position POSITION. */
3746 void
3747 xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
3748 int portion,
3749 int position,
3750 int whole)
3752 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
3754 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
3756 if (wscroll && NILP (bar->dragging))
3758 GtkAdjustment *adj;
3759 gdouble shown;
3760 gdouble top;
3761 int size, value;
3762 int old_size;
3763 int new_step;
3764 bool changed = 0;
3766 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3768 /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
3769 rather than the real portion value. This makes the thumb less likely
3770 to resize and that looks better. */
3771 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
3772 /* When the thumb is at the bottom, position == whole.
3773 So we need to increase `whole' to make space for the thumb. */
3774 whole += portion;
3776 if (whole <= 0)
3777 top = 0, shown = 1;
3778 else
3780 top = (gdouble) position / whole;
3781 shown = (gdouble) portion / whole;
3784 size = shown * XG_SB_RANGE;
3785 size = min (size, XG_SB_RANGE);
3786 size = max (size, 1);
3788 value = top * XG_SB_RANGE;
3789 value = min (value, XG_SB_MAX - size);
3790 value = max (value, XG_SB_MIN);
3792 /* Assume all lines are of equal size. */
3793 new_step = size / max (1, FRAME_LINES (f));
3795 old_size = gtk_adjustment_get_page_size (adj);
3796 if (old_size != size)
3798 int old_step = gtk_adjustment_get_step_increment (adj);
3799 if (old_step != new_step)
3801 gtk_adjustment_set_page_size (adj, size);
3802 gtk_adjustment_set_step_increment (adj, new_step);
3803 /* Assume a page increment is about 95% of the page size */
3804 gtk_adjustment_set_page_increment (adj, size - size / 20);
3805 changed = 1;
3809 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3811 block_input ();
3813 /* gtk_range_set_value invokes the callback. Set
3814 ignore_gtk_scrollbar to make the callback do nothing */
3815 xg_ignore_gtk_scrollbar = 1;
3817 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3818 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3819 else if (changed)
3820 gtk_adjustment_changed (adj);
3822 xg_ignore_gtk_scrollbar = 0;
3824 unblock_input ();
3829 /* Return true if EVENT is for a scroll bar in frame F.
3830 When the same X window is used for several Gtk+ widgets, we cannot
3831 say for sure based on the X window alone if an event is for the
3832 frame. This function does additional checks. */
3834 bool
3835 xg_event_is_for_scrollbar (FRAME_PTR f, XEvent *event)
3837 bool retval = 0;
3839 if (f && event->type == ButtonPress && event->xbutton.button < 4)
3841 /* Check if press occurred outside the edit widget. */
3842 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
3843 GdkWindow *gwin;
3844 #ifdef HAVE_GTK3
3845 GdkDevice *gdev = gdk_device_manager_get_client_pointer
3846 (gdk_display_get_device_manager (gdpy));
3847 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
3848 #else
3849 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
3850 #endif
3851 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
3853 else if (f
3854 && ((event->type == ButtonRelease && event->xbutton.button < 4)
3855 || event->type == MotionNotify))
3857 /* If we are releasing or moving the scroll bar, it has the grab. */
3858 GtkWidget *w = gtk_grab_get_current ();
3859 retval = w != 0 && GTK_IS_SCROLLBAR (w);
3862 return retval;
3867 /***********************************************************************
3868 Tool bar functions
3869 ***********************************************************************/
3870 /* The key for the data we put in the GtkImage widgets. The data is
3871 the image used by Emacs. We use this to see if we need to update
3872 the GtkImage with a new image. */
3873 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
3875 /* The key for storing the latest modifiers so the activate callback can
3876 get them. */
3877 #define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
3879 /* The key for storing the button widget in its proxy menu item. */
3880 #define XG_TOOL_BAR_PROXY_BUTTON "emacs-tool-bar-proxy-button"
3882 /* The key for the data we put in the GtkImage widgets. The data is
3883 the stock name used by Emacs. We use this to see if we need to update
3884 the GtkImage with a new image. */
3885 #define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
3887 /* As above, but this is used for named theme widgets, as opposed to
3888 stock items. */
3889 #define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
3891 /* Callback function invoked when a tool bar item is pressed.
3892 W is the button widget in the tool bar that got pressed,
3893 CLIENT_DATA is an integer that is the index of the button in the
3894 tool bar. 0 is the first button. */
3896 static gboolean
3897 xg_tool_bar_button_cb (GtkWidget *widget,
3898 GdkEventButton *event,
3899 gpointer user_data)
3901 intptr_t state = event->state;
3902 gpointer ptr = (gpointer) state;
3903 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
3904 return FALSE;
3908 /* Callback function invoked when a tool bar item is pressed.
3909 W is the button widget in the tool bar that got pressed,
3910 CLIENT_DATA is an integer that is the index of the button in the
3911 tool bar. 0 is the first button. */
3913 static void
3914 xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
3916 intptr_t idx = (intptr_t) client_data;
3917 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
3918 intptr_t mod = (intptr_t) gmod;
3920 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
3921 Lisp_Object key, frame;
3922 struct input_event event;
3923 EVENT_INIT (event);
3925 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
3926 return;
3928 idx *= TOOL_BAR_ITEM_NSLOTS;
3930 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
3931 XSETFRAME (frame, f);
3933 /* We generate two events here. The first one is to set the prefix
3934 to `(tool_bar)', see keyboard.c. */
3935 event.kind = TOOL_BAR_EVENT;
3936 event.frame_or_window = frame;
3937 event.arg = frame;
3938 kbd_buffer_store_event (&event);
3940 event.kind = TOOL_BAR_EVENT;
3941 event.frame_or_window = frame;
3942 event.arg = key;
3943 /* Convert between the modifier bits GDK uses and the modifier bits
3944 Emacs uses. This assumes GDK and X masks are the same, which they are when
3945 this is written. */
3946 event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod);
3947 kbd_buffer_store_event (&event);
3949 /* Return focus to the frame after we have clicked on a detached
3950 tool bar button. */
3951 Fx_focus_frame (frame);
3954 /* Callback function invoked when a tool bar item is pressed in a detached
3955 tool bar or the overflow drop down menu.
3956 We just call xg_tool_bar_callback.
3957 W is the menu item widget that got pressed,
3958 CLIENT_DATA is an integer that is the index of the button in the
3959 tool bar. 0 is the first button. */
3961 static void
3962 xg_tool_bar_proxy_callback (GtkWidget *w, gpointer client_data)
3964 GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
3965 XG_TOOL_BAR_PROXY_BUTTON));
3966 xg_tool_bar_callback (wbutton, client_data);
3970 static gboolean
3971 xg_tool_bar_help_callback (GtkWidget *w,
3972 GdkEventCrossing *event,
3973 gpointer client_data);
3975 /* This callback is called when a help is to be shown for an item in
3976 the detached tool bar when the detached tool bar it is not expanded. */
3978 static gboolean
3979 xg_tool_bar_proxy_help_callback (GtkWidget *w,
3980 GdkEventCrossing *event,
3981 gpointer client_data)
3983 GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
3984 XG_TOOL_BAR_PROXY_BUTTON));
3986 return xg_tool_bar_help_callback (wbutton, event, client_data);
3989 static GtkWidget *
3990 xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
3992 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
3993 GtkWidget *c1 = (GtkWidget *) clist->data;
3994 GtkWidget *c2 = clist->next ? (GtkWidget *) clist->next->data : NULL;
3996 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
3997 g_list_free (clist);
3998 return GTK_IS_LABEL (c1) ? c1 : c2;
4002 /* This callback is called when a tool item should create a proxy item,
4003 such as for the overflow menu. Also called when the tool bar is detached.
4004 If we don't create a proxy menu item, the detached tool bar will be
4005 blank. */
4007 static gboolean
4008 xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
4010 GtkButton *wbutton = GTK_BUTTON (XG_BIN_CHILD (XG_BIN_CHILD (toolitem)));
4011 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4012 GtkWidget *c1;
4013 GtkLabel *wlbl = GTK_LABEL (xg_get_tool_bar_widgets (vb, &c1));
4014 GtkImage *wimage = GTK_IMAGE (c1);
4015 GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label
4016 (wlbl ? gtk_label_get_text (wlbl) : "");
4017 GtkWidget *wmenuimage;
4020 if (gtk_button_get_use_stock (wbutton))
4021 wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
4022 GTK_ICON_SIZE_MENU);
4023 else
4025 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
4026 GtkImageType store_type = gtk_image_get_storage_type (wimage);
4028 g_object_set (G_OBJECT (settings), "gtk-menu-images", TRUE, NULL);
4030 if (store_type == GTK_IMAGE_STOCK)
4032 gchar *stock_id;
4033 gtk_image_get_stock (wimage, &stock_id, NULL);
4034 wmenuimage = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
4036 else if (store_type == GTK_IMAGE_ICON_SET)
4038 GtkIconSet *icon_set;
4039 gtk_image_get_icon_set (wimage, &icon_set, NULL);
4040 wmenuimage = gtk_image_new_from_icon_set (icon_set,
4041 GTK_ICON_SIZE_MENU);
4043 else if (store_type == GTK_IMAGE_PIXBUF)
4045 gint width, height;
4047 if (settings &&
4048 gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
4049 &width, &height))
4051 GdkPixbuf *src_pixbuf, *dest_pixbuf;
4053 src_pixbuf = gtk_image_get_pixbuf (wimage);
4054 dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
4055 GDK_INTERP_BILINEAR);
4057 wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
4059 else
4061 fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
4062 emacs_abort ();
4065 else if (store_type == GTK_IMAGE_ICON_NAME)
4067 const gchar *icon_name;
4068 GtkIconSize icon_size;
4070 gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
4071 wmenuimage = gtk_image_new_from_icon_name (icon_name,
4072 GTK_ICON_SIZE_MENU);
4074 else
4076 fprintf (stderr, "internal error: store_type is %d\n", store_type);
4077 emacs_abort ();
4080 if (wmenuimage)
4081 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (wmenuitem), wmenuimage);
4083 g_signal_connect (G_OBJECT (wmenuitem),
4084 "activate",
4085 G_CALLBACK (xg_tool_bar_proxy_callback),
4086 user_data);
4089 g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON,
4090 (gpointer) wbutton);
4091 gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem);
4092 gtk_widget_set_sensitive (wmenuitem,
4093 gtk_widget_get_sensitive (GTK_WIDGET (wbutton)));
4095 /* Use enter/leave notify to show help. We use the events
4096 rather than the GtkButton specific signals "enter" and
4097 "leave", so we can have only one callback. The event
4098 will tell us what kind of event it is. */
4099 g_signal_connect (G_OBJECT (wmenuitem),
4100 "enter-notify-event",
4101 G_CALLBACK (xg_tool_bar_proxy_help_callback),
4102 user_data);
4103 g_signal_connect (G_OBJECT (wmenuitem),
4104 "leave-notify-event",
4105 G_CALLBACK (xg_tool_bar_proxy_help_callback),
4106 user_data);
4108 return TRUE;
4111 /* This callback is called when a tool bar is detached. We must set
4112 the height of the tool bar to zero when this happens so frame sizes
4113 are correctly calculated.
4114 WBOX is the handle box widget that enables detach/attach of the tool bar.
4115 W is the tool bar widget.
4116 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
4118 static void
4119 xg_tool_bar_detach_callback (GtkHandleBox *wbox,
4120 GtkWidget *w,
4121 gpointer client_data)
4123 FRAME_PTR f = (FRAME_PTR) client_data;
4125 g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
4126 NULL);
4128 if (f)
4130 GtkRequisition req, req2;
4131 FRAME_X_OUTPUT (f)->toolbar_detached = 1;
4132 gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
4133 gtk_widget_get_preferred_size (w, NULL, &req2);
4134 req.width -= req2.width;
4135 req.height -= req2.height;
4136 if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
4137 FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
4138 else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
4139 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
4140 else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
4141 FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
4142 else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
4143 FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
4144 xg_height_or_width_changed (f);
4148 /* This callback is called when a tool bar is reattached. We must set
4149 the height of the tool bar when this happens so frame sizes
4150 are correctly calculated.
4151 WBOX is the handle box widget that enables detach/attach of the tool bar.
4152 W is the tool bar widget.
4153 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
4155 static void
4156 xg_tool_bar_attach_callback (GtkHandleBox *wbox,
4157 GtkWidget *w,
4158 gpointer client_data)
4160 FRAME_PTR f = (FRAME_PTR) client_data;
4161 g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
4163 if (f)
4165 GtkRequisition req, req2;
4166 FRAME_X_OUTPUT (f)->toolbar_detached = 0;
4167 gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
4168 gtk_widget_get_preferred_size (w, NULL, &req2);
4169 req.width += req2.width;
4170 req.height += req2.height;
4171 if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
4172 FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
4173 else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
4174 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
4175 else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
4176 FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
4177 else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
4178 FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
4179 xg_height_or_width_changed (f);
4183 /* This callback is called when the mouse enters or leaves a tool bar item.
4184 It is used for displaying and hiding the help text.
4185 W is the tool bar item, a button.
4186 EVENT is either an enter event or leave event.
4187 CLIENT_DATA is an integer that is the index of the button in the
4188 tool bar. 0 is the first button.
4190 Returns FALSE to tell GTK to keep processing this event. */
4192 static gboolean
4193 xg_tool_bar_help_callback (GtkWidget *w,
4194 GdkEventCrossing *event,
4195 gpointer client_data)
4197 intptr_t idx = (intptr_t) client_data;
4198 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
4199 Lisp_Object help, frame;
4201 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
4202 return FALSE;
4204 if (event->type == GDK_ENTER_NOTIFY)
4206 idx *= TOOL_BAR_ITEM_NSLOTS;
4207 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
4209 if (NILP (help))
4210 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
4212 else
4213 help = Qnil;
4215 XSETFRAME (frame, f);
4216 kbd_buffer_store_help_event (frame, help);
4218 return FALSE;
4222 /* This callback is called when a tool bar item shall be redrawn.
4223 It modifies the expose event so that the GtkImage widget redraws the
4224 whole image. This to overcome a bug that makes GtkImage draw the image
4225 in the wrong place when it tries to redraw just a part of the image.
4226 W is the GtkImage to be redrawn.
4227 EVENT is the expose event for W.
4228 CLIENT_DATA is unused.
4230 Returns FALSE to tell GTK to keep processing this event. */
4232 #ifndef HAVE_GTK3
4233 static gboolean
4234 xg_tool_bar_item_expose_callback (GtkWidget *w,
4235 GdkEventExpose *event,
4236 gpointer client_data)
4238 gint width, height;
4240 gdk_drawable_get_size (event->window, &width, &height);
4241 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4242 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4244 event->area.x = max (0, event->area.x);
4245 event->area.y = max (0, event->area.y);
4247 event->area.width = max (width, event->area.width);
4248 event->area.height = max (height, event->area.height);
4250 return FALSE;
4252 #endif
4254 #ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4255 #define toolbar_set_orientation(w, o) \
4256 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4257 #else
4258 #define toolbar_set_orientation(w, o) \
4259 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4260 #endif
4262 /* Attach a tool bar to frame F. */
4264 static void
4265 xg_pack_tool_bar (FRAME_PTR f, Lisp_Object pos)
4267 struct x_output *x = f->output_data.x;
4268 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
4270 toolbar_set_orientation (x->toolbar_widget,
4271 into_hbox
4272 ? GTK_ORIENTATION_VERTICAL
4273 : GTK_ORIENTATION_HORIZONTAL);
4274 if (!x->handlebox_widget)
4276 x->handlebox_widget = gtk_handle_box_new ();
4277 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
4278 G_CALLBACK (xg_tool_bar_detach_callback), f);
4279 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
4280 G_CALLBACK (xg_tool_bar_attach_callback), f);
4281 gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
4282 x->toolbar_widget);
4285 if (into_hbox)
4287 gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
4288 GTK_POS_TOP);
4289 gtk_box_pack_start (GTK_BOX (x->hbox_widget), x->handlebox_widget,
4290 FALSE, FALSE, 0);
4292 if (EQ (pos, Qleft))
4293 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
4294 x->handlebox_widget,
4296 x->toolbar_in_hbox = 1;
4298 else
4300 bool vbox_pos = x->menubar_widget != 0;
4301 gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
4302 GTK_POS_LEFT);
4303 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
4304 FALSE, FALSE, 0);
4306 if (EQ (pos, Qtop))
4307 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
4308 x->handlebox_widget,
4309 vbox_pos);
4310 x->toolbar_in_hbox = 0;
4314 /* Create a tool bar for frame F. */
4316 static void
4317 xg_create_tool_bar (FRAME_PTR f)
4319 struct x_output *x = f->output_data.x;
4320 #if GTK_CHECK_VERSION (3, 3, 6)
4321 GtkStyleContext *gsty;
4322 #endif
4323 struct xg_frame_tb_info *tbinfo
4324 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4325 TB_INFO_KEY);
4326 if (! tbinfo)
4328 tbinfo = xmalloc (sizeof (*tbinfo));
4329 tbinfo->last_tool_bar = Qnil;
4330 tbinfo->style = Qnil;
4331 tbinfo->hmargin = tbinfo->vmargin = 0;
4332 tbinfo->dir = GTK_TEXT_DIR_NONE;
4333 tbinfo->n_last_items = 0;
4334 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4335 TB_INFO_KEY,
4336 tbinfo);
4339 x->toolbar_widget = gtk_toolbar_new ();
4340 x->toolbar_detached = 0;
4342 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4344 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
4345 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
4346 #if GTK_CHECK_VERSION (3, 3, 6)
4347 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4348 gtk_style_context_add_class (gsty, "primary-toolbar");
4349 #endif
4353 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
4355 /* Find the right-to-left image named by RTL in the tool bar images for F.
4356 Returns IMAGE if RTL is not found. */
4358 static Lisp_Object
4359 find_rtl_image (FRAME_PTR f, Lisp_Object image, Lisp_Object rtl)
4361 int i;
4362 Lisp_Object file, rtl_name;
4363 struct gcpro gcpro1, gcpro2;
4364 GCPRO2 (file, rtl_name);
4366 rtl_name = Ffile_name_nondirectory (rtl);
4368 for (i = 0; i < f->n_tool_bar_items; ++i)
4370 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
4371 if (!NILP (file = file_for_image (rtl_image)))
4373 file = call1 (intern ("file-name-sans-extension"),
4374 Ffile_name_nondirectory (file));
4375 if (! NILP (Fequal (file, rtl_name)))
4377 image = rtl_image;
4378 break;
4383 return image;
4386 static GtkToolItem *
4387 xg_make_tool_item (FRAME_PTR f,
4388 GtkWidget *wimage,
4389 GtkWidget **wbutton,
4390 const char *label,
4391 int i, bool horiz, bool text_image)
4393 GtkToolItem *ti = gtk_tool_item_new ();
4394 GtkWidget *vb = gtk_box_new (horiz
4395 ? GTK_ORIENTATION_HORIZONTAL
4396 : GTK_ORIENTATION_VERTICAL,
4398 GtkWidget *wb = gtk_button_new ();
4399 /* The eventbox is here so we can have tooltips on disabled items. */
4400 GtkWidget *weventbox = gtk_event_box_new ();
4401 #if GTK_CHECK_VERSION (3, 3, 6)
4402 GtkCssProvider *css_prov = gtk_css_provider_new ();
4403 GtkStyleContext *gsty;
4405 gtk_css_provider_load_from_data (css_prov,
4406 "GtkEventBox {"
4407 " background-color: transparent;"
4408 "}",
4409 -1, NULL);
4411 gsty = gtk_widget_get_style_context (weventbox);
4412 gtk_style_context_add_provider (gsty,
4413 GTK_STYLE_PROVIDER (css_prov),
4414 GTK_STYLE_PROVIDER_PRIORITY_USER);
4415 g_object_unref (css_prov);
4416 #endif
4418 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4420 if (wimage && !text_image)
4421 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4422 if (label)
4423 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
4424 if (wimage && text_image)
4425 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4427 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4428 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4429 gtk_container_add (GTK_CONTAINER (wb), vb);
4430 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4431 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4433 if (wimage || label)
4435 intptr_t ii = i;
4436 gpointer gi = (gpointer) ii;
4438 g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
4439 G_CALLBACK (xg_tool_bar_menu_proxy),
4440 gi);
4442 g_signal_connect (G_OBJECT (wb), "clicked",
4443 G_CALLBACK (xg_tool_bar_callback),
4444 gi);
4446 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4448 #ifndef HAVE_GTK3
4449 /* Catch expose events to overcome an annoying redraw bug, see
4450 comment for xg_tool_bar_item_expose_callback. */
4451 g_signal_connect (G_OBJECT (ti),
4452 "expose-event",
4453 G_CALLBACK (xg_tool_bar_item_expose_callback),
4455 #endif
4456 gtk_tool_item_set_homogeneous (ti, FALSE);
4458 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
4459 no distinction based on modifiers in the activate callback,
4460 so we have to do it ourselves. */
4461 g_signal_connect (wb, "button-release-event",
4462 G_CALLBACK (xg_tool_bar_button_cb),
4463 NULL);
4465 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
4467 /* Use enter/leave notify to show help. We use the events
4468 rather than the GtkButton specific signals "enter" and
4469 "leave", so we can have only one callback. The event
4470 will tell us what kind of event it is. */
4471 g_signal_connect (G_OBJECT (weventbox),
4472 "enter-notify-event",
4473 G_CALLBACK (xg_tool_bar_help_callback),
4474 gi);
4475 g_signal_connect (G_OBJECT (weventbox),
4476 "leave-notify-event",
4477 G_CALLBACK (xg_tool_bar_help_callback),
4478 gi);
4481 if (wbutton) *wbutton = wb;
4483 return ti;
4486 static bool
4487 is_box_type (GtkWidget *vb, bool is_horizontal)
4489 #ifdef HAVE_GTK3
4490 bool ret = 0;
4491 if (GTK_IS_BOX (vb))
4493 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4494 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4495 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4497 return ret;
4498 #else
4499 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4500 #endif
4504 static bool
4505 xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4506 const char *icon_name, const struct image *img,
4507 const char *label, bool horiz)
4509 gpointer old;
4510 GtkWidget *wimage;
4511 GtkWidget *vb = XG_BIN_CHILD (wbutton);
4512 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
4514 /* Check if the tool icon matches. */
4515 if (stock_name && wimage)
4517 old = g_object_get_data (G_OBJECT (wimage),
4518 XG_TOOL_BAR_STOCK_NAME);
4519 if (!old || strcmp (old, stock_name))
4520 return 1;
4522 else if (icon_name && wimage)
4524 old = g_object_get_data (G_OBJECT (wimage),
4525 XG_TOOL_BAR_ICON_NAME);
4526 if (!old || strcmp (old, icon_name))
4527 return 1;
4529 else if (wimage)
4531 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
4532 XG_TOOL_BAR_IMAGE_DATA);
4533 Pixmap old_img = (Pixmap) gold_img;
4534 if (old_img != img->pixmap)
4535 return 1;
4538 /* Check button configuration and label. */
4539 if (is_box_type (vb, horiz)
4540 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4541 return 1;
4543 /* Ensure label is correct. */
4544 if (label && wlbl)
4545 gtk_label_set_text (GTK_LABEL (wlbl), label);
4546 return 0;
4549 static bool
4550 xg_update_tool_bar_sizes (FRAME_PTR f)
4552 struct x_output *x = f->output_data.x;
4553 GtkRequisition req;
4554 int nl = 0, nr = 0, nt = 0, nb = 0;
4556 gtk_widget_get_preferred_size (GTK_WIDGET (x->handlebox_widget), NULL, &req);
4557 if (x->toolbar_in_hbox)
4559 int pos;
4560 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
4561 x->handlebox_widget,
4562 "position", &pos, NULL);
4563 if (pos == 0) nl = req.width;
4564 else nr = req.width;
4566 else
4568 int pos;
4569 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
4570 x->handlebox_widget,
4571 "position", &pos, NULL);
4572 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4573 else nb = req.height;
4576 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4577 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4578 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4579 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4581 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4582 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4583 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4584 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4585 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4586 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4587 return 1;
4590 return 0;
4594 /* Update the tool bar for frame F. Add new buttons and remove old. */
4596 void
4597 update_frame_tool_bar (FRAME_PTR f)
4599 int i, j;
4600 struct x_output *x = f->output_data.x;
4601 int hmargin = 0, vmargin = 0;
4602 GtkToolbar *wtoolbar;
4603 GtkToolItem *ti;
4604 GtkTextDirection dir;
4605 bool pack_tool_bar = x->handlebox_widget == NULL;
4606 Lisp_Object style;
4607 bool text_image, horiz;
4608 struct xg_frame_tb_info *tbinfo;
4610 if (! FRAME_GTK_WIDGET (f))
4611 return;
4613 block_input ();
4615 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
4617 hmargin = XFASTINT (Vtool_bar_button_margin);
4618 vmargin = XFASTINT (Vtool_bar_button_margin);
4620 else if (CONSP (Vtool_bar_button_margin))
4622 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
4623 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4625 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
4626 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4629 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4630 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4631 i.e. zero. This means that margins less than
4632 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4633 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4634 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4636 if (! x->toolbar_widget)
4637 xg_create_tool_bar (f);
4639 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
4640 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
4642 style = Ftool_bar_get_system_style ();
4644 /* Are we up to date? */
4645 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4646 TB_INFO_KEY);
4648 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4649 && tbinfo->n_last_items == f->n_tool_bar_items
4650 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4651 && tbinfo->dir == dir
4652 && ! NILP (Fequal (tbinfo->style, style))
4653 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
4655 unblock_input ();
4656 return;
4659 tbinfo->last_tool_bar = f->tool_bar_items;
4660 tbinfo->n_last_items = f->n_tool_bar_items;
4661 tbinfo->style = style;
4662 tbinfo->hmargin = hmargin;
4663 tbinfo->vmargin = vmargin;
4664 tbinfo->dir = dir;
4666 text_image = EQ (style, Qtext_image_horiz);
4667 horiz = EQ (style, Qboth_horiz) || text_image;
4669 for (i = j = 0; i < f->n_tool_bar_items; ++i)
4671 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4672 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
4673 int idx;
4674 ptrdiff_t img_id;
4675 int icon_size = 0;
4676 struct image *img = NULL;
4677 Lisp_Object image;
4678 Lisp_Object stock = Qnil;
4679 GtkStockItem stock_item;
4680 char *stock_name = NULL;
4681 char *icon_name = NULL;
4682 Lisp_Object rtl;
4683 GtkWidget *wbutton = NULL;
4684 Lisp_Object specified_file;
4685 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
4686 const char *label
4687 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4688 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4689 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4690 : "";
4692 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4694 /* If this is a separator, use a gtk separator item. */
4695 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4697 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4699 if (ti)
4700 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4701 GTK_WIDGET (ti));
4702 ti = gtk_separator_tool_item_new ();
4703 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4705 j++;
4706 continue;
4709 /* Otherwise, the tool-bar item is an ordinary button. */
4711 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4713 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4714 ti = NULL;
4717 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
4719 /* Ignore invalid image specifications. */
4720 image = PROP (TOOL_BAR_ITEM_IMAGES);
4721 if (!valid_image_p (image))
4723 if (ti)
4724 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4725 GTK_WIDGET (ti));
4726 continue;
4729 specified_file = file_for_image (image);
4730 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4731 stock = call1 (Qx_gtk_map_stock, specified_file);
4733 if (STRINGP (stock))
4735 stock_name = SSDATA (stock);
4736 if (stock_name[0] == 'n' && stock_name[1] == ':')
4738 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4739 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (screen);
4741 icon_name = stock_name + 2;
4742 stock_name = NULL;
4743 stock = Qnil;
4745 if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
4746 icon_name = NULL;
4747 else
4748 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4750 else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
4751 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4752 else
4754 stock = Qnil;
4755 stock_name = NULL;
4759 if (stock_name == NULL && icon_name == NULL)
4761 /* No stock image, or stock item not known. Try regular
4762 image. If image is a vector, choose it according to the
4763 button state. */
4764 if (dir == GTK_TEXT_DIR_RTL
4765 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4766 && STRINGP (rtl))
4767 image = find_rtl_image (f, image, rtl);
4769 if (VECTORP (image))
4771 if (enabled_p)
4772 idx = (selected_p
4773 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4774 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4775 else
4776 idx = (selected_p
4777 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4778 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
4780 eassert (ASIZE (image) >= idx);
4781 image = AREF (image, idx);
4783 else
4784 idx = -1;
4786 img_id = lookup_image (f, image);
4787 img = IMAGE_FROM_ID (f, img_id);
4788 prepare_image_for_display (f, img);
4790 if (img->load_failed_p || img->pixmap == None)
4792 if (ti)
4793 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4794 GTK_WIDGET (ti));
4795 continue;
4799 /* If there is an existing widget, check if it's stale; if so,
4800 remove it and make a new tool item from scratch. */
4801 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
4802 img, label, horiz))
4804 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4805 GTK_WIDGET (ti));
4806 ti = NULL;
4809 if (ti == NULL)
4811 GtkWidget *w;
4813 /* Save the image so we can see if an update is needed the
4814 next time we call xg_tool_item_match_p. */
4815 if (EQ (style, Qtext))
4816 w = NULL;
4817 else if (stock_name)
4819 w = gtk_image_new_from_stock (stock_name, icon_size);
4820 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
4821 (gpointer) xstrdup (stock_name),
4822 (GDestroyNotify) xfree);
4824 else if (icon_name)
4826 w = gtk_image_new_from_icon_name (icon_name, icon_size);
4827 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
4828 (gpointer) xstrdup (icon_name),
4829 (GDestroyNotify) xfree);
4831 else
4833 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
4834 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
4835 (gpointer)img->pixmap);
4838 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
4839 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
4840 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4843 #undef PROP
4845 gtk_widget_set_sensitive (wbutton, enabled_p);
4846 j++;
4849 /* Remove buttons not longer needed. */
4852 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
4853 if (ti)
4854 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4855 } while (ti != NULL);
4857 if (f->n_tool_bar_items != 0)
4859 if (pack_tool_bar)
4860 xg_pack_tool_bar (f, f->tool_bar_position);
4861 gtk_widget_show_all (GTK_WIDGET (x->handlebox_widget));
4862 if (xg_update_tool_bar_sizes (f))
4863 xg_height_or_width_changed (f);
4866 unblock_input ();
4869 /* Deallocate all resources for the tool bar on frame F.
4870 Remove the tool bar. */
4872 void
4873 free_frame_tool_bar (FRAME_PTR f)
4875 struct x_output *x = f->output_data.x;
4877 if (x->toolbar_widget)
4879 struct xg_frame_tb_info *tbinfo;
4880 bool is_packed = x->handlebox_widget != 0;
4881 block_input ();
4882 /* We may have created the toolbar_widget in xg_create_tool_bar, but
4883 not the x->handlebox_widget which is created in xg_pack_tool_bar. */
4884 if (is_packed)
4886 if (x->toolbar_in_hbox)
4887 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
4888 x->handlebox_widget);
4889 else
4890 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
4891 x->handlebox_widget);
4893 else
4894 gtk_widget_destroy (x->toolbar_widget);
4896 x->toolbar_widget = 0;
4897 x->handlebox_widget = 0;
4898 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4899 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
4901 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4902 TB_INFO_KEY);
4903 if (tbinfo)
4905 xfree (tbinfo);
4906 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4907 TB_INFO_KEY,
4908 NULL);
4911 xg_height_or_width_changed (f);
4913 unblock_input ();
4917 void
4918 xg_change_toolbar_position (FRAME_PTR f, Lisp_Object pos)
4920 struct x_output *x = f->output_data.x;
4922 if (! x->toolbar_widget || ! x->handlebox_widget)
4923 return;
4925 block_input ();
4926 g_object_ref (x->handlebox_widget);
4927 if (x->toolbar_in_hbox)
4928 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
4929 x->handlebox_widget);
4930 else
4931 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
4932 x->handlebox_widget);
4933 xg_pack_tool_bar (f, pos);
4934 g_object_unref (x->handlebox_widget);
4935 if (xg_update_tool_bar_sizes (f))
4936 xg_height_or_width_changed (f);
4938 unblock_input ();
4943 /***********************************************************************
4944 Initializing
4945 ***********************************************************************/
4946 void
4947 xg_initialize (void)
4949 GtkBindingSet *binding_set;
4950 GtkSettings *settings;
4952 #if HAVE_XFT
4953 /* Work around a bug with corrupted data if libXft gets unloaded. This way
4954 we keep it permanently linked in. */
4955 XftInit (0);
4956 #endif
4958 gdpy_def = NULL;
4959 xg_ignore_gtk_scrollbar = 0;
4960 xg_detached_menus = 0;
4961 xg_menu_cb_list.prev = xg_menu_cb_list.next =
4962 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
4964 id_to_widget.max_size = id_to_widget.used = 0;
4965 id_to_widget.widgets = 0;
4967 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
4968 (gdk_display_get_default ()));
4969 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
4970 bindings. It doesn't seem to be any way to remove properties,
4971 so we set it to VoidSymbol which in X means "no key". */
4972 gtk_settings_set_string_property (settings,
4973 "gtk-menu-bar-accel",
4974 "VoidSymbol",
4975 EMACS_CLASS);
4977 /* Make GTK text input widgets use Emacs style keybindings. This is
4978 Emacs after all. */
4979 gtk_settings_set_string_property (settings,
4980 "gtk-key-theme-name",
4981 "Emacs",
4982 EMACS_CLASS);
4984 /* Make dialogs close on C-g. Since file dialog inherits from
4985 dialog, this works for them also. */
4986 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
4987 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
4988 "close", 0);
4990 /* Make menus close on C-g. */
4991 binding_set = gtk_binding_set_by_class (g_type_class_ref
4992 (GTK_TYPE_MENU_SHELL));
4993 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
4994 "cancel", 0);
4995 update_theme_scrollbar_width ();
4997 x_last_font_name = NULL;
5000 #endif /* USE_GTK */