Again fix last fix.
[bpt/emacs.git] / src / gtkutil.c
CommitLineData
f392e843 1/* Functions for creating and updating GTK widgets.
95df8112 2
ab422c4d 3Copyright (C) 2003-2013 Free Software Foundation, Inc.
f392e843
JD
4
5This file is part of GNU Emacs.
6
9ec0b715 7GNU Emacs is free software: you can redistribute it and/or modify
f392e843 8it under the terms of the GNU General Public License as published by
9ec0b715
GM
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
f392e843
JD
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
9ec0b715 18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
f392e843 19
f3bbb3b5 20#include <config.h>
f392e843
JD
21
22#ifdef USE_GTK
6e1a67fb 23#include <float.h>
e8794476 24#include <stdio.h>
620f13b0
PE
25
26#include <c-ctype.h>
27
f392e843
JD
28#include "lisp.h"
29#include "xterm.h"
30#include "blockinput.h"
aa477689 31#include "syssignal.h"
f392e843 32#include "window.h"
f392e843
JD
33#include "gtkutil.h"
34#include "termhooks.h"
5b07197a
DL
35#include "keyboard.h"
36#include "charset.h"
37#include "coding.h"
d350e350
DA
38#include "font.h"
39
f392e843 40#include <gdk/gdkkeysyms.h>
ff5dec5c 41#include "xsettings.h"
f392e843 42
ddaa36e1
AS
43#ifdef HAVE_XFT
44#include <X11/Xft/Xft.h>
45#endif
810f2256 46
0afb4571
J
47#ifdef HAVE_GTK3
48#include <gtk/gtkx.h>
c195f2de 49#include "emacsgtkfixed.h"
0afb4571
J
50#endif
51
f392e843 52#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
be786000 53 (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
cea9be54 54
bfeabdc3
JD
55#define FRAME_TOTAL_PIXEL_WIDTH(f) \
56 (FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f))
57
e547b051
J
58#ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
59#define gtk_widget_set_has_window(w, b) \
60 (gtk_fixed_set_has_window (GTK_FIXED (w), b))
61#endif
62#ifndef HAVE_GTK_DIALOG_GET_ACTION_AREA
63#define gtk_dialog_get_action_area(w) ((w)->action_area)
64#define gtk_dialog_get_content_area(w) ((w)->vbox)
65#endif
66#ifndef HAVE_GTK_WIDGET_GET_SENSITIVE
67#define gtk_widget_get_sensitive(w) (GTK_WIDGET_SENSITIVE (w))
68#endif
69#ifndef HAVE_GTK_ADJUSTMENT_GET_PAGE_SIZE
70#define gtk_adjustment_set_page_size(w, s) ((w)->page_size = (s))
71#define gtk_adjustment_set_page_increment(w, s) ((w)->page_increment = (s))
72#define gtk_adjustment_get_step_increment(w) ((w)->step_increment)
73#define gtk_adjustment_set_step_increment(w, s) ((w)->step_increment = (s))
74#endif
7583e2a0 75#if GTK_CHECK_VERSION (2, 12, 0)
e547b051
J
76#define remove_submenu(w) gtk_menu_item_set_submenu ((w), NULL)
77#else
78#define remove_submenu(w) gtk_menu_item_remove_submenu ((w))
79#endif
80
7583e2a0 81#if GTK_CHECK_VERSION (3, 2, 0)
f2045622
CY
82#define USE_NEW_GTK_FONT_CHOOSER 1
83#else
84#define USE_NEW_GTK_FONT_CHOOSER 0
32bcadb4
JD
85#define gtk_font_chooser_dialog_new(x, y) \
86 gtk_font_selection_dialog_new (x)
87#undef GTK_FONT_CHOOSER
88#define GTK_FONT_CHOOSER(x) GTK_FONT_SELECTION_DIALOG (x)
89#define gtk_font_chooser_set_font(x, y) \
90 gtk_font_selection_dialog_set_font_name (x, y)
32bcadb4
JD
91#endif
92
0afb4571 93#ifndef HAVE_GTK3
01e0b5ad 94#ifdef USE_GTK_TOOLTIP
0afb4571 95#define gdk_window_get_screen(w) gdk_drawable_get_screen (w)
01e0b5ad 96#endif
0afb4571 97#define gdk_window_get_geometry(w, a, b, c, d) \
7c86ee98 98 gdk_window_get_geometry (w, a, b, c, d, 0)
0afb4571
J
99#define gdk_x11_window_lookup_for_display(d, w) \
100 gdk_xid_table_lookup_for_display (d, w)
383b7c95
JD
101#define gtk_box_new(ori, spacing) \
102 ((ori) == GTK_ORIENTATION_HORIZONTAL \
103 ? gtk_hbox_new (FALSE, (spacing)) : gtk_vbox_new (FALSE, (spacing)))
104#define gtk_scrollbar_new(ori, spacing) \
105 ((ori) == GTK_ORIENTATION_HORIZONTAL \
106 ? gtk_hscrollbar_new ((spacing)) : gtk_vscrollbar_new ((spacing)))
6048fb2a 107#ifndef GDK_KEY_g
0afb4571
J
108#define GDK_KEY_g GDK_g
109#endif
383b7c95 110#endif /* HAVE_GTK3 */
0afb4571 111
4039c786
CY
112#define XG_BIN_CHILD(x) gtk_bin_get_child (GTK_BIN (x))
113
c195f2de 114static void update_theme_scrollbar_width (void);
1e5524e7 115
ca06f160
JD
116#define TB_INFO_KEY "xg_frame_tb_info"
117struct xg_frame_tb_info
118{
119 Lisp_Object last_tool_bar;
120 Lisp_Object style;
121 int n_last_items;
122 int hmargin, vmargin;
123 GtkTextDirection dir;
124};
125
810f2256
JD
126\f
127/***********************************************************************
128 Display handling functions
129 ***********************************************************************/
130
c1ffddfc
CY
131/* Keep track of the default display, or NULL if there is none. Emacs
132 may close all its displays. */
879ffad9
JD
133
134static GdkDisplay *gdpy_def;
135
810f2256
JD
136/* When the GTK widget W is to be created on a display for F that
137 is not the default display, set the display for W.
138 W can be a GtkMenu or a GtkWindow widget. */
71bacd48 139
810f2256 140static void
a10c8269 141xg_set_screen (GtkWidget *w, struct frame *f)
810f2256 142{
0afb4571 143 if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
810f2256
JD
144 {
145 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
146 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
147
148 if (GTK_IS_MENU (w))
149 gtk_menu_set_screen (GTK_MENU (w), gscreen);
150 else
151 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
152 }
153}
154
155
810f2256
JD
156/* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
157 *DPY is set to NULL if the display can't be opened.
158
159 Returns non-zero if display could be opened, zero if display could not
160 be opened, and less than zero if the GTK version doesn't support
e1dbe924 161 multiple displays. */
71bacd48 162
e547b051 163void
971de7fb 164xg_display_open (char *display_name, Display **dpy)
810f2256 165{
810f2256
JD
166 GdkDisplay *gdpy;
167
168 gdpy = gdk_display_open (display_name);
c39ea606
JD
169 if (!gdpy_def && gdpy)
170 {
171 gdpy_def = gdpy;
172 gdk_display_manager_set_default_display (gdk_display_manager_get (),
173 gdpy);
174 }
810f2256 175
c1ffddfc 176 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
810f2256
JD
177}
178
179
71bacd48
JD
180/* Close display DPY. */
181
810f2256
JD
182void
183xg_display_close (Display *dpy)
184{
810f2256
JD
185 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
186
c1ffddfc
CY
187 /* If this is the default display, try to change it before closing.
188 If there is no other display to use, gdpy_def is set to NULL, and
189 the next call to xg_display_open resets the default display. */
810f2256
JD
190 if (gdk_display_get_default () == gdpy)
191 {
192 struct x_display_info *dpyinfo;
c1ffddfc 193 GdkDisplay *gdpy_new = NULL;
810f2256
JD
194
195 /* Find another display. */
196 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
197 if (dpyinfo->display != dpy)
198 {
c1ffddfc
CY
199 gdpy_new = gdk_x11_lookup_xdisplay (dpyinfo->display);
200 gdk_display_manager_set_default_display (gdk_display_manager_get (),
201 gdpy_new);
810f2256
JD
202 break;
203 }
c1ffddfc 204 gdpy_def = gdpy_new;
810f2256
JD
205 }
206
7583e2a0 207#if GTK_CHECK_VERSION (2, 0, 0) && ! GTK_CHECK_VERSION (2, 10, 0)
c1ffddfc
CY
208 /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
209 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way we
210 can continue running, but there will be memory leaks. */
9d7dda8a 211 g_object_run_dispose (G_OBJECT (gdpy));
810f2256 212#else
9d7dda8a 213 /* This seems to be fixed in GTK 2.10. */
810f2256
JD
214 gdk_display_close (gdpy);
215#endif
810f2256 216}
cea9be54 217
f392e843
JD
218\f
219/***********************************************************************
220 Utility functions
221 ***********************************************************************/
f392e843
JD
222/* The next two variables and functions are taken from lwlib. */
223static widget_value *widget_value_free_list;
224static int malloc_cpt;
225
226/* Allocate a widget_value structure, either by taking one from the
227 widget_value_free_list or by malloc:ing a new one.
228
229 Return a pointer to the allocated structure. */
71bacd48 230
f392e843 231widget_value *
971de7fb 232malloc_widget_value (void)
f392e843
JD
233{
234 widget_value *wv;
235 if (widget_value_free_list)
236 {
237 wv = widget_value_free_list;
238 widget_value_free_list = wv->free_list;
239 wv->free_list = 0;
240 }
241 else
242 {
38182d90 243 wv = xmalloc (sizeof *wv);
f392e843
JD
244 malloc_cpt++;
245 }
246 memset (wv, 0, sizeof (widget_value));
247 return wv;
248}
249
250/* This is analogous to free. It frees only what was allocated
251 by malloc_widget_value, and no substructures. */
71bacd48 252
f392e843 253void
971de7fb 254free_widget_value (widget_value *wv)
f392e843
JD
255{
256 if (wv->free_list)
1088b922 257 emacs_abort ();
f392e843
JD
258
259 if (malloc_cpt > 25)
260 {
261 /* When the number of already allocated cells is too big,
262 We free it. */
8b146312 263 xfree (wv);
f392e843
JD
264 malloc_cpt--;
265 }
266 else
267 {
268 wv->free_list = widget_value_free_list;
269 widget_value_free_list = wv;
270 }
271}
272
f392e843 273
810f2256
JD
274/* Create and return the cursor to be used for popup menus and
275 scroll bars on display DPY. */
71bacd48 276
810f2256 277GdkCursor *
971de7fb 278xg_create_default_cursor (Display *dpy)
810f2256
JD
279{
280 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
281 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
282}
283
0afb4571 284static GdkPixbuf *
a10c8269 285xg_get_pixbuf_from_pixmap (struct frame *f, Pixmap pix)
0afb4571
J
286{
287 int iunused;
288 GdkPixbuf *tmp_buf;
289 Window wunused;
290 unsigned int width, height, uunused;
291 XImage *xim;
292
293 XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
294 &width, &height, &uunused, &uunused);
295
296 xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height,
297 ~0, XYPixmap);
298 if (!xim) return 0;
299
7c86ee98 300 tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data,
0afb4571
J
301 GDK_COLORSPACE_RGB,
302 FALSE,
303 xim->bitmap_unit,
5f8f9cc2
PE
304 width,
305 height,
0afb4571
J
306 xim->bytes_per_line,
307 NULL,
308 NULL);
309 XDestroyImage (xim);
310 return tmp_buf;
311}
312
5695f1b4
JD
313/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
314
1001243b 315static GdkPixbuf *
a10c8269 316xg_get_pixbuf_from_pix_and_mask (struct frame *f,
0afb4571
J
317 Pixmap pix,
318 Pixmap mask)
5695f1b4 319{
32e737d7 320 int width, height;
5695f1b4 321 GdkPixbuf *icon_buf, *tmp_buf;
5695f1b4 322
0afb4571 323 tmp_buf = xg_get_pixbuf_from_pixmap (f, pix);
5695f1b4
JD
324 icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
325 g_object_unref (G_OBJECT (tmp_buf));
326
0afb4571
J
327 width = gdk_pixbuf_get_width (icon_buf);
328 height = gdk_pixbuf_get_height (icon_buf);
7c86ee98 329
0afb4571 330 if (mask)
5695f1b4 331 {
0afb4571 332 GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask);
5695f1b4
JD
333 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
334 guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
335 int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
336 int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
337 int y;
338
339 for (y = 0; y < height; ++y)
340 {
341 guchar *iconptr, *maskptr;
342 int x;
343
344 iconptr = pixels + y * rowstride;
345 maskptr = mask_pixels + y * mask_rowstride;
346
347 for (x = 0; x < width; ++x)
348 {
349 /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
350 just R is sufficient. */
351 if (maskptr[0] == 0)
352 iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
353
354 iconptr += rowstride/width;
355 maskptr += mask_rowstride/width;
356 }
357 }
358
359 g_object_unref (G_OBJECT (mask_buf));
360 }
361
362 return icon_buf;
363}
364
98a92193 365static Lisp_Object
971de7fb 366file_for_image (Lisp_Object image)
98a92193
JD
367{
368 Lisp_Object specified_file = Qnil;
369 Lisp_Object tail;
98a92193
JD
370
371 for (tail = XCDR (image);
372 NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
373 tail = XCDR (XCDR (tail)))
374 if (EQ (XCAR (tail), QCfile))
375 specified_file = XCAR (XCDR (tail));
376
377 return specified_file;
378}
379
5b166323
JD
380/* For the image defined in IMG, make and return a GtkImage. For displays with
381 8 planes or less we must make a GdkPixbuf and apply the mask manually.
3ed8598c 382 Otherwise the highlighting and dimming the tool bar code in GTK does
5b166323
JD
383 will look bad. For display with more than 8 planes we just use the
384 pixmap and mask directly. For monochrome displays, GTK doesn't seem
385 able to use external pixmaps, it looks bad whatever we do.
386 The image is defined on the display where frame F is.
387 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
388 If OLD_WIDGET is NULL, a new widget is constructed and returned.
389 If OLD_WIDGET is not NULL, that widget is modified. */
71bacd48 390
5b166323 391static GtkWidget *
a10c8269 392xg_get_image_for_pixmap (struct frame *f,
e4c8d29a
J
393 struct image *img,
394 GtkWidget *widget,
395 GtkImage *old_widget)
810f2256 396{
45c94881 397 GdkPixbuf *icon_buf;
03ecb80f 398
0facd9c3 399 /* If we have a file, let GTK do all the image handling.
03ecb80f 400 This seems to be the only way to make insensitive and activated icons
0facd9c3 401 look good in all cases. */
98a92193 402 Lisp_Object specified_file = file_for_image (img->spec);
a821c035 403 Lisp_Object file;
0facd9c3 404
a821c035
JD
405 /* We already loaded the image once before calling this
406 function, so this only fails if the image file has been removed.
407 In that case, use the pixmap already loaded. */
0facd9c3 408
a821c035
JD
409 if (STRINGP (specified_file)
410 && STRINGP (file = x_find_image_file (specified_file)))
411 {
0facd9c3
JD
412 if (! old_widget)
413 old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
414 else
415 gtk_image_set_from_file (old_widget, SSDATA (file));
416
0facd9c3 417 return GTK_WIDGET (old_widget);
03ecb80f 418 }
810f2256 419
0facd9c3
JD
420 /* No file, do the image handling ourselves. This will look very bad
421 on a monochrome display, and sometimes bad on all displays with
422 certain themes. */
423
45c94881
JD
424 /* This is a workaround to make icons look good on pseudo color
425 displays. Apparently GTK expects the images to have an alpha
426 channel. If they don't, insensitive and activated icons will
427 look bad. This workaround does not work on monochrome displays,
428 and is strictly not needed on true color/static color displays (i.e.
429 16 bits and higher). But we do it anyway so we get a pixbuf that is
430 not associated with the img->pixmap. The img->pixmap may be removed
431 by clearing the image cache and then the tool bar redraw fails, since
432 Gtk+ assumes the pixmap is always there. */
0afb4571 433 icon_buf = xg_get_pixbuf_from_pix_and_mask (f, img->pixmap, img->mask);
5695f1b4 434
7c86ee98 435 if (icon_buf)
0afb4571
J
436 {
437 if (! old_widget)
438 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
439 else
440 gtk_image_set_from_pixbuf (old_widget, icon_buf);
5b166323 441
0afb4571
J
442 g_object_unref (G_OBJECT (icon_buf));
443 }
74cdfe05 444
5b166323 445 return GTK_WIDGET (old_widget);
810f2256
JD
446}
447
448
449/* Set CURSOR on W and all widgets W contain. We must do like this
450 for scroll bars and menu because they create widgets internally,
451 and it is those widgets that are visible. */
71bacd48 452
810f2256 453static void
971de7fb 454xg_set_cursor (GtkWidget *w, GdkCursor *cursor)
f392e843 455{
5e617bc2 456 GdkWindow *window = gtk_widget_get_window (w);
e547b051 457 GList *children = gdk_window_peek_children (window);
f392e843 458
e547b051 459 gdk_window_set_cursor (window, cursor);
f392e843
JD
460
461 /* The scroll bar widget has more than one GDK window (had to look at
462 the source to figure this out), and there is no way to set cursor
463 on widgets in GTK. So we must set the cursor for all GDK windows.
464 Ditto for menus. */
465
466 for ( ; children; children = g_list_next (children))
810f2256 467 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
f392e843
JD
468}
469
f392e843 470/* Insert NODE into linked LIST. */
71bacd48 471
f392e843
JD
472static void
473xg_list_insert (xg_list_node *list, xg_list_node *node)
474{
475 xg_list_node *list_start = list->next;
177c0ea7 476
f392e843
JD
477 if (list_start) list_start->prev = node;
478 node->next = list_start;
479 node->prev = 0;
480 list->next = node;
481}
482
483/* Remove NODE from linked LIST. */
71bacd48 484
f392e843
JD
485static void
486xg_list_remove (xg_list_node *list, xg_list_node *node)
487{
488 xg_list_node *list_start = list->next;
489 if (node == list_start)
490 {
491 list->next = node->next;
492 if (list->next) list->next->prev = 0;
493 }
494 else
495 {
496 node->prev->next = node->next;
497 if (node->next) node->next->prev = node->prev;
498 }
499}
500
501/* Allocate and return a utf8 version of STR. If STR is already
42ca4633
J
502 utf8 or NULL, just return a copy of STR.
503 A new string is allocated and the caller must free the result
f392e843 504 with g_free. */
71bacd48 505
f392e843 506static char *
42ca4633 507get_utf8_string (const char *str)
f392e843 508{
42ca4633 509 char *utf8_str;
177c0ea7 510
241ad3ca
JD
511 if (!str) return NULL;
512
f392e843 513 /* If not UTF-8, try current locale. */
241ad3ca 514 if (!g_utf8_validate (str, -1, NULL))
f392e843 515 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
42ca4633
J
516 else
517 return g_strdup (str);
f392e843 518
8e5a8840 519 if (!utf8_str)
241ad3ca
JD
520 {
521 /* Probably some control characters in str. Escape them. */
0eb0f318
PE
522 ptrdiff_t len;
523 ptrdiff_t nr_bad = 0;
241ad3ca
JD
524 gsize bytes_read;
525 gsize bytes_written;
526 unsigned char *p = (unsigned char *)str;
527 char *cp, *up;
65dc836c 528 GError *err = NULL;
241ad3ca 529
b43da352 530 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
65dc836c
PE
531 &bytes_written, &err))
532 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
241ad3ca
JD
533 {
534 ++nr_bad;
535 p += bytes_written+1;
65dc836c
PE
536 g_error_free (err);
537 err = NULL;
241ad3ca
JD
538 }
539
65dc836c 540 if (err)
241ad3ca 541 {
65dc836c
PE
542 g_error_free (err);
543 err = NULL;
241ad3ca
JD
544 }
545 if (cp) g_free (cp);
546
0eb0f318 547 len = strlen (str);
7216e43b 548 if ((min (PTRDIFF_MAX, SIZE_MAX) - len - 1) / 4 < nr_bad)
0eb0f318
PE
549 memory_full (SIZE_MAX);
550 up = utf8_str = xmalloc (len + nr_bad * 4 + 1);
b43da352 551 p = (unsigned char *)str;
241ad3ca 552
b43da352 553 while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
65dc836c
PE
554 &bytes_written, &err))
555 && err->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
241ad3ca 556 {
e99a530f 557 memcpy (up, p, bytes_written);
241ad3ca 558 sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
241ad3ca
JD
559 up += bytes_written+4;
560 p += bytes_written+1;
65dc836c
PE
561 g_error_free (err);
562 err = NULL;
241ad3ca
JD
563 }
564
8e5a8840 565 if (cp)
241ad3ca
JD
566 {
567 strcat (utf8_str, cp);
568 g_free (cp);
569 }
65dc836c 570 if (err)
241ad3ca 571 {
65dc836c
PE
572 g_error_free (err);
573 err = NULL;
241ad3ca
JD
574 }
575 }
f392e843
JD
576 return utf8_str;
577}
578
3a46642b
J
579/* Check for special colors used in face spec for region face.
580 The colors are fetched from the Gtk+ theme.
18e27ea8 581 Return true if color was found, false if not. */
3a46642b 582
18e27ea8 583bool
3a46642b
J
584xg_check_special_colors (struct frame *f,
585 const char *color_name,
586 XColor *color)
587{
18e27ea8
PE
588 bool success_p = 0;
589 bool get_bg = strcmp ("gtk_selection_bg_color", color_name) == 0;
590 bool get_fg = !get_bg && strcmp ("gtk_selection_fg_color", color_name) == 0;
0afb4571
J
591
592 if (! FRAME_GTK_WIDGET (f) || ! (get_bg || get_fg))
593 return success_p;
594
4d7e6e51 595 block_input ();
0afb4571
J
596 {
597#ifdef HAVE_GTK3
598 GtkStyleContext *gsty
599 = gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
600 GdkRGBA col;
b24ac90f 601 char buf[sizeof "rgb://rrrr/gggg/bbbb"];
0afb4571
J
602 int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
603 if (get_fg)
604 gtk_style_context_get_color (gsty, state, &col);
605 else
606 gtk_style_context_get_background_color (gsty, state, &col);
607
b24ac90f
JD
608 sprintf (buf, "rgb:%04x/%04x/%04x",
609 (int)(col.red * 65535),
610 (int)(col.green * 65535),
611 (int)(col.blue * 65535));
18e27ea8
PE
612 success_p = (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
613 buf, color)
614 != 0);
0afb4571
J
615#else
616 GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
617 GdkColor *grgb = get_bg
618 ? &gsty->bg[GTK_STATE_SELECTED]
619 : &gsty->fg[GTK_STATE_SELECTED];
620
621 color->red = grgb->red;
622 color->green = grgb->green;
623 color->blue = grgb->blue;
624 color->pixel = grgb->pixel;
625 success_p = 1;
626#endif
3a46642b 627
0afb4571 628 }
4d7e6e51 629 unblock_input ();
3a46642b
J
630 return success_p;
631}
632
f392e843
JD
633
634\f
aa1859f5
J
635/***********************************************************************
636 Tooltips
637 ***********************************************************************/
638/* Gtk+ calls this callback when the parent of our tooltip dummy changes.
639 We use that to pop down the tooltip. This happens if Gtk+ for some
640 reason wants to change or hide the tooltip. */
641
ac01763e
JD
642#ifdef USE_GTK_TOOLTIP
643
aa1859f5
J
644static void
645hierarchy_ch_cb (GtkWidget *widget,
646 GtkWidget *previous_toplevel,
647 gpointer user_data)
648{
7d652d97 649 struct frame *f = user_data;
aa1859f5
J
650 struct x_output *x = f->output_data.x;
651 GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
088c8c09 652
aa1859f5
J
653 if (! top || ! GTK_IS_WINDOW (top))
654 gtk_widget_hide (previous_toplevel);
655}
656
657/* Callback called when Gtk+ thinks a tooltip should be displayed.
658 We use it to get the tooltip window and the tooltip widget so
659 we can manipulate the ourselves.
660
661 Return FALSE ensures that the tooltip is not shown. */
662
663static gboolean
664qttip_cb (GtkWidget *widget,
665 gint xpos,
666 gint ypos,
667 gboolean keyboard_mode,
668 GtkTooltip *tooltip,
669 gpointer user_data)
670{
7d652d97 671 struct frame *f = user_data;
aa1859f5 672 struct x_output *x = f->output_data.x;
088c8c09 673 if (x->ttip_widget == NULL)
aa1859f5 674 {
1b854618
JD
675 GtkWidget *p;
676 GList *list, *iter;
677
aa1859f5
J
678 g_object_set (G_OBJECT (widget), "has-tooltip", FALSE, NULL);
679 x->ttip_widget = tooltip;
680 g_object_ref (G_OBJECT (tooltip));
681 x->ttip_lbl = gtk_label_new ("");
682 g_object_ref (G_OBJECT (x->ttip_lbl));
683 gtk_tooltip_set_custom (tooltip, x->ttip_lbl);
684 x->ttip_window = GTK_WINDOW (gtk_widget_get_toplevel (x->ttip_lbl));
1b854618
JD
685
686 /* Change stupid Gtk+ default line wrapping. */
687 p = gtk_widget_get_parent (x->ttip_lbl);
688 list = gtk_container_get_children (GTK_CONTAINER (p));
1b854618
JD
689 for (iter = list; iter; iter = g_list_next (iter))
690 {
691 GtkWidget *w = GTK_WIDGET (iter->data);
692 if (GTK_IS_LABEL (w))
693 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
694 }
695 g_list_free (list);
696
8daaeda6
J
697 /* ATK needs an empty title for some reason. */
698 gtk_window_set_title (x->ttip_window, "");
aa1859f5
J
699 /* Realize so we can safely get screen later on. */
700 gtk_widget_realize (GTK_WIDGET (x->ttip_window));
701 gtk_widget_realize (x->ttip_lbl);
702
703 g_signal_connect (x->ttip_lbl, "hierarchy-changed",
704 G_CALLBACK (hierarchy_ch_cb), f);
705 }
706 return FALSE;
707}
708
ac01763e
JD
709#endif /* USE_GTK_TOOLTIP */
710
aa1859f5 711/* Prepare a tooltip to be shown, i.e. calculate WIDTH and HEIGHT.
18e27ea8 712 Return true if a system tooltip is available. */
aa1859f5 713
18e27ea8 714bool
a10c8269 715xg_prepare_tooltip (struct frame *f,
0ce7e563
JD
716 Lisp_Object string,
717 int *width,
aa1859f5
J
718 int *height)
719{
ac01763e
JD
720#ifndef USE_GTK_TOOLTIP
721 return 0;
722#else
aa1859f5
J
723 struct x_output *x = f->output_data.x;
724 GtkWidget *widget;
725 GdkWindow *gwin;
726 GdkScreen *screen;
727 GtkSettings *settings;
728 gboolean tt_enabled = TRUE;
729 GtkRequisition req;
730 Lisp_Object encoded_string;
731
732 if (!x->ttip_lbl) return 0;
733
4d7e6e51 734 block_input ();
aa1859f5
J
735 encoded_string = ENCODE_UTF_8 (string);
736 widget = GTK_WIDGET (x->ttip_lbl);
737 gwin = gtk_widget_get_window (GTK_WIDGET (x->ttip_window));
0afb4571 738 screen = gdk_window_get_screen (gwin);
aa1859f5
J
739 settings = gtk_settings_get_for_screen (screen);
740 g_object_get (settings, "gtk-enable-tooltips", &tt_enabled, NULL);
088c8c09 741 if (tt_enabled)
aa1859f5
J
742 {
743 g_object_set (settings, "gtk-enable-tooltips", FALSE, NULL);
744 /* Record that we disabled it so it can be enabled again. */
745 g_object_set_data (G_OBJECT (x->ttip_window), "restore-tt",
746 (gpointer)f);
747 }
088c8c09 748
aa1859f5
J
749 /* Prevent Gtk+ from hiding tooltip on mouse move and such. */
750 g_object_set_data (G_OBJECT
751 (gtk_widget_get_display (GTK_WIDGET (x->ttip_window))),
752 "gdk-display-current-tooltip", NULL);
753
0ce7e563 754 /* Put our dummy widget in so we can get callbacks for unrealize and
aa1859f5
J
755 hierarchy-changed. */
756 gtk_tooltip_set_custom (x->ttip_widget, widget);
1b854618 757 gtk_tooltip_set_text (x->ttip_widget, SSDATA (encoded_string));
0afb4571 758 gtk_widget_get_preferred_size (GTK_WIDGET (x->ttip_window), NULL, &req);
aa1859f5
J
759 if (width) *width = req.width;
760 if (height) *height = req.height;
088c8c09 761
4d7e6e51 762 unblock_input ();
aa1859f5
J
763
764 return 1;
ac01763e 765#endif /* USE_GTK_TOOLTIP */
aa1859f5
J
766}
767
768/* Show the tooltip at ROOT_X and ROOT_Y.
769 xg_prepare_tooltip must have been called before this function. */
770
771void
a10c8269 772xg_show_tooltip (struct frame *f, int root_x, int root_y)
aa1859f5 773{
ac01763e 774#ifdef USE_GTK_TOOLTIP
aa1859f5
J
775 struct x_output *x = f->output_data.x;
776 if (x->ttip_window)
777 {
4d7e6e51 778 block_input ();
aa1859f5
J
779 gtk_window_move (x->ttip_window, root_x, root_y);
780 gtk_widget_show_all (GTK_WIDGET (x->ttip_window));
4d7e6e51 781 unblock_input ();
aa1859f5 782 }
ac01763e 783#endif
aa1859f5
J
784}
785
786/* Hide tooltip if shown. Do nothing if not shown.
18e27ea8 787 Return true if tip was hidden, false if not (i.e. not using
aa1859f5
J
788 system tooltips). */
789
18e27ea8 790bool
a10c8269 791xg_hide_tooltip (struct frame *f)
aa1859f5 792{
18e27ea8 793 bool ret = 0;
ac01763e 794#ifdef USE_GTK_TOOLTIP
aa1859f5
J
795 if (f->output_data.x->ttip_window)
796 {
797 GtkWindow *win = f->output_data.x->ttip_window;
4d7e6e51 798 block_input ();
aa1859f5
J
799 gtk_widget_hide (GTK_WIDGET (win));
800
801 if (g_object_get_data (G_OBJECT (win), "restore-tt"))
802 {
803 GdkWindow *gwin = gtk_widget_get_window (GTK_WIDGET (win));
0afb4571 804 GdkScreen *screen = gdk_window_get_screen (gwin);
aa1859f5
J
805 GtkSettings *settings = gtk_settings_get_for_screen (screen);
806 g_object_set (settings, "gtk-enable-tooltips", TRUE, NULL);
807 }
4d7e6e51 808 unblock_input ();
aa1859f5
J
809
810 ret = 1;
811 }
ac01763e 812#endif
aa1859f5
J
813 return ret;
814}
815
816\f
f392e843
JD
817/***********************************************************************
818 General functions for creating widgets, resizing, events, e.t.c.
819 ***********************************************************************/
820
005c8d13
JD
821static void
822my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
823 const gchar *msg, gpointer user_data)
824{
825 if (!strstr (msg, "visible children"))
826 fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
827}
828
f392e843
JD
829/* Make a geometry string and pass that to GTK. It seems this is the
830 only way to get geometry position right if the user explicitly
831 asked for a position when starting Emacs.
832 F is the frame we shall set geometry for. */
71bacd48 833
f392e843 834static void
a10c8269 835xg_set_geometry (struct frame *f)
f392e843 836{
92848133 837 if (f->size_hint_flags & (USPosition | PPosition))
1c9c1270
JD
838 {
839 int left = f->left_pos;
840 int xneg = f->size_hint_flags & XNegative;
841 int top = f->top_pos;
842 int yneg = f->size_hint_flags & YNegative;
84722b3d 843 char geom_str[sizeof "=x--" + 4 * INT_STRLEN_BOUND (int)];
005c8d13 844 guint id;
1c9c1270
JD
845
846 if (xneg)
847 left = -left;
848 if (yneg)
849 top = -top;
850
22aa44a8
JD
851 sprintf (geom_str, "=%dx%d%c%d%c%d",
852 FRAME_PIXEL_WIDTH (f),
853 FRAME_PIXEL_HEIGHT (f),
1c9c1270
JD
854 (xneg ? '-' : '+'), left,
855 (yneg ? '-' : '+'), top);
856
005c8d13
JD
857 /* Silence warning about visible children. */
858 id = g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
859 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
860
1c9c1270
JD
861 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
862 geom_str))
863 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
005c8d13
JD
864
865 g_log_remove_handler ("Gtk", id);
1c9c1270 866 }
f392e843
JD
867}
868
7c583cd8
JD
869/* Clear under internal border if any. As we use a mix of Gtk+ and X calls
870 and use a GtkFixed widget, this doesn't happen automatically. */
871
872static void
a10c8269 873xg_clear_under_internal_border (struct frame *f)
7c583cd8
JD
874{
875 if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
876 {
877 GtkWidget *wfixed = f->output_data.x->edit_widget;
1f5cf200 878
7c583cd8
JD
879 gtk_widget_queue_draw (wfixed);
880 gdk_window_process_all_updates ();
1f5cf200
DA
881
882 x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
883 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
884
885 x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0, 0,
886 FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
887
888 x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), 0,
889 FRAME_PIXEL_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
890 FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));
891
892 x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
893 FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f),
894 0, FRAME_INTERNAL_BORDER_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
7c583cd8
JD
895 }
896}
897
1e39cbfb
JD
898/* Function to handle resize of our frame. As we have a Gtk+ tool bar
899 and a Gtk+ menu bar, we get resize events for the edit part of the
900 frame only. We let Gtk+ deal with the Gtk+ parts.
f392e843
JD
901 F is the frame to resize.
902 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
71bacd48 903
f392e843 904void
a10c8269 905xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
f392e843 906{
880e6158 907 int width, height;
7b507248
JD
908
909 if (pixelwidth == -1 && pixelheight == -1)
910 {
e547b051
J
911 if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
912 gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
913 0, 0,
0afb4571 914 &pixelwidth, &pixelheight);
7b507248
JD
915 else return;
916 }
088c8c09 917
7b507248 918
880e6158
MR
919 width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
920 height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
7b507248 921
880e6158
MR
922 if (width != FRAME_TEXT_WIDTH (f)
923 || height != FRAME_TEXT_HEIGHT (f)
7b507248
JD
924 || pixelwidth != FRAME_PIXEL_WIDTH (f)
925 || pixelheight != FRAME_PIXEL_HEIGHT (f))
f392e843 926 {
1e39cbfb
JD
927 FRAME_PIXEL_WIDTH (f) = pixelwidth;
928 FRAME_PIXEL_HEIGHT (f) = pixelheight;
f392e843 929
7c583cd8 930 xg_clear_under_internal_border (f);
880e6158 931 change_frame_size (f, width, height, 0, 1, 0, 1);
5a130941
JD
932 SET_FRAME_GARBAGED (f);
933 cancel_mouse_face (f);
1e39cbfb
JD
934 }
935}
f392e843 936
da6062e6 937/* Resize the outer window of frame F after changing the height.
1c9c1270 938 COLUMNS/ROWS is the size the edit area shall have after the resize. */
71bacd48 939
f392e843 940void
880e6158 941xg_frame_set_char_size (struct frame *f, int width, int height)
f392e843 942{
7303a0ae 943 int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
880e6158 944 int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
177c0ea7 945
1e39cbfb
JD
946 if (FRAME_PIXEL_HEIGHT (f) == 0)
947 return;
948
7c583cd8
JD
949 /* Do this before resize, as we don't know yet if we will be resized. */
950 xg_clear_under_internal_border (f);
951
f392e843 952 /* Must resize our top level widget. Font size may have changed,
3f1c6666 953 but not rows/cols. */
f392e843
JD
954 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
955 pixelwidth, pixelheight);
3f1c6666 956 x_wm_set_size_hint (f, 0, 0);
47a6002f 957
5c646d5a
JD
958 SET_FRAME_GARBAGED (f);
959 cancel_mouse_face (f);
960
7b507248
JD
961 /* We can not call change_frame_size for a mapped frame,
962 we can not set pixel width/height either. The window manager may
963 override our resize request, XMonad does this all the time.
964 The best we can do is try to sync, so lisp code sees the updated
965 size as fast as possible.
966 For unmapped windows, we can set rows/cols. When
967 the frame is mapped again we will (hopefully) get the correct size. */
edfa7fa0 968 if (FRAME_VISIBLE_P (f))
5c646d5a
JD
969 {
970 /* Must call this to flush out events */
971 (void)gtk_events_pending ();
972 gdk_flush ();
973 x_wait_for_event (f, ConfigureNotify);
974 }
7b507248 975 else
7303a0ae 976 change_frame_size (f, width, height, 0, 1, 0, 1);
3f1c6666
JD
977}
978
bfeabdc3 979/* Handle height/width changes (i.e. add/remove/move menu/toolbar).
3f1c6666
JD
980 The policy is to keep the number of editable lines. */
981
982static void
a10c8269 983xg_height_or_width_changed (struct frame *f)
3f1c6666
JD
984{
985 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
bfeabdc3
JD
986 FRAME_TOTAL_PIXEL_WIDTH (f),
987 FRAME_TOTAL_PIXEL_HEIGHT (f));
3f1c6666 988 f->output_data.x->hint_flags = 0;
f26fab36 989 x_wm_set_size_hint (f, 0, 0);
f392e843
JD
990}
991
810f2256 992/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
f392e843
JD
993 Must be done like this, because GtkWidget:s can have "hidden"
994 X Window that aren't accessible.
995
996 Return 0 if no widget match WDESC. */
71bacd48 997
f392e843 998GtkWidget *
971de7fb 999xg_win_to_widget (Display *dpy, Window wdesc)
f392e843
JD
1000{
1001 gpointer gdkwin;
1002 GtkWidget *gwdesc = 0;
1003
4d7e6e51 1004 block_input ();
810f2256 1005
0afb4571
J
1006 gdkwin = gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
1007 wdesc);
f392e843
JD
1008 if (gdkwin)
1009 {
1010 GdkEvent event;
1011 event.any.window = gdkwin;
3b574623 1012 event.any.type = GDK_NOTHING;
f392e843
JD
1013 gwdesc = gtk_get_event_widget (&event);
1014 }
177c0ea7 1015
4d7e6e51 1016 unblock_input ();
f392e843
JD
1017 return gwdesc;
1018}
1019
0afb4571 1020/* Set the background of widget W to PIXEL. */
71bacd48 1021
f392e843 1022static void
2d9783e0 1023xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
f392e843 1024{
0afb4571
J
1025#ifdef HAVE_GTK3
1026 GdkRGBA bg;
1027 XColor xbg;
1028 xbg.pixel = pixel;
1029 if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
1030 {
537f336d
JD
1031 bg.red = (double)xbg.red/65535.0;
1032 bg.green = (double)xbg.green/65535.0;
1033 bg.blue = (double)xbg.blue/65535.0;
0afb4571
J
1034 bg.alpha = 1.0;
1035 gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
1036 }
1037#else
1038 GdkColor bg;
f392e843 1039 GdkColormap *map = gtk_widget_get_colormap (w);
0afb4571
J
1040 gdk_colormap_query_color (map, pixel, &bg);
1041 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &bg);
1042#endif
f392e843
JD
1043}
1044
3a46642b
J
1045/* Callback called when the gtk theme changes.
1046 We notify lisp code so it can fix faces used for region for example. */
1047
1048static void
1049style_changed_cb (GObject *go,
1050 GParamSpec *spec,
1051 gpointer user_data)
1052{
1053 struct input_event event;
7d652d97 1054 GdkDisplay *gdpy = user_data;
3a46642b 1055 const char *display_name = gdk_display_get_name (gdpy);
c195f2de 1056 Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
3a46642b
J
1057
1058 EVENT_INIT (event);
1059 event.kind = CONFIG_CHANGED_EVENT;
9bda3520 1060 event.frame_or_window = build_string (display_name);
3a46642b
J
1061 /* Theme doesn't change often, so intern is called seldom. */
1062 event.arg = intern ("theme-name");
1063 kbd_buffer_store_event (&event);
c195f2de
JD
1064
1065 update_theme_scrollbar_width ();
1066
1067 /* If scroll bar width changed, we need set the new size on all frames
1068 on this display. */
9bda3520 1069 if (dpy)
c195f2de
JD
1070 {
1071 Lisp_Object rest, frame;
1072 FOR_EACH_FRAME (rest, frame)
1073 {
a10c8269 1074 struct frame *f = XFRAME (frame);
54e95010
JD
1075 if (FRAME_LIVE_P (f)
1076 && FRAME_X_P (f)
1077 && FRAME_X_DISPLAY (f) == dpy)
c195f2de
JD
1078 {
1079 x_set_scroll_bar_default_width (f);
7303a0ae 1080 xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
c195f2de
JD
1081 }
1082 }
1083 }
3a46642b
J
1084}
1085
0afb4571
J
1086/* Called when a delete-event occurs on WIDGET. */
1087
1088static gboolean
1089delete_cb (GtkWidget *widget,
1090 GdkEvent *event,
1091 gpointer user_data)
1092{
1093#ifdef HAVE_GTK3
1094 /* The event doesn't arrive in the normal event loop. Send event
1095 here. */
7d652d97 1096 struct frame *f = user_data;
0afb4571
J
1097 struct input_event ie;
1098
1099 EVENT_INIT (ie);
1100 ie.kind = DELETE_WINDOW_EVENT;
1101 XSETFRAME (ie.frame_or_window, f);
1102 kbd_buffer_store_event (&ie);
1103#endif
1104
1105 return TRUE;
1106}
1107
f392e843 1108/* Create and set up the GTK widgets for frame F.
18e27ea8 1109 Return true if creation succeeded. */
71bacd48 1110
18e27ea8 1111bool
a10c8269 1112xg_create_frame_widgets (struct frame *f)
f392e843
JD
1113{
1114 GtkWidget *wtop;
bfeabdc3 1115 GtkWidget *wvbox, *whbox;
f392e843 1116 GtkWidget *wfixed;
1068fe4d 1117#ifndef HAVE_GTK3
f392e843 1118 GtkRcStyle *style;
1068fe4d 1119#endif
f392e843 1120 char *title = 0;
177c0ea7 1121
4d7e6e51 1122 block_input ();
f392e843 1123
0f0d223b 1124 if (FRAME_X_EMBEDDED_P (f))
383b7c95
JD
1125 {
1126 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
1127 wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
1128 }
0f0d223b
JD
1129 else
1130 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1131
54e9e3bf
JD
1132 /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
1133 has backported it to Gtk+ 2.0 and they add the resize grip for
1134 Gtk+ 2.0 applications also. But it has a bug that makes Emacs loop
1135 forever, so disable the grip. */
7583e2a0
PE
1136#if (! GTK_CHECK_VERSION (3, 0, 0) \
1137 && defined HAVE_GTK_WINDOW_SET_HAS_RESIZE_GRIP)
54e9e3bf
JD
1138 gtk_window_set_has_resize_grip (GTK_WINDOW (wtop), FALSE);
1139#endif
1140
810f2256
JD
1141 xg_set_screen (wtop, f);
1142
383b7c95
JD
1143 wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1144 whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1145 gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
1146 gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
c195f2de
JD
1147
1148#ifdef HAVE_GTK3
c7e73be5 1149 wfixed = emacs_fixed_new (f);
c195f2de
JD
1150#else
1151 wfixed = gtk_fixed_new ();
1152#endif
177c0ea7 1153
bfeabdc3 1154 if (! wtop || ! wvbox || ! whbox || ! wfixed)
f392e843
JD
1155 {
1156 if (wtop) gtk_widget_destroy (wtop);
1157 if (wvbox) gtk_widget_destroy (wvbox);
bfeabdc3 1158 if (whbox) gtk_widget_destroy (whbox);
f392e843
JD
1159 if (wfixed) gtk_widget_destroy (wfixed);
1160
4d7e6e51 1161 unblock_input ();
f392e843
JD
1162 return 0;
1163 }
1164
1165 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
1166 gtk_widget_set_name (wtop, EMACS_CLASS);
1167 gtk_widget_set_name (wvbox, "pane");
93c579e0 1168 gtk_widget_set_name (wfixed, SSDATA (Vx_resource_name));
f392e843
JD
1169
1170 /* If this frame has a title or name, set it in the title bar. */
e69b0960
DA
1171 if (! NILP (f->title))
1172 title = SSDATA (ENCODE_UTF_8 (f->title));
1173 else if (! NILP (f->name))
1174 title = SSDATA (ENCODE_UTF_8 (f->name));
f392e843
JD
1175
1176 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
177c0ea7 1177
f392e843
JD
1178 FRAME_GTK_OUTER_WIDGET (f) = wtop;
1179 FRAME_GTK_WIDGET (f) = wfixed;
1180 f->output_data.x->vbox_widget = wvbox;
bfeabdc3 1181 f->output_data.x->hbox_widget = whbox;
f392e843 1182
e547b051 1183 gtk_widget_set_has_window (wfixed, TRUE);
f392e843 1184
f392e843 1185 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
bfeabdc3
JD
1186 gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
1187 gtk_box_pack_start (GTK_BOX (whbox), wfixed, TRUE, TRUE, 0);
f392e843
JD
1188
1189 if (FRAME_EXTERNAL_TOOL_BAR (f))
1190 update_frame_tool_bar (f);
1191
7863d625
JD
1192 /* We don't want this widget double buffered, because we draw on it
1193 with regular X drawing primitives, so from a GTK/GDK point of
1194 view, the widget is totally blank. When an expose comes, this
1195 will make the widget blank, and then Emacs redraws it. This flickers
1196 a lot, so we turn off double buffering. */
f392e843 1197 gtk_widget_set_double_buffered (wfixed, FALSE);
7863d625 1198
f392e843 1199 gtk_window_set_wmclass (GTK_WINDOW (wtop),
93c579e0
JD
1200 SSDATA (Vx_resource_name),
1201 SSDATA (Vx_resource_class));
f392e843
JD
1202
1203 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
1204 GTK is to destroy the widget. We want Emacs to do that instead. */
1205 g_signal_connect (G_OBJECT (wtop), "delete-event",
0afb4571 1206 G_CALLBACK (delete_cb), f);
177c0ea7 1207
f392e843
JD
1208 /* Convert our geometry parameters into a geometry string
1209 and specify it.
1210 GTK will itself handle calculating the real position this way. */
1211 xg_set_geometry (f);
32e737d7
JD
1212 f->win_gravity
1213 = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
f392e843
JD
1214
1215 gtk_widget_add_events (wfixed,
1216 GDK_POINTER_MOTION_MASK
1217 | GDK_EXPOSURE_MASK
1218 | GDK_BUTTON_PRESS_MASK
1219 | GDK_BUTTON_RELEASE_MASK
1220 | GDK_KEY_PRESS_MASK
1221 | GDK_ENTER_NOTIFY_MASK
1222 | GDK_LEAVE_NOTIFY_MASK
1223 | GDK_FOCUS_CHANGE_MASK
1224 | GDK_STRUCTURE_MASK
1225 | GDK_VISIBILITY_NOTIFY_MASK);
1226
1227 /* Must realize the windows so the X window gets created. It is used
1228 by callers of this function. */
1229 gtk_widget_realize (wfixed);
1230 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
1231
1232 /* Since GTK clears its window by filling with the background color,
1233 we must keep X and GTK background in sync. */
0afb4571 1234 xg_set_widget_bg (f, wfixed, FRAME_BACKGROUND_PIXEL (f));
f392e843 1235
0afb4571 1236#ifndef HAVE_GTK3
f392e843
JD
1237 /* Also, do not let any background pixmap to be set, this looks very
1238 bad as Emacs overwrites the background pixmap with its own idea
1239 of background color. */
1240 style = gtk_widget_get_modifier_style (wfixed);
1241
1242 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
1243 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
1244 gtk_widget_modify_style (wfixed, style);
0afb4571
J
1245#else
1246 gtk_widget_set_can_focus (wfixed, TRUE);
c195f2de 1247 gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
0afb4571 1248#endif
177c0ea7 1249
ac01763e 1250#ifdef USE_GTK_TOOLTIP
aa1859f5
J
1251 /* Steal a tool tip window we can move ourselves. */
1252 f->output_data.x->ttip_widget = 0;
1253 f->output_data.x->ttip_lbl = 0;
1254 f->output_data.x->ttip_window = 0;
088c8c09 1255 gtk_widget_set_tooltip_text (wtop, "Dummy text");
aa1859f5 1256 g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
ac01763e 1257#endif
f392e843 1258
3a46642b
J
1259 {
1260 GdkScreen *screen = gtk_widget_get_screen (wtop);
1261 GtkSettings *gs = gtk_settings_get_for_screen (screen);
1262 /* Only connect this signal once per screen. */
1263 if (! g_signal_handler_find (G_OBJECT (gs),
1264 G_SIGNAL_MATCH_FUNC,
1265 0, 0, 0,
1266 G_CALLBACK (style_changed_cb),
1267 0))
1268 {
1269 g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
1270 G_CALLBACK (style_changed_cb),
1271 gdk_screen_get_display (screen));
1272 }
1273 }
1274
4d7e6e51 1275 unblock_input ();
f392e843
JD
1276
1277 return 1;
1278}
1279
aa1859f5 1280void
a10c8269 1281xg_free_frame_widgets (struct frame *f)
aa1859f5
J
1282{
1283 if (FRAME_GTK_OUTER_WIDGET (f))
1284 {
b0afc268 1285#ifdef USE_GTK_TOOLTIP
aa1859f5 1286 struct x_output *x = f->output_data.x;
b0afc268 1287#endif
ca06f160
JD
1288 struct xg_frame_tb_info *tbinfo
1289 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
1290 TB_INFO_KEY);
1291 if (tbinfo)
1292 xfree (tbinfo);
1293
aa1859f5
J
1294 gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
1295 FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
1296 FRAME_GTK_OUTER_WIDGET (f) = 0;
ac01763e 1297#ifdef USE_GTK_TOOLTIP
aa1859f5
J
1298 if (x->ttip_lbl)
1299 gtk_widget_destroy (x->ttip_lbl);
1300 if (x->ttip_widget)
1301 g_object_unref (G_OBJECT (x->ttip_widget));
ac01763e 1302#endif
aa1859f5
J
1303 }
1304}
1305
f392e843
JD
1306/* Set the normal size hints for the window manager, for frame F.
1307 FLAGS is the flags word to use--or 0 meaning preserve the flags
1308 that the window now has.
18e27ea8 1309 If USER_POSITION, set the User Position
f392e843 1310 flag (this is useful when FLAGS is 0). */
71bacd48 1311
f392e843 1312void
a10c8269 1313x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
f392e843 1314{
3e5fc571
CY
1315 /* Must use GTK routines here, otherwise GTK resets the size hints
1316 to its own defaults. */
1317 GdkGeometry size_hints;
1318 gint hint_flags = 0;
1319 int base_width, base_height;
1320 int min_rows = 0, min_cols = 0;
1321 int win_gravity = f->win_gravity;
6e1b469e 1322 Lisp_Object fs_state, frame;
3e5fc571 1323
32e737d7
JD
1324 /* Don't set size hints during initialization; that apparently leads
1325 to a race condition. See the thread at
1326 http://lists.gnu.org/archive/html/emacs-devel/2008-10/msg00033.html */
1327 if (NILP (Vafter_init_time) || !FRAME_GTK_OUTER_WIDGET (f))
1328 return;
1329
6e1b469e
JD
1330 XSETFRAME (frame, f);
1331 fs_state = Fframe_parameter (frame, Qfullscreen);
1332 if (EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth))
1333 {
1334 /* Don't set hints when maximized or fullscreen. Apparently KWin and
1335 Gtk3 don't get along and the frame shrinks (!).
1336 */
1337 return;
1338 }
1339
3e5fc571
CY
1340 if (flags)
1341 {
1342 memset (&size_hints, 0, sizeof (size_hints));
1343 f->output_data.x->size_hints = size_hints;
1344 f->output_data.x->hint_flags = hint_flags;
1345 }
1346 else
1347 flags = f->size_hint_flags;
1348
1349 size_hints = f->output_data.x->size_hints;
1350 hint_flags = f->output_data.x->hint_flags;
1351
1352 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
1353 size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
1354 size_hints.height_inc = FRAME_LINE_HEIGHT (f);
1355
1356 hint_flags |= GDK_HINT_BASE_SIZE;
b4444c8a 1357 /* Use one row/col here so base_height/width does not become zero.
89c94350 1358 Gtk+ and/or Unity on Ubuntu 12.04 can't handle it. */
b4444c8a 1359 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
89c94350 1360 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
3e5fc571
CY
1361 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
1362
880e6158 1363 check_frame_size (f, &min_cols, &min_rows, 0);
b4444c8a 1364 if (min_cols > 0) --min_cols; /* We used one col in base_width = ... 1); */
89c94350 1365 if (min_rows > 0) --min_rows; /* We used one row in base_height = ... 1); */
3e5fc571
CY
1366
1367 size_hints.base_width = base_width;
1368 size_hints.base_height = base_height;
1369 size_hints.min_width = base_width + min_cols * size_hints.width_inc;
1370 size_hints.min_height = base_height + min_rows * size_hints.height_inc;
1371
1372 /* These currently have a one to one mapping with the X values, but I
1373 don't think we should rely on that. */
1374 hint_flags |= GDK_HINT_WIN_GRAVITY;
1375 size_hints.win_gravity = 0;
1376 if (win_gravity == NorthWestGravity)
1377 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
1378 else if (win_gravity == NorthGravity)
1379 size_hints.win_gravity = GDK_GRAVITY_NORTH;
1380 else if (win_gravity == NorthEastGravity)
1381 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
1382 else if (win_gravity == WestGravity)
1383 size_hints.win_gravity = GDK_GRAVITY_WEST;
1384 else if (win_gravity == CenterGravity)
1385 size_hints.win_gravity = GDK_GRAVITY_CENTER;
1386 else if (win_gravity == EastGravity)
1387 size_hints.win_gravity = GDK_GRAVITY_EAST;
1388 else if (win_gravity == SouthWestGravity)
1389 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
1390 else if (win_gravity == SouthGravity)
1391 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
1392 else if (win_gravity == SouthEastGravity)
1393 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
1394 else if (win_gravity == StaticGravity)
1395 size_hints.win_gravity = GDK_GRAVITY_STATIC;
1396
22aa44a8
JD
1397 if (user_position)
1398 {
1399 hint_flags &= ~GDK_HINT_POS;
1400 hint_flags |= GDK_HINT_USER_POS;
1401 }
1402
3e5fc571
CY
1403 if (hint_flags != f->output_data.x->hint_flags
1404 || memcmp (&size_hints,
1405 &f->output_data.x->size_hints,
1406 sizeof (size_hints)) != 0)
1407 {
4d7e6e51 1408 block_input ();
3e5fc571 1409 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
c7e73be5 1410 NULL, &size_hints, hint_flags);
3e5fc571
CY
1411 f->output_data.x->size_hints = size_hints;
1412 f->output_data.x->hint_flags = hint_flags;
4d7e6e51 1413 unblock_input ();
3e5fc571 1414 }
f392e843
JD
1415}
1416
1417/* Change background color of a frame.
6772c8e1 1418 Since GTK uses the background color to clear the window, we must
f392e843
JD
1419 keep the GTK and X colors in sync.
1420 F is the frame to change,
1421 BG is the pixel value to change to. */
71bacd48 1422
f392e843 1423void
2d9783e0 1424xg_set_background_color (struct frame *f, unsigned long bg)
f392e843
JD
1425{
1426 if (FRAME_GTK_WIDGET (f))
1427 {
4d7e6e51 1428 block_input ();
0afb4571 1429 xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f));
4d7e6e51 1430 unblock_input ();
f392e843
JD
1431 }
1432}
1433
1434
1001243b
JD
1435/* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
1436 functions so GTK does not overwrite the icon. */
1437
1438void
a10c8269 1439xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
1001243b 1440{
0afb4571
J
1441 GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
1442 icon_pixmap,
1443 icon_mask);
1444 if (gp)
1001243b
JD
1445 gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
1446}
1447
1448
f392e843
JD
1449\f
1450/***********************************************************************
1451 Dialog functions
1452 ***********************************************************************/
1453/* Return the dialog title to use for a dialog of type KEY.
1454 This is the encoding used by lwlib. We use the same for GTK. */
71bacd48 1455
8ea90aa3 1456static const char *
f392e843
JD
1457get_dialog_title (char key)
1458{
8ea90aa3 1459 const char *title = "";
177c0ea7 1460
f392e843
JD
1461 switch (key) {
1462 case 'E': case 'e':
1463 title = "Error";
1464 break;
1465
1466 case 'I': case 'i':
1467 title = "Information";
1468 break;
1469
1470 case 'L': case 'l':
1471 title = "Prompt";
1472 break;
1473
1474 case 'P': case 'p':
1475 title = "Prompt";
1476 break;
1477
1478 case 'Q': case 'q':
1479 title = "Question";
1480 break;
1481 }
1482
1483 return title;
1484}
1485
1486/* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
1487 the dialog, but return TRUE so the event does not propagate further
1488 in GTK. This prevents GTK from destroying the dialog widget automatically
4c36be58 1489 and we can always destroy the widget manually, regardless of how
f392e843
JD
1490 it was popped down (button press or WM_DELETE_WINDOW).
1491 W is the dialog widget.
1492 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
1493 user_data is NULL (not used).
1494
1495 Returns TRUE to end propagation of event. */
71bacd48 1496
f392e843 1497static gboolean
971de7fb 1498dialog_delete_callback (GtkWidget *w, GdkEvent *event, gpointer user_data)
f392e843
JD
1499{
1500 gtk_widget_unmap (w);
1501 return TRUE;
1502}
1503
1504/* Create a popup dialog window. See also xg_create_widget below.
1505 WV is a widget_value describing the dialog.
1506 SELECT_CB is the callback to use when a button has been pressed.
1507 DEACTIVATE_CB is the callback to use when the dialog pops down.
1508
1509 Returns the GTK dialog widget. */
71bacd48 1510
f392e843 1511static GtkWidget *
e4c8d29a
J
1512create_dialog (widget_value *wv,
1513 GCallback select_cb,
1514 GCallback deactivate_cb)
f392e843 1515{
8ea90aa3 1516 const char *title = get_dialog_title (wv->name[0]);
f392e843
JD
1517 int total_buttons = wv->name[1] - '0';
1518 int right_buttons = wv->name[4] - '0';
1519 int left_buttons;
1520 int button_nr = 0;
1521 int button_spacing = 10;
1522 GtkWidget *wdialog = gtk_dialog_new ();
e547b051
J
1523 GtkDialog *wd = GTK_DIALOG (wdialog);
1524 GtkBox *cur_box = GTK_BOX (gtk_dialog_get_action_area (wd));
f392e843 1525 widget_value *item;
f392e843
JD
1526 GtkWidget *whbox_down;
1527
1528 /* If the number of buttons is greater than 4, make two rows of buttons
1529 instead. This looks better. */
18e27ea8 1530 bool make_two_rows = total_buttons > 4;
f392e843
JD
1531
1532 if (right_buttons == 0) right_buttons = total_buttons/2;
1533 left_buttons = total_buttons - right_buttons;
1534
1535 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1536 gtk_widget_set_name (wdialog, "emacs-dialog");
1537
f392e843
JD
1538
1539 if (make_two_rows)
1540 {
383b7c95
JD
1541 GtkWidget *wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
1542 GtkWidget *whbox_up = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1543 gtk_box_set_homogeneous (GTK_BOX (wvbox), TRUE);
1544 gtk_box_set_homogeneous (GTK_BOX (whbox_up), FALSE);
1545 whbox_down = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1546 gtk_box_set_homogeneous (GTK_BOX (whbox_down), FALSE);
f392e843
JD
1547
1548 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1549 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1550 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1551
1552 cur_box = GTK_BOX (whbox_up);
1553 }
1554
1555 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1556 G_CALLBACK (dialog_delete_callback), 0);
177c0ea7 1557
f392e843
JD
1558 if (deactivate_cb)
1559 {
1560 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1561 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1562 }
1563
1564 for (item = wv->contents; item; item = item->next)
1565 {
1566 char *utf8_label = get_utf8_string (item->value);
1567 GtkWidget *w;
1568 GtkRequisition req;
1569
0a1d6de0 1570 if (item->name && strcmp (item->name, "message") == 0)
f392e843 1571 {
e547b051 1572 GtkBox *wvbox = GTK_BOX (gtk_dialog_get_content_area (wd));
f392e843
JD
1573 /* This is the text part of the dialog. */
1574 w = gtk_label_new (utf8_label);
e547b051
J
1575 gtk_box_pack_start (wvbox, gtk_label_new (""), FALSE, FALSE, 0);
1576 gtk_box_pack_start (wvbox, w, TRUE, TRUE, 0);
f392e843
JD
1577 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1578
1579 /* Try to make dialog look better. Must realize first so
1580 the widget can calculate the size it needs. */
1581 gtk_widget_realize (w);
0afb4571 1582 gtk_widget_get_preferred_size (w, NULL, &req);
e547b051 1583 gtk_box_set_spacing (wvbox, req.height);
0a1d6de0 1584 if (item->value && strlen (item->value) > 0)
f392e843
JD
1585 button_spacing = 2*req.width/strlen (item->value);
1586 }
1587 else
1588 {
1589 /* This is one button to add to the dialog. */
4b1b4443 1590 w = gtk_button_new_with_label (utf8_label);
f392e843
JD
1591 if (! item->enabled)
1592 gtk_widget_set_sensitive (w, FALSE);
1593 if (select_cb)
1594 g_signal_connect (G_OBJECT (w), "clicked",
1595 select_cb, item->call_data);
1596
1597 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1598 if (++button_nr == left_buttons)
1599 {
1600 if (make_two_rows)
1601 cur_box = GTK_BOX (whbox_down);
1602 else
1603 gtk_box_pack_start (cur_box,
1604 gtk_label_new (""),
1605 TRUE, TRUE,
1606 button_spacing);
1607 }
1608 }
1609
42ca4633 1610 if (utf8_label)
f392e843
JD
1611 g_free (utf8_label);
1612 }
1613
1614 return wdialog;
1615}
1616
e90292a9 1617struct xg_dialog_data
90f2e16b 1618{
e90292a9
JD
1619 GMainLoop *loop;
1620 int response;
1621 GtkWidget *w;
1622 guint timerid;
1623};
90f2e16b 1624
3402c0a6 1625/* Function that is called when the file or font dialogs pop down.
457a8155 1626 W is the dialog widget, RESPONSE is the response code.
e90292a9 1627 USER_DATA is what we passed in to g_signal_connect. */
457a8155
JD
1628
1629static void
dd4c5104
DN
1630xg_dialog_response_cb (GtkDialog *w,
1631 gint response,
1632 gpointer user_data)
457a8155 1633{
7d652d97 1634 struct xg_dialog_data *dd = user_data;
e90292a9
JD
1635 dd->response = response;
1636 g_main_loop_quit (dd->loop);
457a8155
JD
1637}
1638
1639
1640/* Destroy the dialog. This makes it pop down. */
1641
27e498e6
PE
1642static void
1643pop_down_dialog (void *arg)
457a8155 1644{
27e498e6 1645 struct xg_dialog_data *dd = arg;
e90292a9 1646
4d7e6e51 1647 block_input ();
e90292a9
JD
1648 if (dd->w) gtk_widget_destroy (dd->w);
1649 if (dd->timerid != 0) g_source_remove (dd->timerid);
1650
1651 g_main_loop_quit (dd->loop);
1652 g_main_loop_unref (dd->loop);
088c8c09 1653
4d7e6e51 1654 unblock_input ();
457a8155
JD
1655}
1656
e90292a9
JD
1657/* If there are any emacs timers pending, add a timeout to main loop in DATA.
1658 We pass in DATA as gpointer* so we can use this as a callback. */
1659
1660static gboolean
971de7fb 1661xg_maybe_add_timer (gpointer data)
e90292a9 1662{
7d652d97 1663 struct xg_dialog_data *dd = data;
43aac990 1664 struct timespec next_time = timer_check ();
e90292a9
JD
1665
1666 dd->timerid = 0;
1667
43aac990 1668 if (timespec_valid_p (next_time))
e90292a9 1669 {
43aac990
PE
1670 time_t s = next_time.tv_sec;
1671 int per_ms = TIMESPEC_RESOLUTION / 1000;
1672 int ms = (next_time.tv_nsec + per_ms - 1) / per_ms;
d35af63c
PE
1673 if (s <= ((guint) -1 - ms) / 1000)
1674 dd->timerid = g_timeout_add (s * 1000 + ms, xg_maybe_add_timer, dd);
e90292a9
JD
1675 }
1676 return FALSE;
1677}
1678
088c8c09 1679
e90292a9
JD
1680/* Pops up a modal dialog W and waits for response.
1681 We don't use gtk_dialog_run because we want to process emacs timers.
1682 The dialog W is not destroyed when this function returns. */
1683
1684static int
a10c8269 1685xg_dialog_run (struct frame *f, GtkWidget *w)
e90292a9 1686{
d311d28c 1687 ptrdiff_t count = SPECPDL_INDEX ();
e90292a9
JD
1688 struct xg_dialog_data dd;
1689
1690 xg_set_screen (w, f);
1691 gtk_window_set_transient_for (GTK_WINDOW (w),
1692 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1693 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1694 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1695
1696 dd.loop = g_main_loop_new (NULL, FALSE);
1697 dd.response = GTK_RESPONSE_CANCEL;
1698 dd.w = w;
1699 dd.timerid = 0;
1700
1701 g_signal_connect (G_OBJECT (w),
1702 "response",
1703 G_CALLBACK (xg_dialog_response_cb),
1704 &dd);
1705 /* Don't destroy the widget if closed by the window manager close button. */
1706 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1707 gtk_widget_show (w);
1708
27e498e6 1709 record_unwind_protect_ptr (pop_down_dialog, &dd);
e90292a9
JD
1710
1711 (void) xg_maybe_add_timer (&dd);
1712 g_main_loop_run (dd.loop);
088c8c09 1713
e90292a9
JD
1714 dd.w = 0;
1715 unbind_to (count, Qnil);
1716
1717 return dd.response;
1718}
1719
1720\f
1721/***********************************************************************
1722 File dialog functions
1723 ***********************************************************************/
18e27ea8 1724/* Return true if the old file selection dialog is being used. */
e90292a9 1725
18e27ea8 1726bool
971de7fb 1727xg_uses_old_file_dialog (void)
e90292a9 1728{
e547b051 1729#ifdef HAVE_GTK_FILE_SELECTION_NEW
e90292a9 1730 return x_gtk_use_old_file_dialog;
e90292a9
JD
1731#else
1732 return 0;
1733#endif
e90292a9
JD
1734}
1735
1736
f57e2426 1737typedef char * (*xg_get_file_func) (GtkWidget *);
f9d64bb3 1738
457a8155
JD
1739/* Return the selected file for file chooser dialog W.
1740 The returned string must be free:d. */
1741
1742static char *
971de7fb 1743xg_get_file_name_from_chooser (GtkWidget *w)
457a8155
JD
1744{
1745 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1746}
1747
4c9ca1a3
JD
1748/* Callback called when the "Show hidden files" toggle is pressed.
1749 WIDGET is the toggle widget, DATA is the file chooser dialog. */
1750
78c55a80 1751static void
971de7fb 1752xg_toggle_visibility_cb (GtkWidget *widget, gpointer data)
78c55a80
JD
1753{
1754 GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
1755 gboolean visible;
78c55a80
JD
1756 g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
1757 g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
4c9ca1a3
JD
1758}
1759
1760
1761/* Callback called when a property changes in a file chooser.
1762 GOBJECT is the file chooser dialog, ARG1 describes the property.
1763 USER_DATA is the toggle widget in the file chooser dialog.
1764 We use this to update the "Show hidden files" toggle when the user
1765 changes that property by right clicking in the file list. */
1766
1767static void
971de7fb 1768xg_toggle_notify_cb (GObject *gobject, GParamSpec *arg1, gpointer user_data)
4c9ca1a3 1769{
4c9ca1a3
JD
1770 if (strcmp (arg1->name, "show-hidden") == 0)
1771 {
4c9ca1a3
JD
1772 GtkWidget *wtoggle = GTK_WIDGET (user_data);
1773 gboolean visible, toggle_on;
1774
1775 g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
1776 toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
1777
1778 if (!!visible != !!toggle_on)
1779 {
1780 g_signal_handlers_block_by_func (G_OBJECT (wtoggle),
1781 G_CALLBACK (xg_toggle_visibility_cb),
1782 gobject);
1783 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
1784 g_signal_handlers_unblock_by_func
1785 (G_OBJECT (wtoggle),
1786 G_CALLBACK (xg_toggle_visibility_cb),
1787 gobject);
1788 }
1789 x_gtk_show_hidden_files = visible;
1790 }
78c55a80
JD
1791}
1792
f9d64bb3
JD
1793/* Read a file name from the user using a file chooser dialog.
1794 F is the current frame.
1795 PROMPT is a prompt to show to the user. May not be NULL.
1796 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
18e27ea8 1797 If MUSTMATCH_P, the returned file name must be an existing
2605051a
GM
1798 file. (Actually, this only has cosmetic effects, the user can
1799 still enter a non-existing file.) *FUNC is set to a function that
1800 can be used to retrieve the selected file name from the returned widget.
f9d64bb3 1801
457a8155 1802 Returns the created widget. */
f9d64bb3 1803
457a8155 1804static GtkWidget *
a10c8269 1805xg_get_file_with_chooser (struct frame *f,
dd4c5104
DN
1806 char *prompt,
1807 char *default_filename,
18e27ea8 1808 bool mustmatch_p, bool only_dir_p,
dd4c5104 1809 xg_get_file_func *func)
f9d64bb3 1810{
65dc836c 1811 char msgbuf[1024];
78c55a80 1812
a60e5f68 1813 GtkWidget *filewin, *wtoggle, *wbox, *wmessage IF_LINT (= NULL);
f9d64bb3 1814 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
f9d64bb3
JD
1815 GtkFileChooserAction action = (mustmatch_p ?
1816 GTK_FILE_CHOOSER_ACTION_OPEN :
1817 GTK_FILE_CHOOSER_ACTION_SAVE);
1818
1819 if (only_dir_p)
1820 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1821
1822 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1823 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1824 (mustmatch_p || only_dir_p ?
8cfd0f36 1825 GTK_STOCK_OPEN : GTK_STOCK_OK),
f9d64bb3
JD
1826 GTK_RESPONSE_OK,
1827 NULL);
ded997c1 1828 gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
f9d64bb3 1829
383b7c95
JD
1830 wbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1831 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
78c55a80
JD
1832 gtk_widget_show (wbox);
1833 wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
8e5a8840
JB
1834
1835 if (x_gtk_show_hidden_files)
78c55a80
JD
1836 {
1837 g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
1838 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
1839 }
1840 gtk_widget_show (wtoggle);
1841 g_signal_connect (G_OBJECT (wtoggle), "clicked",
4c9ca1a3
JD
1842 G_CALLBACK (xg_toggle_visibility_cb), filewin);
1843 g_signal_connect (G_OBJECT (filewin), "notify",
1844 G_CALLBACK (xg_toggle_notify_cb), wtoggle);
78c55a80 1845
a10fe834 1846 if (x_gtk_file_dialog_help_text)
cf1e295f 1847 {
65dc836c 1848 msgbuf[0] = '\0';
7b7d4a79
JD
1849 /* Gtk+ 2.10 has the file name text entry box integrated in the dialog.
1850 Show the C-l help text only for versions < 2.10. */
7adfb96e 1851 if (gtk_check_version (2, 10, 0) && action != GTK_FILE_CHOOSER_ACTION_SAVE)
65dc836c
PE
1852 strcat (msgbuf, "\nType C-l to display a file name text entry box.\n");
1853 strcat (msgbuf, "\nIf you don't like this file selector, use the "
cf1e295f
JD
1854 "corresponding\nkey binding or customize "
1855 "use-file-dialog to turn it off.");
8e5a8840 1856
65dc836c 1857 wmessage = gtk_label_new (msgbuf);
cf1e295f
JD
1858 gtk_widget_show (wmessage);
1859 }
1860
78c55a80 1861 gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
a10fe834 1862 if (x_gtk_file_dialog_help_text)
cf1e295f 1863 gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
78c55a80
JD
1864 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
1865
f9d64bb3 1866 if (default_filename)
a872928c
JD
1867 {
1868 Lisp_Object file;
1869 struct gcpro gcpro1;
4f3097d8 1870 char *utf8_filename;
45c94881 1871 GCPRO1 (file);
a872928c 1872
ded997c1
JD
1873 file = build_string (default_filename);
1874
a872928c
JD
1875 /* File chooser does not understand ~/... in the file name. It must be
1876 an absolute name starting with /. */
1877 if (default_filename[0] != '/')
ded997c1 1878 file = Fexpand_file_name (file, Qnil);
8e5a8840 1879
4f3097d8
JD
1880 utf8_filename = SSDATA (ENCODE_UTF_8 (file));
1881 if (! NILP (Ffile_directory_p (file)))
ded997c1 1882 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
4f3097d8 1883 utf8_filename);
ded997c1 1884 else
4f3097d8 1885 {
4f3097d8
JD
1886 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1887 utf8_filename);
ae6c1c19
JD
1888 if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
1889 {
1890 char *cp = strrchr (utf8_filename, '/');
1891 if (cp) ++cp;
1892 else cp = utf8_filename;
1893 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (filewin), cp);
1894 }
4f3097d8 1895 }
a872928c
JD
1896
1897 UNGCPRO;
1898 }
f9d64bb3 1899
457a8155
JD
1900 *func = xg_get_file_name_from_chooser;
1901 return filewin;
f9d64bb3 1902}
f9d64bb3
JD
1903
1904#ifdef HAVE_GTK_FILE_SELECTION_NEW
f392e843 1905
457a8155
JD
1906/* Return the selected file for file selector dialog W.
1907 The returned string must be free:d. */
71bacd48 1908
457a8155 1909static char *
971de7fb 1910xg_get_file_name_from_selector (GtkWidget *w)
f392e843 1911{
457a8155 1912 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
7d652d97 1913 return xstrdup (gtk_file_selection_get_filename (filesel));
f392e843
JD
1914}
1915
457a8155 1916/* Create a file selection dialog.
f392e843
JD
1917 F is the current frame.
1918 PROMPT is a prompt to show to the user. May not be NULL.
1919 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
18e27ea8 1920 If MUSTMATCH_P, the returned file name must be an existing
457a8155
JD
1921 file. *FUNC is set to a function that can be used to retrieve the
1922 selected file name from the returned widget.
f392e843 1923
457a8155 1924 Returns the created widget. */
71bacd48 1925
457a8155 1926static GtkWidget *
a10c8269 1927xg_get_file_with_selection (struct frame *f,
e4c8d29a
J
1928 char *prompt,
1929 char *default_filename,
18e27ea8 1930 bool mustmatch_p, bool only_dir_p,
e4c8d29a 1931 xg_get_file_func *func)
f392e843
JD
1932{
1933 GtkWidget *filewin;
1934 GtkFileSelection *filesel;
177c0ea7 1935
f392e843
JD
1936 filewin = gtk_file_selection_new (prompt);
1937 filesel = GTK_FILE_SELECTION (filewin);
1938
f392e843
JD
1939 if (default_filename)
1940 gtk_file_selection_set_filename (filesel, default_filename);
1941
1942 if (mustmatch_p)
1943 {
1944 /* The selection_entry part of filesel is not documented. */
1945 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1946 gtk_file_selection_hide_fileop_buttons (filesel);
1947 }
1948
457a8155 1949 *func = xg_get_file_name_from_selector;
177c0ea7 1950
457a8155 1951 return filewin;
f392e843 1952}
f9d64bb3
JD
1953#endif /* HAVE_GTK_FILE_SELECTION_NEW */
1954
1955/* Read a file name from the user using a file dialog, either the old
1956 file selection dialog, or the new file chooser dialog. Which to use
1957 depends on what the GTK version used has, and what the value of
1958 gtk-use-old-file-dialog.
1959 F is the current frame.
1960 PROMPT is a prompt to show to the user. May not be NULL.
1961 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
18e27ea8 1962 If MUSTMATCH_P, the returned file name must be an existing
f9d64bb3
JD
1963 file.
1964
1965 Returns a file name or NULL if no file was selected.
1966 The returned string must be freed by the caller. */
1967
1968char *
a10c8269 1969xg_get_file_name (struct frame *f,
e4c8d29a
J
1970 char *prompt,
1971 char *default_filename,
18e27ea8
PE
1972 bool mustmatch_p,
1973 bool only_dir_p)
f9d64bb3 1974{
724cde0d 1975 GtkWidget *w = 0;
457a8155
JD
1976 char *fn = 0;
1977 int filesel_done = 0;
1978 xg_get_file_func func;
1979
e547b051 1980#ifdef HAVE_GTK_FILE_SELECTION_NEW
255e4140 1981
90f2e16b 1982 if (xg_uses_old_file_dialog ())
457a8155
JD
1983 w = xg_get_file_with_selection (f, prompt, default_filename,
1984 mustmatch_p, only_dir_p, &func);
1985 else
1986 w = xg_get_file_with_chooser (f, prompt, default_filename,
1987 mustmatch_p, only_dir_p, &func);
f9d64bb3 1988
e547b051 1989#else /* not HAVE_GTK_FILE_SELECTION_NEW */
457a8155
JD
1990 w = xg_get_file_with_chooser (f, prompt, default_filename,
1991 mustmatch_p, only_dir_p, &func);
e547b051 1992#endif /* not HAVE_GTK_FILE_SELECTION_NEW */
457a8155 1993
457a8155 1994 gtk_widget_set_name (w, "emacs-filedialog");
457a8155 1995
e90292a9 1996 filesel_done = xg_dialog_run (f, w);
457a8155
JD
1997 if (filesel_done == GTK_RESPONSE_OK)
1998 fn = (*func) (w);
1999
e90292a9 2000 gtk_widget_destroy (w);
457a8155 2001 return fn;
f9d64bb3 2002}
f392e843 2003
f2045622
CY
2004/***********************************************************************
2005 GTK font chooser
2006 ***********************************************************************/
2007
3402c0a6 2008#ifdef HAVE_FREETYPE
f2045622
CY
2009
2010#if USE_NEW_GTK_FONT_CHOOSER
2011
f2045622
CY
2012#define XG_WEIGHT_TO_SYMBOL(w) \
2013 (w <= PANGO_WEIGHT_THIN ? Qextra_light \
2014 : w <= PANGO_WEIGHT_ULTRALIGHT ? Qlight \
2015 : w <= PANGO_WEIGHT_LIGHT ? Qsemi_light \
2016 : w < PANGO_WEIGHT_MEDIUM ? Qnormal \
2017 : w <= PANGO_WEIGHT_SEMIBOLD ? Qsemi_bold \
2018 : w <= PANGO_WEIGHT_BOLD ? Qbold \
2019 : w <= PANGO_WEIGHT_HEAVY ? Qextra_bold \
2020 : Qultra_bold)
2021
2022#define XG_STYLE_TO_SYMBOL(s) \
2023 (s == PANGO_STYLE_OBLIQUE ? Qoblique \
2024 : s == PANGO_STYLE_ITALIC ? Qitalic \
2025 : Qnormal)
2026
2027#endif /* USE_NEW_GTK_FONT_CHOOSER */
2028
2029
2030static char *x_last_font_name;
2031
3402c0a6
CY
2032/* Pop up a GTK font selector and return the name of the font the user
2033 selects, as a C string. The returned font name follows GTK's own
2034 format:
2035
2036 `FAMILY [VALUE1 VALUE2] SIZE'
2037
2038 This can be parsed using font_parse_fcname in font.c.
2039 DEFAULT_NAME, if non-zero, is the default font name. */
2040
f2045622 2041Lisp_Object
a10c8269 2042xg_get_font (struct frame *f, const char *default_name)
3402c0a6 2043{
e90292a9 2044 GtkWidget *w;
3402c0a6 2045 int done = 0;
f2045622 2046 Lisp_Object font = Qnil;
3402c0a6 2047
32bcadb4
JD
2048 w = gtk_font_chooser_dialog_new
2049 ("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2050
f2045622
CY
2051 if (default_name)
2052 {
2053 /* Convert fontconfig names to Gtk names, i.e. remove - before
2054 number */
2055 char *p = strrchr (default_name, '-');
2056 if (p)
2057 {
2058 char *ep = p+1;
620f13b0 2059 while (c_isdigit (*ep))
f2045622
CY
2060 ++ep;
2061 if (*ep == '\0') *p = ' ';
2062 }
2063 }
2064 else if (x_last_font_name)
2065 default_name = x_last_font_name;
3402c0a6 2066
f2045622
CY
2067 if (default_name)
2068 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (w), default_name);
3402c0a6 2069
f2045622 2070 gtk_widget_set_name (w, "emacs-fontdialog");
e90292a9 2071 done = xg_dialog_run (f, w);
3402c0a6 2072 if (done == GTK_RESPONSE_OK)
f2045622
CY
2073 {
2074#if USE_NEW_GTK_FONT_CHOOSER
2075 /* Use the GTK3 font chooser. */
2076 PangoFontDescription *desc
2077 = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (w));
2078
2079 if (desc)
2080 {
a2d19368 2081 Lisp_Object args[10];
f2045622 2082 const char *name = pango_font_description_get_family (desc);
5bf192ca 2083 gint size = pango_font_description_get_size (desc);
f2045622 2084 PangoWeight weight = pango_font_description_get_weight (desc);
5bf192ca 2085 PangoStyle style = pango_font_description_get_style (desc);
f2045622
CY
2086
2087 args[0] = QCname;
2088 args[1] = build_string (name);
2089
2090 args[2] = QCsize;
5bf192ca 2091 args[3] = make_float (pango_units_to_double (size));
f2045622
CY
2092
2093 args[4] = QCweight;
2094 args[5] = XG_WEIGHT_TO_SYMBOL (weight);
2095
2096 args[6] = QCslant;
2097 args[7] = XG_STYLE_TO_SYMBOL (style);
2098
a2d19368
CY
2099 args[8] = QCtype;
2100 args[9] = Qxft;
2101
f2045622
CY
2102 font = Ffont_spec (8, args);
2103
2104 pango_font_description_free (desc);
2105 xfree (x_last_font_name);
2106 x_last_font_name = xstrdup (name);
2107 }
2108
2109#else /* Use old font selector, which just returns the font name. */
2110
2111 char *font_name
2112 = gtk_font_selection_dialog_get_font_name (GTK_FONT_CHOOSER (w));
2113
2114 if (font_name)
2115 {
2116 font = build_string (font_name);
2117 g_free (x_last_font_name);
2118 x_last_font_name = font_name;
2119 }
2120#endif /* USE_NEW_GTK_FONT_CHOOSER */
2121 }
3402c0a6 2122
e90292a9 2123 gtk_widget_destroy (w);
f2045622 2124 return font;
3402c0a6
CY
2125}
2126#endif /* HAVE_FREETYPE */
2127
2128
f392e843
JD
2129\f
2130/***********************************************************************
2131 Menu functions.
2132 ***********************************************************************/
2133
d1c38b57 2134/* The name of menu items that can be used for customization. Since GTK
f392e843 2135 RC files are very crude and primitive, we have to set this on all
d1c38b57 2136 menu item names so a user can easily customize menu items. */
f392e843
JD
2137
2138#define MENU_ITEM_NAME "emacs-menuitem"
2139
2140
2141/* Linked list of all allocated struct xg_menu_cb_data. Used for marking
2142 during GC. The next member points to the items. */
2143static xg_list_node xg_menu_cb_list;
2144
2145/* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
2146 during GC. The next member points to the items. */
2147static xg_list_node xg_menu_item_cb_list;
2148
2149/* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
2150 F is the frame CL_DATA will be initialized for.
2151 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2152
2153 The menu bar and all sub menus under the menu bar in a frame
2154 share the same structure, hence the reference count.
177c0ea7 2155
f392e843
JD
2156 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
2157 allocated xg_menu_cb_data if CL_DATA is NULL. */
71bacd48 2158
f392e843 2159static xg_menu_cb_data *
a10c8269 2160make_cl_data (xg_menu_cb_data *cl_data, struct frame *f, GCallback highlight_cb)
f392e843
JD
2161{
2162 if (! cl_data)
2163 {
38182d90 2164 cl_data = xmalloc (sizeof *cl_data);
f392e843 2165 cl_data->f = f;
e69b0960 2166 cl_data->menu_bar_vector = f->menu_bar_vector;
f392e843
JD
2167 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2168 cl_data->highlight_cb = highlight_cb;
2169 cl_data->ref_count = 0;
2170
2171 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
2172 }
2173
2174 cl_data->ref_count++;
2175
2176 return cl_data;
2177}
2178
2179/* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
2180 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2181
2182 When the menu bar is updated, menu items may have been added and/or
2183 removed, so menu_bar_vector and menu_bar_items_used change. We must
2184 then update CL_DATA since it is used to determine which menu
2185 item that is invoked in the menu.
2186 HIGHLIGHT_CB could change, there is no check that the same
2187 function is given when modifying a menu bar as was given when
2188 creating the menu bar. */
71bacd48 2189
f392e843 2190static void
e4c8d29a 2191update_cl_data (xg_menu_cb_data *cl_data,
a10c8269 2192 struct frame *f,
e4c8d29a 2193 GCallback highlight_cb)
f392e843
JD
2194{
2195 if (cl_data)
2196 {
2197 cl_data->f = f;
e69b0960 2198 cl_data->menu_bar_vector = f->menu_bar_vector;
f392e843
JD
2199 cl_data->menu_bar_items_used = f->menu_bar_items_used;
2200 cl_data->highlight_cb = highlight_cb;
2201 }
2202}
2203
2204/* Decrease reference count for CL_DATA.
2205 If reference count is zero, free CL_DATA. */
71bacd48 2206
f392e843 2207static void
971de7fb 2208unref_cl_data (xg_menu_cb_data *cl_data)
f392e843
JD
2209{
2210 if (cl_data && cl_data->ref_count > 0)
2211 {
2212 cl_data->ref_count--;
2213 if (cl_data->ref_count == 0)
2214 {
2215 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
2216 xfree (cl_data);
2217 }
2218 }
2219}
2220
2221/* Function that marks all lisp data during GC. */
71bacd48 2222
f392e843 2223void
971de7fb 2224xg_mark_data (void)
f392e843
JD
2225{
2226 xg_list_node *iter;
5884c324 2227 Lisp_Object rest, frame;
f392e843
JD
2228
2229 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
631f2082 2230 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
f392e843
JD
2231
2232 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
2233 {
2234 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
2235
2236 if (! NILP (cb_data->help))
631f2082 2237 mark_object (cb_data->help);
f392e843 2238 }
ca06f160 2239
ca06f160
JD
2240 FOR_EACH_FRAME (rest, frame)
2241 {
a10c8269 2242 struct frame *f = XFRAME (frame);
ca06f160 2243
ac4845a6 2244 if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
ca06f160
JD
2245 {
2246 struct xg_frame_tb_info *tbinfo
2247 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
2248 TB_INFO_KEY);
2249 if (tbinfo)
2250 {
2251 mark_object (tbinfo->last_tool_bar);
2252 mark_object (tbinfo->style);
2253 }
2254 }
2255 }
f392e843
JD
2256}
2257
2258
2259/* Callback called when a menu item is destroyed. Used to free data.
2260 W is the widget that is being destroyed (not used).
2261 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
71bacd48 2262
f392e843 2263static void
971de7fb 2264menuitem_destroy_callback (GtkWidget *w, gpointer client_data)
f392e843
JD
2265{
2266 if (client_data)
2267 {
7d652d97 2268 xg_menu_item_cb_data *data = client_data;
f392e843
JD
2269 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
2270 xfree (data);
2271 }
2272}
2273
2274/* Callback called when the pointer enters/leaves a menu item.
665c8f1c 2275 W is the parent of the menu item.
f392e843 2276 EVENT is either an enter event or leave event.
665c8f1c 2277 CLIENT_DATA is not used.
f392e843
JD
2278
2279 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 2280
f392e843 2281static gboolean
e4c8d29a
J
2282menuitem_highlight_callback (GtkWidget *w,
2283 GdkEventCrossing *event,
2284 gpointer client_data)
f392e843 2285{
665c8f1c
JD
2286 GdkEvent ev;
2287 GtkWidget *subwidget;
2288 xg_menu_item_cb_data *data;
177c0ea7 2289
665c8f1c
JD
2290 ev.crossing = *event;
2291 subwidget = gtk_get_event_widget (&ev);
7d652d97 2292 data = g_object_get_data (G_OBJECT (subwidget), XG_ITEM_DATA);
665c8f1c
JD
2293 if (data)
2294 {
f392e843
JD
2295 if (! NILP (data->help) && data->cl_data->highlight_cb)
2296 {
665c8f1c 2297 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : data;
f392e843 2298 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
665c8f1c 2299 (*func) (subwidget, call_data);
f392e843
JD
2300 }
2301 }
2302
2303 return FALSE;
2304}
2305
2306/* Callback called when a menu is destroyed. Used to free data.
2307 W is the widget that is being destroyed (not used).
2308 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
71bacd48 2309
f392e843 2310static void
971de7fb 2311menu_destroy_callback (GtkWidget *w, gpointer client_data)
f392e843 2312{
7d652d97 2313 unref_cl_data (client_data);
f392e843
JD
2314}
2315
f392e843
JD
2316/* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
2317 must be non-NULL) and can be inserted into a menu item.
2318
2319 Returns the GtkHBox. */
71bacd48 2320
f392e843 2321static GtkWidget *
8ea90aa3 2322make_widget_for_menu_item (const char *utf8_label, const char *utf8_key)
f392e843
JD
2323{
2324 GtkWidget *wlbl;
2325 GtkWidget *wkey;
2326 GtkWidget *wbox;
177c0ea7 2327
383b7c95
JD
2328 wbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2329 gtk_box_set_homogeneous (GTK_BOX (wbox), FALSE);
4b1b4443 2330 wlbl = gtk_label_new (utf8_label);
f392e843
JD
2331 wkey = gtk_label_new (utf8_key);
2332
2333 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
2334 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
177c0ea7 2335
f392e843
JD
2336 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
2337 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
2338
2339 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
2340 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
7863d625 2341 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
f392e843
JD
2342
2343 return wbox;
2344}
2345
2346/* Make and return a menu item widget with the key to the right.
2347 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
2348 UTF8_KEY is the text representing the key binding.
2349 ITEM is the widget_value describing the menu item.
177c0ea7 2350
f392e843
JD
2351 GROUP is an in/out parameter. If the menu item to be created is not
2352 part of any radio menu group, *GROUP contains NULL on entry and exit.
2353 If the menu item to be created is part of a radio menu group, on entry
2354 *GROUP contains the group to use, or NULL if this is the first item
2355 in the group. On exit, *GROUP contains the radio item group.
2356
2357 Unfortunately, keys don't line up as nicely as in Motif,
2358 but the MacOS X version doesn't either, so I guess that is OK. */
71bacd48 2359
f392e843 2360static GtkWidget *
8ea90aa3
DN
2361make_menu_item (const char *utf8_label,
2362 const char *utf8_key,
e4c8d29a
J
2363 widget_value *item,
2364 GSList **group)
f392e843
JD
2365{
2366 GtkWidget *w;
2367 GtkWidget *wtoadd = 0;
177c0ea7 2368
adcb132c
JD
2369 /* It has been observed that some menu items have a NULL name field.
2370 This will lead to this function being called with a NULL utf8_label.
2371 GTK crashes on that so we set a blank label. Why there is a NULL
2372 name remains to be investigated. */
2373 if (! utf8_label) utf8_label = " ";
2374
f392e843
JD
2375 if (utf8_key)
2376 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
177c0ea7 2377
f392e843
JD
2378 if (item->button_type == BUTTON_TYPE_TOGGLE)
2379 {
2380 *group = NULL;
2381 if (utf8_key) w = gtk_check_menu_item_new ();
4b1b4443 2382 else w = gtk_check_menu_item_new_with_label (utf8_label);
f392e843
JD
2383 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
2384 }
2385 else if (item->button_type == BUTTON_TYPE_RADIO)
2386 {
2387 if (utf8_key) w = gtk_radio_menu_item_new (*group);
4b1b4443 2388 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
f392e843
JD
2389 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
2390 if (item->selected)
2391 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
2392 }
2393 else
2394 {
2395 *group = NULL;
2396 if (utf8_key) w = gtk_menu_item_new ();
4b1b4443 2397 else w = gtk_menu_item_new_with_label (utf8_label);
f392e843 2398 }
177c0ea7 2399
f392e843
JD
2400 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
2401 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
2402
2403 return w;
2404}
2405
8b745d92
JD
2406#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
2407
da18b5ac
JD
2408static int xg_detached_menus;
2409
18e27ea8 2410/* Return true if there are detached menus. */
71bacd48 2411
18e27ea8 2412bool
971de7fb 2413xg_have_tear_offs (void)
da18b5ac
JD
2414{
2415 return xg_detached_menus > 0;
2416}
f392e843
JD
2417
2418/* Callback invoked when a detached menu window is removed. Here we
da18b5ac 2419 decrease the xg_detached_menus count.
f392e843 2420 WIDGET is the top level window that is removed (the parent of the menu).
da18b5ac 2421 CLIENT_DATA is not used. */
71bacd48 2422
da18b5ac 2423static void
971de7fb 2424tearoff_remove (GtkWidget *widget, gpointer client_data)
f392e843 2425{
da18b5ac 2426 if (xg_detached_menus > 0) --xg_detached_menus;
f392e843
JD
2427}
2428
da18b5ac
JD
2429/* Callback invoked when a menu is detached. It increases the
2430 xg_detached_menus count.
f392e843 2431 WIDGET is the GtkTearoffMenuItem.
177c0ea7 2432 CLIENT_DATA is not used. */
71bacd48 2433
f392e843 2434static void
971de7fb 2435tearoff_activate (GtkWidget *widget, gpointer client_data)
f392e843
JD
2436{
2437 GtkWidget *menu = gtk_widget_get_parent (widget);
da18b5ac
JD
2438 if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
2439 {
2440 ++xg_detached_menus;
2441 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
2442 "destroy",
2443 G_CALLBACK (tearoff_remove), 0);
2444 }
f392e843 2445}
8b745d92
JD
2446#else /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
2447bool
2448xg_have_tear_offs (void)
2449{
2450 return false;
2451}
2452#endif /* ! HAVE_GTK_TEAROFF_MENU_ITEM_NEW */
f392e843 2453
f392e843 2454/* Create a menu item widget, and connect the callbacks.
4c36be58 2455 ITEM describes the menu item.
f392e843
JD
2456 F is the frame the created menu belongs to.
2457 SELECT_CB is the callback to use when a menu item is selected.
2458 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2459 CL_DATA points to the callback data to be used for this menu.
2460 GROUP is an in/out parameter. If the menu item to be created is not
2461 part of any radio menu group, *GROUP contains NULL on entry and exit.
2462 If the menu item to be created is part of a radio menu group, on entry
2463 *GROUP contains the group to use, or NULL if this is the first item
2464 in the group. On exit, *GROUP contains the radio item group.
2465
2466 Returns the created GtkWidget. */
71bacd48 2467
f392e843 2468static GtkWidget *
e4c8d29a 2469xg_create_one_menuitem (widget_value *item,
a10c8269 2470 struct frame *f,
e4c8d29a
J
2471 GCallback select_cb,
2472 GCallback highlight_cb,
2473 xg_menu_cb_data *cl_data,
2474 GSList **group)
f392e843
JD
2475{
2476 char *utf8_label;
2477 char *utf8_key;
2478 GtkWidget *w;
2479 xg_menu_item_cb_data *cb_data;
2480
2481 utf8_label = get_utf8_string (item->name);
2482 utf8_key = get_utf8_string (item->key);
2483
2484 w = make_menu_item (utf8_label, utf8_key, item, group);
2485
42ca4633
J
2486 if (utf8_label) g_free (utf8_label);
2487 if (utf8_key) g_free (utf8_key);
f392e843 2488
38182d90 2489 cb_data = xmalloc (sizeof *cb_data);
f392e843
JD
2490
2491 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
2492
665c8f1c 2493 cb_data->select_id = 0;
f392e843
JD
2494 cb_data->help = item->help;
2495 cb_data->cl_data = cl_data;
2496 cb_data->call_data = item->call_data;
177c0ea7 2497
f392e843
JD
2498 g_signal_connect (G_OBJECT (w),
2499 "destroy",
2500 G_CALLBACK (menuitem_destroy_callback),
2501 cb_data);
2502
2503 /* Put cb_data in widget, so we can get at it when modifying menubar */
2504 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
2505
2506 /* final item, not a submenu */
2507 if (item->call_data && ! item->contents)
2508 {
2509 if (select_cb)
2510 cb_data->select_id
2511 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
2512 }
2513
f392e843
JD
2514 return w;
2515}
2516
2517/* Create a full menu tree specified by DATA.
2518 F is the frame the created menu belongs to.
2519 SELECT_CB is the callback to use when a menu item is selected.
2520 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2521 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
18e27ea8
PE
2522 If POP_UP_P, create a popup menu.
2523 If MENU_BAR_P, create a menu bar.
8b745d92
JD
2524 If ADD_TEAROFF_P, add a tearoff menu item. Ignored if MENU_BAR_P or
2525 the Gtk+ version used does not have tearoffs.
f392e843
JD
2526 TOPMENU is the topmost GtkWidget that others shall be placed under.
2527 It may be NULL, in that case we create the appropriate widget
2528 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
2529 CL_DATA is the callback data we shall use for this menu, or NULL
2530 if we haven't set the first callback yet.
2531 NAME is the name to give to the top level menu if this function
2532 creates it. May be NULL to not set any name.
2533
2534 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
2535 not NULL.
2536
2537 This function calls itself to create submenus. */
2538
2539static GtkWidget *
e4c8d29a 2540create_menus (widget_value *data,
a10c8269 2541 struct frame *f,
e4c8d29a
J
2542 GCallback select_cb,
2543 GCallback deactivate_cb,
2544 GCallback highlight_cb,
18e27ea8
PE
2545 bool pop_up_p,
2546 bool menu_bar_p,
2547 bool add_tearoff_p,
e4c8d29a
J
2548 GtkWidget *topmenu,
2549 xg_menu_cb_data *cl_data,
8ea90aa3 2550 const char *name)
f392e843
JD
2551{
2552 widget_value *item;
2553 GtkWidget *wmenu = topmenu;
2554 GSList *group = NULL;
2555
2556 if (! topmenu)
2557 {
810f2256
JD
2558 if (! menu_bar_p)
2559 {
2560 wmenu = gtk_menu_new ();
2561 xg_set_screen (wmenu, f);
665c8f1c
JD
2562 /* Connect this to the menu instead of items so we get enter/leave for
2563 disabled items also. TODO: Still does not get enter/leave for
2564 disabled items in detached menus. */
2565 g_signal_connect (G_OBJECT (wmenu),
2566 "enter-notify-event",
2567 G_CALLBACK (menuitem_highlight_callback),
2568 NULL);
2569 g_signal_connect (G_OBJECT (wmenu),
2570 "leave-notify-event",
2571 G_CALLBACK (menuitem_highlight_callback),
2572 NULL);
810f2256 2573 }
5be883cd
JD
2574 else
2575 {
2576 wmenu = gtk_menu_bar_new ();
872870b2
JD
2577 /* Set width of menu bar to a small value so it doesn't enlarge
2578 a small initial frame size. The width will be set to the
2579 width of the frame later on when it is added to a container.
2580 height -1: Natural height. */
5be883cd
JD
2581 gtk_widget_set_size_request (wmenu, 1, -1);
2582 }
f392e843
JD
2583
2584 /* Put cl_data on the top menu for easier access. */
2585 cl_data = make_cl_data (cl_data, f, highlight_cb);
2586 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
2587 g_signal_connect (G_OBJECT (wmenu), "destroy",
2588 G_CALLBACK (menu_destroy_callback), cl_data);
177c0ea7 2589
f392e843
JD
2590 if (name)
2591 gtk_widget_set_name (wmenu, name);
2592
2593 if (deactivate_cb)
2594 g_signal_connect (G_OBJECT (wmenu),
c8934d9d 2595 "selection-done", deactivate_cb, 0);
f392e843 2596 }
177c0ea7 2597
8b745d92 2598#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
f392e843
JD
2599 if (! menu_bar_p && add_tearoff_p)
2600 {
2601 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2602 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
2603
2604 g_signal_connect (G_OBJECT (tearoff), "activate",
2605 G_CALLBACK (tearoff_activate), 0);
2606 }
8b745d92 2607#endif
f392e843
JD
2608
2609 for (item = data; item; item = item->next)
2610 {
2611 GtkWidget *w;
177c0ea7 2612
f392e843 2613 if (pop_up_p && !item->contents && !item->call_data
4039c786 2614 && !menu_separator_name_p (item->name))
f392e843
JD
2615 {
2616 char *utf8_label;
2617 /* A title for a popup. We do the same as GTK does when
2618 creating titles, but it does not look good. */
2619 group = NULL;
2620 utf8_label = get_utf8_string (item->name);
2621
2622 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
4b1b4443 2623 w = gtk_menu_item_new_with_label (utf8_label);
f392e843 2624 gtk_widget_set_sensitive (w, FALSE);
42ca4633 2625 if (utf8_label) g_free (utf8_label);
f392e843 2626 }
4039c786 2627 else if (menu_separator_name_p (item->name))
f392e843
JD
2628 {
2629 group = NULL;
2630 /* GTK only have one separator type. */
2631 w = gtk_separator_menu_item_new ();
2632 }
2633 else
2634 {
2635 w = xg_create_one_menuitem (item,
2636 f,
2637 item->contents ? 0 : select_cb,
2638 highlight_cb,
2639 cl_data,
2640 &group);
2641
f56cff88
JD
2642 /* Create a possibly empty submenu for menu bar items, since some
2643 themes don't highlight items correctly without it. */
2644 if (item->contents || menu_bar_p)
f392e843
JD
2645 {
2646 GtkWidget *submenu = create_menus (item->contents,
2647 f,
2648 select_cb,
2649 deactivate_cb,
2650 highlight_cb,
2651 0,
2652 0,
da18b5ac 2653 add_tearoff_p,
f392e843
JD
2654 0,
2655 cl_data,
2656 0);
2657 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
2658 }
f392e843
JD
2659 }
2660
2661 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
2662 gtk_widget_set_name (w, MENU_ITEM_NAME);
2663 }
2664
2665 return wmenu;
2666}
2667
2668/* Create a menubar, popup menu or dialog, depending on the TYPE argument.
2669 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
2670 with some text and buttons.
2671 F is the frame the created item belongs to.
2672 NAME is the name to use for the top widget.
2673 VAL is a widget_value structure describing items to be created.
2674 SELECT_CB is the callback to use when a menu item is selected or
2675 a dialog button is pressed.
2676 DEACTIVATE_CB is the callback to use when an item is deactivated.
2677 For a menu, when a sub menu is not shown anymore, for a dialog it is
2678 called when the dialog is popped down.
2679 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2680
2681 Returns the widget created. */
71bacd48 2682
f392e843 2683GtkWidget *
a10c8269
DA
2684xg_create_widget (const char *type, const char *name, struct frame *f,
2685 widget_value *val, GCallback select_cb,
2686 GCallback deactivate_cb, GCallback highlight_cb)
f392e843
JD
2687{
2688 GtkWidget *w = 0;
18e27ea8
PE
2689 bool menu_bar_p = strcmp (type, "menubar") == 0;
2690 bool pop_up_p = strcmp (type, "popup") == 0;
da18b5ac 2691
f392e843
JD
2692 if (strcmp (type, "dialog") == 0)
2693 {
2694 w = create_dialog (val, select_cb, deactivate_cb);
810f2256 2695 xg_set_screen (w, f);
f392e843
JD
2696 gtk_window_set_transient_for (GTK_WINDOW (w),
2697 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
2698 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
810f2256 2699 gtk_widget_set_name (w, "emacs-dialog");
457a8155 2700 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
f392e843 2701 }
da18b5ac 2702 else if (menu_bar_p || pop_up_p)
f392e843
JD
2703 {
2704 w = create_menus (val->contents,
2705 f,
2706 select_cb,
2707 deactivate_cb,
2708 highlight_cb,
da18b5ac
JD
2709 pop_up_p,
2710 menu_bar_p,
2711 menu_bar_p,
f392e843
JD
2712 0,
2713 0,
2714 name);
2715
2716 /* Set the cursor to an arrow for popup menus when they are mapped.
2717 This is done by default for menu bar menus. */
da18b5ac 2718 if (pop_up_p)
f392e843
JD
2719 {
2720 /* Must realize so the GdkWindow inside the widget is created. */
2721 gtk_widget_realize (w);
aad3612f 2722 xg_set_cursor (w, FRAME_DISPLAY_INFO (f)->xg_cursor);
f392e843
JD
2723 }
2724 }
2725 else
2726 {
2727 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2728 type);
2729 }
2730
2731 return w;
2732}
2733
0a1d6de0 2734/* Return the label for menu item WITEM. */
71bacd48 2735
f392e843 2736static const char *
971de7fb 2737xg_get_menu_item_label (GtkMenuItem *witem)
f392e843 2738{
4039c786 2739 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
f392e843
JD
2740 return gtk_label_get_label (wlabel);
2741}
2742
18e27ea8 2743/* Return true if the menu item WITEM has the text LABEL. */
71bacd48 2744
18e27ea8 2745static bool
42ca4633 2746xg_item_label_same_p (GtkMenuItem *witem, const char *label)
f392e843 2747{
18e27ea8 2748 bool is_same = 0;
f392e843 2749 char *utf8_label = get_utf8_string (label);
0a1d6de0
JD
2750 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2751
2752 if (! old_label && ! utf8_label)
2753 is_same = 1;
2754 else if (old_label && utf8_label)
2755 is_same = strcmp (utf8_label, old_label) == 0;
2756
42ca4633 2757 if (utf8_label) g_free (utf8_label);
f392e843
JD
2758
2759 return is_same;
2760}
2761
71bacd48
JD
2762/* Destroy widgets in LIST. */
2763
f392e843 2764static void
971de7fb 2765xg_destroy_widgets (GList *list)
f392e843 2766{
f392e843
JD
2767 GList *iter;
2768
49853a4d 2769 for (iter = list; iter; iter = g_list_next (iter))
f392e843
JD
2770 {
2771 GtkWidget *w = GTK_WIDGET (iter->data);
2772
71bacd48 2773 /* Destroying the widget will remove it from the container it is in. */
f392e843
JD
2774 gtk_widget_destroy (w);
2775 }
f392e843
JD
2776}
2777
2778/* Update the top level names in MENUBAR (i.e. not submenus).
2779 F is the frame the menu bar belongs to.
49853a4d
JD
2780 *LIST is a list with the current menu bar names (menu item widgets).
2781 ITER is the item within *LIST that shall be updated.
2782 POS is the numerical position, starting at 0, of ITER in *LIST.
f392e843
JD
2783 VAL describes what the menu bar shall look like after the update.
2784 SELECT_CB is the callback to use when a menu item is selected.
2785 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
49853a4d 2786 CL_DATA points to the callback data to be used for this menu bar.
f392e843
JD
2787
2788 This function calls itself to walk through the menu bar names. */
71bacd48 2789
f392e843 2790static void
dd4c5104 2791xg_update_menubar (GtkWidget *menubar,
a10c8269 2792 struct frame *f,
dd4c5104
DN
2793 GList **list,
2794 GList *iter,
2795 int pos,
2796 widget_value *val,
2797 GCallback select_cb,
2798 GCallback deactivate_cb,
2799 GCallback highlight_cb,
2800 xg_menu_cb_data *cl_data)
f392e843 2801{
49853a4d 2802 if (! iter && ! val)
f392e843 2803 return;
49853a4d 2804 else if (iter && ! val)
f392e843 2805 {
49853a4d 2806 /* Item(s) have been removed. Remove all remaining items. */
71bacd48 2807 xg_destroy_widgets (iter);
f392e843 2808
4a8e097d
JD
2809 /* Add a blank entry so the menubar doesn't collapse to nothing. */
2810 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2811 gtk_menu_item_new_with_label (""),
2812 0);
f392e843
JD
2813 /* All updated. */
2814 val = 0;
49853a4d 2815 iter = 0;
f392e843 2816 }
49853a4d 2817 else if (! iter && val)
f392e843
JD
2818 {
2819 /* Item(s) added. Add all new items in one call. */
d1c38b57 2820 create_menus (val, f, select_cb, deactivate_cb, highlight_cb,
f392e843
JD
2821 0, 1, 0, menubar, cl_data, 0);
2822
2823 /* All updated. */
2824 val = 0;
49853a4d 2825 iter = 0;
f392e843 2826 }
49853a4d
JD
2827 /* Below this neither iter or val is NULL */
2828 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
f392e843
JD
2829 {
2830 /* This item is still the same, check next item. */
2831 val = val->next;
49853a4d
JD
2832 iter = g_list_next (iter);
2833 ++pos;
f392e843
JD
2834 }
2835 else /* This item is changed. */
2836 {
49853a4d 2837 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
f392e843 2838 GtkMenuItem *witem2 = 0;
18e27ea8
PE
2839 bool val_in_menubar = 0;
2840 bool iter_in_new_menubar = 0;
49853a4d 2841 GList *iter2;
f392e843
JD
2842 widget_value *cur;
2843
f392e843 2844 /* See if the changed entry (val) is present later in the menu bar */
49853a4d
JD
2845 for (iter2 = iter;
2846 iter2 && ! val_in_menubar;
2847 iter2 = g_list_next (iter2))
f392e843 2848 {
49853a4d 2849 witem2 = GTK_MENU_ITEM (iter2->data);
f392e843
JD
2850 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2851 }
2852
49853a4d 2853 /* See if the current entry (iter) is present later in the
f392e843 2854 specification for the new menu bar. */
49853a4d
JD
2855 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2856 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
f392e843 2857
49853a4d 2858 if (val_in_menubar && ! iter_in_new_menubar)
f392e843 2859 {
49853a4d
JD
2860 int nr = pos;
2861
f392e843
JD
2862 /* This corresponds to:
2863 Current: A B C
2864 New: A C
2865 Remove B. */
177c0ea7 2866
e547b051 2867 g_object_ref (G_OBJECT (witem));
f392e843
JD
2868 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2869 gtk_widget_destroy (GTK_WIDGET (witem));
2870
2871 /* Must get new list since the old changed. */
49853a4d
JD
2872 g_list_free (*list);
2873 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2874 while (nr-- > 0) iter = g_list_next (iter);
f392e843 2875 }
49853a4d 2876 else if (! val_in_menubar && ! iter_in_new_menubar)
f392e843
JD
2877 {
2878 /* This corresponds to:
2879 Current: A B C
2880 New: A X C
2881 Rename B to X. This might seem to be a strange thing to do,
2882 since if there is a menu under B it will be totally wrong for X.
2883 But consider editing a C file. Then there is a C-mode menu
2884 (corresponds to B above).
2885 If then doing C-x C-f the minibuf menu (X above) replaces the
2886 C-mode menu. When returning from the minibuffer, we get
2887 back the C-mode menu. Thus we do:
2888 Rename B to X (C-mode to minibuf menu)
2889 Rename X to B (minibuf to C-mode menu).
2890 If the X menu hasn't been invoked, the menu under B
2891 is up to date when leaving the minibuffer. */
4039c786 2892 GtkLabel *wlabel = GTK_LABEL (XG_BIN_CHILD (witem));
f392e843 2893 char *utf8_label = get_utf8_string (val->name);
da18b5ac 2894 GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
177c0ea7 2895
4b1b4443 2896 gtk_label_set_text (wlabel, utf8_label);
f392e843 2897
8b745d92 2898#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
da18b5ac
JD
2899 /* If this item has a submenu that has been detached, change
2900 the title in the WM decorations also. */
2901 if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
2902 /* Set the title of the detached window. */
2903 gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
8b745d92 2904#endif
da18b5ac 2905
42ca4633 2906 if (utf8_label) g_free (utf8_label);
49853a4d 2907 iter = g_list_next (iter);
f392e843 2908 val = val->next;
49853a4d 2909 ++pos;
f392e843 2910 }
49853a4d 2911 else if (! val_in_menubar && iter_in_new_menubar)
f392e843
JD
2912 {
2913 /* This corresponds to:
2914 Current: A B C
2915 New: A X B C
2916 Insert X. */
2917
49853a4d 2918 int nr = pos;
e4c8d29a 2919 GSList *group = 0;
f392e843
JD
2920 GtkWidget *w = xg_create_one_menuitem (val,
2921 f,
2922 select_cb,
2923 highlight_cb,
2924 cl_data,
2925 &group);
2926
f56cff88
JD
2927 /* Create a possibly empty submenu for menu bar items, since some
2928 themes don't highlight items correctly without it. */
2929 GtkWidget *submenu = create_menus (NULL, f,
d1c38b57
JD
2930 select_cb, deactivate_cb,
2931 highlight_cb,
f56cff88 2932 0, 0, 0, 0, cl_data, 0);
f392e843
JD
2933 gtk_widget_set_name (w, MENU_ITEM_NAME);
2934 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
f56cff88 2935 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
f392e843 2936
49853a4d
JD
2937 g_list_free (*list);
2938 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2939 while (nr-- > 0) iter = g_list_next (iter);
2940 iter = g_list_next (iter);
f392e843 2941 val = val->next;
49853a4d 2942 ++pos;
f392e843 2943 }
49853a4d 2944 else /* if (val_in_menubar && iter_in_new_menubar) */
f392e843 2945 {
49853a4d 2946 int nr = pos;
f392e843
JD
2947 /* This corresponds to:
2948 Current: A B C
2949 New: A C B
2950 Move C before B */
2951
e547b051 2952 g_object_ref (G_OBJECT (witem2));
f392e843
JD
2953 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2954 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2955 GTK_WIDGET (witem2), pos);
e547b051 2956 g_object_unref (G_OBJECT (witem2));
f392e843 2957
49853a4d
JD
2958 g_list_free (*list);
2959 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2960 while (nr-- > 0) iter = g_list_next (iter);
0a194c92 2961 if (iter) iter = g_list_next (iter);
f392e843 2962 val = val->next;
49853a4d 2963 ++pos;
f392e843 2964 }
f392e843
JD
2965 }
2966
2967 /* Update the rest of the menu bar. */
49853a4d 2968 xg_update_menubar (menubar, f, list, iter, pos, val,
d1c38b57 2969 select_cb, deactivate_cb, highlight_cb, cl_data);
f392e843
JD
2970}
2971
2972/* Update the menu item W so it corresponds to VAL.
2973 SELECT_CB is the callback to use when a menu item is selected.
2974 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
a8ce3d17 2975 CL_DATA is the data to set in the widget for menu invocation. */
71bacd48 2976
f392e843 2977static void
e4c8d29a
J
2978xg_update_menu_item (widget_value *val,
2979 GtkWidget *w,
2980 GCallback select_cb,
2981 GCallback highlight_cb,
2982 xg_menu_cb_data *cl_data)
f392e843
JD
2983{
2984 GtkWidget *wchild;
2985 GtkLabel *wlbl = 0;
2986 GtkLabel *wkey = 0;
2987 char *utf8_label;
2988 char *utf8_key;
0a1d6de0
JD
2989 const char *old_label = 0;
2990 const char *old_key = 0;
f392e843 2991 xg_menu_item_cb_data *cb_data;
177c0ea7 2992
4039c786 2993 wchild = XG_BIN_CHILD (w);
f392e843
JD
2994 utf8_label = get_utf8_string (val->name);
2995 utf8_key = get_utf8_string (val->key);
2996
2997 /* See if W is a menu item with a key. See make_menu_item above. */
383b7c95 2998 if (GTK_IS_BOX (wchild))
f392e843
JD
2999 {
3000 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
3001
3002 wlbl = GTK_LABEL (list->data);
3003 wkey = GTK_LABEL (list->next->data);
49853a4d
JD
3004 g_list_free (list);
3005
f392e843
JD
3006 if (! utf8_key)
3007 {
3008 /* Remove the key and keep just the label. */
e547b051 3009 g_object_ref (G_OBJECT (wlbl));
f392e843
JD
3010 gtk_container_remove (GTK_CONTAINER (w), wchild);
3011 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
e547b051 3012 g_object_unref (G_OBJECT (wlbl));
f392e843
JD
3013 wkey = 0;
3014 }
49853a4d 3015
f392e843
JD
3016 }
3017 else /* Just a label. */
3018 {
3019 wlbl = GTK_LABEL (wchild);
3020
3021 /* Check if there is now a key. */
3022 if (utf8_key)
3023 {
3024 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
3025 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
49853a4d 3026
f392e843
JD
3027 wlbl = GTK_LABEL (list->data);
3028 wkey = GTK_LABEL (list->next->data);
49853a4d 3029 g_list_free (list);
f392e843
JD
3030
3031 gtk_container_remove (GTK_CONTAINER (w), wchild);
3032 gtk_container_add (GTK_CONTAINER (w), wtoadd);
3033 }
3034 }
3035
177c0ea7 3036
0a1d6de0
JD
3037 if (wkey) old_key = gtk_label_get_label (wkey);
3038 if (wlbl) old_label = gtk_label_get_label (wlbl);
177c0ea7 3039
0a1d6de0 3040 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
f392e843
JD
3041 gtk_label_set_text (wkey, utf8_key);
3042
0a1d6de0 3043 if (! old_label || strcmp (utf8_label, old_label) != 0)
4b1b4443 3044 gtk_label_set_text (wlbl, utf8_label);
f392e843 3045
42ca4633
J
3046 if (utf8_key) g_free (utf8_key);
3047 if (utf8_label) g_free (utf8_label);
177c0ea7 3048
e547b051 3049 if (! val->enabled && gtk_widget_get_sensitive (w))
f392e843 3050 gtk_widget_set_sensitive (w, FALSE);
e547b051 3051 else if (val->enabled && ! gtk_widget_get_sensitive (w))
f392e843
JD
3052 gtk_widget_set_sensitive (w, TRUE);
3053
7d652d97 3054 cb_data = g_object_get_data (G_OBJECT (w), XG_ITEM_DATA);
f392e843
JD
3055 if (cb_data)
3056 {
3057 cb_data->call_data = val->call_data;
3058 cb_data->help = val->help;
3059 cb_data->cl_data = cl_data;
177c0ea7 3060
f392e843
JD
3061 /* We assume the callback functions don't change. */
3062 if (val->call_data && ! val->contents)
3063 {
3064 /* This item shall have a select callback. */
3065 if (! cb_data->select_id)
3066 cb_data->select_id
3067 = g_signal_connect (G_OBJECT (w), "activate",
3068 select_cb, cb_data);
3069 }
3070 else if (cb_data->select_id)
3071 {
3072 g_signal_handler_disconnect (w, cb_data->select_id);
3073 cb_data->select_id = 0;
3074 }
f392e843
JD
3075 }
3076}
3077
3078/* Update the toggle menu item W so it corresponds to VAL. */
71bacd48 3079
f392e843 3080static void
971de7fb 3081xg_update_toggle_item (widget_value *val, GtkWidget *w)
f392e843
JD
3082{
3083 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3084}
3085
3086/* Update the radio menu item W so it corresponds to VAL. */
71bacd48 3087
f392e843 3088static void
971de7fb 3089xg_update_radio_item (widget_value *val, GtkWidget *w)
f392e843
JD
3090{
3091 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
3092}
3093
3094/* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
3095 SUBMENU may be NULL, in that case a new menu is created.
3096 F is the frame the menu bar belongs to.
3097 VAL describes the contents of the menu bar.
3098 SELECT_CB is the callback to use when a menu item is selected.
3099 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3100 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
3101 CL_DATA is the call back data to use for any newly created items.
3102
3103 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
3104 was NULL. */
3105
3106static GtkWidget *
dd4c5104 3107xg_update_submenu (GtkWidget *submenu,
a10c8269 3108 struct frame *f,
dd4c5104
DN
3109 widget_value *val,
3110 GCallback select_cb,
3111 GCallback deactivate_cb,
3112 GCallback highlight_cb,
3113 xg_menu_cb_data *cl_data)
f392e843
JD
3114{
3115 GtkWidget *newsub = submenu;
3116 GList *list = 0;
3117 GList *iter;
3118 widget_value *cur;
18e27ea8 3119 bool has_tearoff_p = 0;
f392e843 3120 GList *first_radio = 0;
177c0ea7 3121
f392e843
JD
3122 if (submenu)
3123 list = gtk_container_get_children (GTK_CONTAINER (submenu));
177c0ea7 3124
f392e843
JD
3125 for (cur = val, iter = list;
3126 cur && iter;
3127 iter = g_list_next (iter), cur = cur->next)
3128 {
3129 GtkWidget *w = GTK_WIDGET (iter->data);
3130
8b745d92
JD
3131#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
3132 /* Skip tearoff items, they have no counterpart in val. */
f392e843
JD
3133 if (GTK_IS_TEAROFF_MENU_ITEM (w))
3134 {
3135 has_tearoff_p = 1;
3136 iter = g_list_next (iter);
3137 if (iter) w = GTK_WIDGET (iter->data);
3138 else break;
3139 }
8b745d92 3140#endif
f392e843
JD
3141
3142 /* Remember first radio button in a group. If we get a mismatch in
3143 a radio group we must rebuild the whole group so that the connections
3144 in GTK becomes correct. */
3145 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
3146 first_radio = iter;
3147 else if (cur->button_type != BUTTON_TYPE_RADIO
3148 && ! GTK_IS_RADIO_MENU_ITEM (w))
3149 first_radio = 0;
3150
3151 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
3152 {
4039c786 3153 if (! menu_separator_name_p (cur->name))
f392e843
JD
3154 break;
3155 }
3156 else if (GTK_IS_CHECK_MENU_ITEM (w))
3157 {
3158 if (cur->button_type != BUTTON_TYPE_TOGGLE)
3159 break;
3160 xg_update_toggle_item (cur, w);
3161 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3162 }
3163 else if (GTK_IS_RADIO_MENU_ITEM (w))
3164 {
3165 if (cur->button_type != BUTTON_TYPE_RADIO)
3166 break;
3167 xg_update_radio_item (cur, w);
3168 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3169 }
3170 else if (GTK_IS_MENU_ITEM (w))
3171 {
3172 GtkMenuItem *witem = GTK_MENU_ITEM (w);
3173 GtkWidget *sub;
3174
3175 if (cur->button_type != BUTTON_TYPE_NONE ||
4039c786 3176 menu_separator_name_p (cur->name))
f392e843
JD
3177 break;
3178
3179 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
3180
3181 sub = gtk_menu_item_get_submenu (witem);
3182 if (sub && ! cur->contents)
3183 {
3184 /* Not a submenu anymore. */
e547b051
J
3185 g_object_ref (G_OBJECT (sub));
3186 remove_submenu (witem);
f392e843
JD
3187 gtk_widget_destroy (sub);
3188 }
3189 else if (cur->contents)
3190 {
3191 GtkWidget *nsub;
3192
3193 nsub = xg_update_submenu (sub, f, cur->contents,
3194 select_cb, deactivate_cb,
3195 highlight_cb, cl_data);
3196
3197 /* If this item just became a submenu, we must set it. */
3198 if (nsub != sub)
3199 gtk_menu_item_set_submenu (witem, nsub);
3200 }
3201 }
3202 else
3203 {
3204 /* Structural difference. Remove everything from here and down
3205 in SUBMENU. */
3206 break;
3207 }
3208 }
3209
22bcf204 3210 /* Remove widgets from first structural change. */
f392e843
JD
3211 if (iter)
3212 {
3213 /* If we are adding new menu items below, we must remove from
3214 first radio button so that radio groups become correct. */
71bacd48
JD
3215 if (cur && first_radio) xg_destroy_widgets (first_radio);
3216 else xg_destroy_widgets (iter);
f392e843 3217 }
177c0ea7 3218
f392e843
JD
3219 if (cur)
3220 {
3221 /* More items added. Create them. */
3222 newsub = create_menus (cur,
3223 f,
3224 select_cb,
3225 deactivate_cb,
3226 highlight_cb,
3227 0,
3228 0,
3229 ! has_tearoff_p,
3230 submenu,
3231 cl_data,
3232 0);
3233 }
177c0ea7 3234
49853a4d
JD
3235 if (list) g_list_free (list);
3236
f392e843
JD
3237 return newsub;
3238}
3239
3240/* Update the MENUBAR.
3241 F is the frame the menu bar belongs to.
3242 VAL describes the contents of the menu bar.
18e27ea8 3243 If DEEP_P, rebuild all but the top level menu names in
f392e843
JD
3244 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
3245 SELECT_CB is the callback to use when a menu item is selected.
3246 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
3247 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
71bacd48 3248
f392e843 3249void
a10c8269
DA
3250xg_modify_menubar_widgets (GtkWidget *menubar, struct frame *f,
3251 widget_value *val, bool deep_p,
d5a3eaaf
AS
3252 GCallback select_cb, GCallback deactivate_cb,
3253 GCallback highlight_cb)
f392e843
JD
3254{
3255 xg_menu_cb_data *cl_data;
3256 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
f392e843
JD
3257
3258 if (! list) return;
177c0ea7 3259
7d652d97 3260 cl_data = g_object_get_data (G_OBJECT (menubar), XG_FRAME_DATA);
f392e843 3261
da18b5ac 3262 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
d1c38b57 3263 select_cb, deactivate_cb, highlight_cb, cl_data);
da18b5ac 3264
aa41b0bf 3265 if (deep_p)
f392e843
JD
3266 {
3267 widget_value *cur;
3268
3269 /* Update all sub menus.
da18b5ac 3270 We must keep the submenus (GTK menu item widgets) since the
f392e843
JD
3271 X Window in the XEvent that activates the menu are those widgets. */
3272
3273 /* Update cl_data, menu_item things in F may have changed. */
3274 update_cl_data (cl_data, f, highlight_cb);
3275
3276 for (cur = val->contents; cur; cur = cur->next)
3277 {
49853a4d 3278 GList *iter;
f392e843
JD
3279 GtkWidget *sub = 0;
3280 GtkWidget *newsub;
e4c8d29a 3281 GtkMenuItem *witem = 0;
f392e843
JD
3282
3283 /* Find sub menu that corresponds to val and update it. */
3284 for (iter = list ; iter; iter = g_list_next (iter))
3285 {
3286 witem = GTK_MENU_ITEM (iter->data);
3287 if (xg_item_label_same_p (witem, cur->name))
3288 {
3289 sub = gtk_menu_item_get_submenu (witem);
3290 break;
3291 }
3292 }
177c0ea7 3293
f392e843
JD
3294 newsub = xg_update_submenu (sub,
3295 f,
3296 cur->contents,
3297 select_cb,
3298 deactivate_cb,
3299 highlight_cb,
3300 cl_data);
3301 /* sub may still be NULL. If we just updated non deep and added
3302 a new menu bar item, it has no sub menu yet. So we set the
3303 newly created sub menu under witem. */
e4c8d29a 3304 if (newsub != sub && witem != 0)
810f2256
JD
3305 {
3306 xg_set_screen (newsub, f);
3307 gtk_menu_item_set_submenu (witem, newsub);
3308 }
f392e843
JD
3309 }
3310 }
3311
49853a4d 3312 g_list_free (list);
f392e843
JD
3313 gtk_widget_show_all (menubar);
3314}
3315
8d7f026f
JD
3316/* Callback called when the menu bar W is mapped.
3317 Used to find the height of the menu bar if we didn't get it
3318 after showing the widget. */
3319
3320static void
3321menubar_map_cb (GtkWidget *w, gpointer user_data)
3322{
3323 GtkRequisition req;
7d652d97 3324 struct frame *f = user_data;
0afb4571 3325 gtk_widget_get_preferred_size (w, NULL, &req);
088c8c09 3326 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
8d7f026f
JD
3327 {
3328 FRAME_MENUBAR_HEIGHT (f) = req.height;
088c8c09 3329 xg_height_or_width_changed (f);
8d7f026f
JD
3330 }
3331}
3332
f392e843 3333/* Recompute all the widgets of frame F, when the menu bar has been
18e27ea8 3334 changed. */
f392e843 3335
18e27ea8 3336void
a10c8269 3337xg_update_frame_menubar (struct frame *f)
f392e843
JD
3338{
3339 struct x_output *x = f->output_data.x;
3340 GtkRequisition req;
177c0ea7 3341
e547b051 3342 if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
18e27ea8 3343 return;
f392e843 3344
473a99b7 3345 if (x->menubar_widget && gtk_widget_get_parent (x->menubar_widget))
18e27ea8 3346 return; /* Already done this, happens for frames created invisible. */
473a99b7 3347
4d7e6e51 3348 block_input ();
f392e843
JD
3349
3350 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
3351 FALSE, FALSE, 0);
3352 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
3353
8d7f026f 3354 g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
f392e843 3355 gtk_widget_show_all (x->menubar_widget);
0afb4571 3356 gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
07976ae3 3357
8d7f026f
JD
3358 /* If menu bar doesn't know its height yet, cheat a little so the frame
3359 doesn't jump so much when resized later in menubar_map_cb. */
3360 if (req.height == 0)
3361 req.height = 23;
3362
3363 if (FRAME_MENUBAR_HEIGHT (f) != req.height)
3364 {
3365 FRAME_MENUBAR_HEIGHT (f) = req.height;
07976ae3 3366 xg_height_or_width_changed (f);
8d7f026f 3367 }
4d7e6e51 3368 unblock_input ();
f392e843
JD
3369}
3370
3371/* Get rid of the menu bar of frame F, and free its storage.
3372 This is used when deleting a frame, and when turning off the menu bar. */
3373
3374void
a10c8269 3375free_frame_menubar (struct frame *f)
f392e843
JD
3376{
3377 struct x_output *x = f->output_data.x;
3378
3379 if (x->menubar_widget)
3380 {
4d7e6e51 3381 block_input ();
f392e843
JD
3382
3383 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
3384 /* The menubar and its children shall be deleted when removed from
3385 the container. */
3386 x->menubar_widget = 0;
3387 FRAME_MENUBAR_HEIGHT (f) = 0;
bfeabdc3 3388 xg_height_or_width_changed (f);
4d7e6e51 3389 unblock_input ();
f392e843
JD
3390 }
3391}
3392
18e27ea8 3393bool
b7ad2f74 3394xg_event_is_for_menubar (struct frame *f, const XEvent *event)
b78f9767
J
3395{
3396 struct x_output *x = f->output_data.x;
499322ce
J
3397 GList *iter;
3398 GdkRectangle rec;
3399 GList *list;
3400 GdkDisplay *gdpy;
3401 GdkWindow *gw;
3402 GdkEvent gevent;
3403 GtkWidget *gwdesc;
b78f9767
J
3404
3405 if (! x->menubar_widget) return 0;
3406
3407 if (! (event->xbutton.x >= 0
3408 && event->xbutton.x < FRAME_PIXEL_WIDTH (f)
3409 && event->xbutton.y >= 0
3410 && event->xbutton.y < f->output_data.x->menubar_height
3411 && event->xbutton.same_screen))
3412 return 0;
3413
499322ce 3414 gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
0afb4571 3415 gw = gdk_x11_window_lookup_for_display (gdpy, event->xbutton.window);
499322ce
J
3416 if (! gw) return 0;
3417 gevent.any.window = gw;
3b574623 3418 gevent.any.type = GDK_NOTHING;
499322ce
J
3419 gwdesc = gtk_get_event_widget (&gevent);
3420 if (! gwdesc) return 0;
3421 if (! GTK_IS_MENU_BAR (gwdesc)
3422 && ! GTK_IS_MENU_ITEM (gwdesc)
3423 && ! gtk_widget_is_ancestor (x->menubar_widget, gwdesc))
3424 return 0;
3425
3426 list = gtk_container_get_children (GTK_CONTAINER (x->menubar_widget));
b78f9767 3427 if (! list) return 0;
b78f9767
J
3428 rec.x = event->xbutton.x;
3429 rec.y = event->xbutton.y;
3430 rec.width = 1;
3431 rec.height = 1;
499322ce 3432
b78f9767
J
3433 for (iter = list ; iter; iter = g_list_next (iter))
3434 {
3435 GtkWidget *w = GTK_WIDGET (iter->data);
ce6e9d7f 3436 if (gtk_widget_get_mapped (w) && gtk_widget_intersect (w, &rec, NULL))
b78f9767
J
3437 break;
3438 }
3439 g_list_free (list);
18e27ea8 3440 return iter != 0;
b78f9767
J
3441}
3442
f392e843
JD
3443
3444\f
3445/***********************************************************************
3446 Scroll bar functions
3447 ***********************************************************************/
3448
3449
3450/* Setting scroll bar values invokes the callback. Use this variable
3451 to indicate that callback should do nothing. */
71bacd48 3452
18e27ea8 3453bool xg_ignore_gtk_scrollbar;
f392e843 3454
c195f2de
JD
3455/* The width of the scroll bar for the current theme. */
3456
3457static int scroll_bar_width_for_theme;
3458
056ce195
SM
3459/* Xlib's `Window' fits in 32 bits. But we want to store pointers, and they
3460 may be larger than 32 bits. Keep a mapping from integer index to widget
3461 pointers to get around the 32 bit limitation. */
71bacd48 3462
f392e843
JD
3463static struct
3464{
3465 GtkWidget **widgets;
0eb0f318
PE
3466 ptrdiff_t max_size;
3467 ptrdiff_t used;
81e302ef 3468} id_to_widget;
f392e843
JD
3469
3470/* Grow this much every time we need to allocate more */
71bacd48 3471
f392e843
JD
3472#define ID_TO_WIDGET_INCR 32
3473
3474/* Store the widget pointer W in id_to_widget and return the integer index. */
71bacd48 3475
0eb0f318 3476static ptrdiff_t
971de7fb 3477xg_store_widget_in_map (GtkWidget *w)
f392e843 3478{
0eb0f318 3479 ptrdiff_t i;
f392e843
JD
3480
3481 if (id_to_widget.max_size == id_to_widget.used)
3482 {
0eb0f318 3483 ptrdiff_t new_size;
0065d054 3484 if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size)
0eb0f318 3485 memory_full (SIZE_MAX);
f392e843 3486
0eb0f318 3487 new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
0065d054
PE
3488 id_to_widget.widgets = xnrealloc (id_to_widget.widgets,
3489 new_size, sizeof (GtkWidget *));
f392e843
JD
3490
3491 for (i = id_to_widget.max_size; i < new_size; ++i)
3492 id_to_widget.widgets[i] = 0;
3493 id_to_widget.max_size = new_size;
3494 }
3495
3496 /* Just loop over the array and find a free place. After all,
3497 how many scroll bars are we creating? Should be a small number.
3498 The check above guarantees we will find a free place. */
3499 for (i = 0; i < id_to_widget.max_size; ++i)
3500 {
3501 if (! id_to_widget.widgets[i])
3502 {
3503 id_to_widget.widgets[i] = w;
3504 ++id_to_widget.used;
3505
3506 return i;
3507 }
3508 }
3509
3510 /* Should never end up here */
1088b922 3511 emacs_abort ();
f392e843
JD
3512}
3513
3514/* Remove pointer at IDX from id_to_widget.
3515 Called when scroll bar is destroyed. */
71bacd48 3516
f392e843 3517static void
0eb0f318 3518xg_remove_widget_from_map (ptrdiff_t idx)
f392e843
JD
3519{
3520 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3521 {
3522 id_to_widget.widgets[idx] = 0;
3523 --id_to_widget.used;
3524 }
3525}
3526
3527/* Get the widget pointer at IDX from id_to_widget. */
71bacd48 3528
f392e843 3529static GtkWidget *
0eb0f318 3530xg_get_widget_from_map (ptrdiff_t idx)
f392e843
JD
3531{
3532 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
3533 return id_to_widget.widgets[idx];
3534
3535 return 0;
3536}
3537
c195f2de
JD
3538static void
3539update_theme_scrollbar_width (void)
a059fe24 3540{
43f862f7
AS
3541#ifdef HAVE_GTK3
3542 GtkAdjustment *vadj;
3543#else
3544 GtkObject *vadj;
3545#endif
3546 GtkWidget *wscroll;
a059fe24 3547 int w = 0, b = 0;
c195f2de 3548
43f862f7 3549 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, 0.1, 0.1, 0.1);
383b7c95 3550 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
c195f2de 3551 g_object_ref_sink (G_OBJECT (wscroll));
a059fe24
JD
3552 gtk_widget_style_get (wscroll, "slider-width", &w, "trough-border", &b, NULL);
3553 gtk_widget_destroy (wscroll);
c195f2de 3554 g_object_unref (G_OBJECT (wscroll));
a059fe24
JD
3555 w += 2*b;
3556 if (w < 16) w = 16;
c195f2de
JD
3557 scroll_bar_width_for_theme = w;
3558}
3559
3560int
3561xg_get_default_scrollbar_width (void)
3562{
3563 return scroll_bar_width_for_theme;
a059fe24
JD
3564}
3565
810f2256 3566/* Return the scrollbar id for X Window WID on display DPY.
3a8a22fc 3567 Return -1 if WID not in id_to_widget. */
71bacd48 3568
0eb0f318 3569ptrdiff_t
971de7fb 3570xg_get_scroll_id_for_window (Display *dpy, Window wid)
3a8a22fc 3571{
0eb0f318 3572 ptrdiff_t idx;
3a8a22fc
JD
3573 GtkWidget *w;
3574
810f2256 3575 w = xg_win_to_widget (dpy, wid);
3a8a22fc
JD
3576
3577 if (w)
3578 {
3579 for (idx = 0; idx < id_to_widget.max_size; ++idx)
3580 if (id_to_widget.widgets[idx] == w)
3581 return idx;
3582 }
3583
3584 return -1;
3585}
3586
f392e843
JD
3587/* Callback invoked when scroll bar WIDGET is destroyed.
3588 DATA is the index into id_to_widget for WIDGET.
cea9be54 3589 We free pointer to last scroll bar values here and remove the index. */
71bacd48 3590
f392e843 3591static void
971de7fb 3592xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data)
f392e843 3593{
0eb0f318 3594 intptr_t id = (intptr_t) data;
f392e843
JD
3595 xg_remove_widget_from_map (id);
3596}
3597
f392e843
JD
3598/* Create a scroll bar widget for frame F. Store the scroll bar
3599 in BAR.
3600 SCROLL_CALLBACK is the callback to invoke when the value of the
3601 bar changes.
e5f0bc9a 3602 END_CALLBACK is the callback to invoke when scrolling ends.
f392e843
JD
3603 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
3604 to set resources for the widget. */
71bacd48 3605
f392e843 3606void
a10c8269 3607xg_create_scroll_bar (struct frame *f,
e4c8d29a
J
3608 struct scroll_bar *bar,
3609 GCallback scroll_callback,
3610 GCallback end_callback,
675e2c69 3611 const char *scroll_bar_name)
f392e843
JD
3612{
3613 GtkWidget *wscroll;
1755a397 3614 GtkWidget *webox;
d01a7826 3615 intptr_t scroll_id;
0afb4571
J
3616#ifdef HAVE_GTK3
3617 GtkAdjustment *vadj;
3618#else
3619 GtkObject *vadj;
3620#endif
177c0ea7 3621
f392e843
JD
3622 /* Page, step increment values are not so important here, they
3623 will be corrected in x_set_toolkit_scroll_bar_thumb. */
3624 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
3625 0.1, 0.1, 0.1);
3626
383b7c95 3627 wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj));
1755a397 3628 webox = gtk_event_box_new ();
f392e843 3629 gtk_widget_set_name (wscroll, scroll_bar_name);
0afb4571 3630#ifndef HAVE_GTK3
f392e843 3631 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
0afb4571 3632#endif
e5f0bc9a 3633 g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);
177c0ea7 3634
f392e843 3635 scroll_id = xg_store_widget_in_map (wscroll);
177c0ea7 3636
f392e843
JD
3637 g_signal_connect (G_OBJECT (wscroll),
3638 "destroy",
3639 G_CALLBACK (xg_gtk_scroll_destroy),
8ac068ac 3640 (gpointer) scroll_id);
e5f0bc9a
JD
3641 g_signal_connect (G_OBJECT (wscroll),
3642 "change-value",
3643 scroll_callback,
3644 (gpointer) bar);
e8d7886a
JD
3645 g_signal_connect (G_OBJECT (wscroll),
3646 "button-release-event",
e5f0bc9a 3647 end_callback,
e8d7886a 3648 (gpointer) bar);
088c8c09 3649
1755a397
JD
3650 /* The scroll bar widget does not draw on a window of its own. Instead
3651 it draws on the parent window, in this case the edit widget. So
3652 whenever the edit widget is cleared, the scroll bar needs to redraw
3653 also, which causes flicker. Put an event box between the edit widget
3654 and the scroll bar, so the scroll bar instead draws itself on the
3655 event box window. */
3656 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
3657 gtk_container_add (GTK_CONTAINER (webox), wscroll);
c43923ad 3658
f392e843
JD
3659
3660 /* Set the cursor to an arrow. */
aad3612f 3661 xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
f392e843 3662
056ce195 3663 bar->x_window = scroll_id;
f392e843
JD
3664}
3665
f392e843 3666/* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
71bacd48 3667
f392e843 3668void
a10c8269 3669xg_remove_scroll_bar (struct frame *f, ptrdiff_t scrollbar_id)
f392e843
JD
3670{
3671 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
3672 if (w)
3673 {
1755a397 3674 GtkWidget *wparent = gtk_widget_get_parent (w);
f392e843 3675 gtk_widget_destroy (w);
1755a397 3676 gtk_widget_destroy (wparent);
f392e843
JD
3677 SET_FRAME_GARBAGED (f);
3678 }
3679}
3680
f392e843
JD
3681/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
3682 in frame F.
3683 TOP/LEFT are the new pixel positions where the bar shall appear.
3684 WIDTH, HEIGHT is the size in pixels the bar shall have. */
71bacd48 3685
f392e843 3686void
a10c8269 3687xg_update_scrollbar_pos (struct frame *f,
0eb0f318 3688 ptrdiff_t scrollbar_id,
e4c8d29a
J
3689 int top,
3690 int left,
3691 int width,
3692 int height)
f392e843 3693{
f392e843 3694
49853a4d 3695 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
7863d625 3696
49853a4d
JD
3697 if (wscroll)
3698 {
cea9be54 3699 GtkWidget *wfixed = f->output_data.x->edit_widget;
1755a397 3700 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
8c079ebb 3701 gint msl;
7c583cd8
JD
3702
3703 /* Clear out old position. */
7c583cd8 3704 int oldx = -1, oldy = -1, oldw, oldh;
e547b051
J
3705 if (gtk_widget_get_parent (wparent) == wfixed)
3706 {
3707 gtk_container_child_get (GTK_CONTAINER (wfixed), wparent,
3708 "x", &oldx, "y", &oldy, NULL);
3709 gtk_widget_get_size_request (wscroll, &oldw, &oldh);
3710 }
7863d625 3711
ab2d724b 3712 /* Move and resize to new values. */
1755a397 3713 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
bc869eca
JD
3714 gtk_widget_style_get (wscroll, "min-slider-length", &msl, NULL);
3715 if (msl > height)
3716 {
3717 /* No room. Hide scroll bar as some themes output a warning if
3718 the height is less than the min size. */
3719 gtk_widget_hide (wparent);
3720 gtk_widget_hide (wscroll);
3721 }
3722 else
3723 {
3724 gtk_widget_show_all (wparent);
3725 gtk_widget_set_size_request (wscroll, width, height);
3726 }
7c583cd8 3727 gtk_widget_queue_draw (wfixed);
817d354b 3728 gdk_window_process_all_updates ();
bc869eca 3729 if (oldx != -1 && oldw > 0 && oldh > 0)
1f5cf200
DA
3730 /* Clear under old scroll bar position. This must be done after
3731 the gtk_widget_queue_draw and gdk_window_process_all_updates
3732 above. */
3733 x_clear_area (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
3734 oldx, oldy, oldw, oldh);
088c8c09 3735
817d354b
JD
3736 /* GTK does not redraw until the main loop is entered again, but
3737 if there are no X events pending we will not enter it. So we sync
3738 here to get some events. */
088c8c09 3739
817d354b 3740 x_sync (f);
49853a4d
JD
3741 SET_FRAME_GARBAGED (f);
3742 cancel_mouse_face (f);
3743 }
f392e843
JD
3744}
3745
c195f2de
JD
3746/* Get the current value of the range, truncated to an integer. */
3747
3748static int
3749int_gtk_range_get_value (GtkRange *range)
3750{
3751 return gtk_range_get_value (range);
3752}
3753
3754
f392e843
JD
3755/* Set the thumb size and position of scroll bar BAR. We are currently
3756 displaying PORTION out of a whole WHOLE, and our position POSITION. */
71bacd48 3757
f392e843 3758void
e4c8d29a
J
3759xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
3760 int portion,
3761 int position,
3762 int whole)
f392e843 3763{
056ce195 3764 GtkWidget *wscroll = xg_get_widget_from_map (bar->x_window);
f392e843 3765
a10c8269 3766 struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
f392e843 3767
d1fc6c21 3768 if (wscroll && bar->dragging == -1)
f392e843
JD
3769 {
3770 GtkAdjustment *adj;
3771 gdouble shown;
3772 gdouble top;
3773 int size, value;
6048fb2a 3774 int old_size;
7863d625 3775 int new_step;
18e27ea8 3776 bool changed = 0;
177c0ea7 3777
f392e843
JD
3778 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3779
ec782c5f
JD
3780 if (scroll_bar_adjust_thumb_portion_p)
3781 {
3782 /* We do the same as for MOTIF in xterm.c, use 30 chars per
3783 line rather than the real portion value. This makes the
3784 thumb less likely to resize and that looks better. */
3785 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
3786
3787 /* When the thumb is at the bottom, position == whole.
3788 So we need to increase `whole' to make space for the thumb. */
3789 whole += portion;
3790 }
17097258 3791
f392e843
JD
3792 if (whole <= 0)
3793 top = 0, shown = 1;
3794 else
3795 {
f392e843 3796 top = (gdouble) position / whole;
7863d625 3797 shown = (gdouble) portion / whole;
f392e843
JD
3798 }
3799
2dd61a9b
DA
3800 size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
3801 value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
f392e843 3802
7863d625 3803 /* Assume all lines are of equal size. */
be786000 3804 new_step = size / max (1, FRAME_LINES (f));
2a2071c3 3805
6048fb2a
PE
3806 old_size = gtk_adjustment_get_page_size (adj);
3807 if (old_size != size)
3808 {
3809 int old_step = gtk_adjustment_get_step_increment (adj);
3810 if (old_step != new_step)
3811 {
3812 gtk_adjustment_set_page_size (adj, size);
3813 gtk_adjustment_set_step_increment (adj, new_step);
3814 /* Assume a page increment is about 95% of the page size */
5f8f9cc2 3815 gtk_adjustment_set_page_increment (adj, size - size / 20);
6048fb2a
PE
3816 changed = 1;
3817 }
3818 }
17097258 3819
1e5524e7 3820 if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
7863d625 3821 {
4d7e6e51 3822 block_input ();
cea9be54 3823
7863d625
JD
3824 /* gtk_range_set_value invokes the callback. Set
3825 ignore_gtk_scrollbar to make the callback do nothing */
3826 xg_ignore_gtk_scrollbar = 1;
f392e843 3827
1e5524e7 3828 if (int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
7863d625
JD
3829 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3830 else if (changed)
3831 gtk_adjustment_changed (adj);
3832
3833 xg_ignore_gtk_scrollbar = 0;
3834
4d7e6e51 3835 unblock_input ();
7863d625
JD
3836 }
3837 }
f392e843
JD
3838}
3839
18e27ea8 3840/* Return true if EVENT is for a scroll bar in frame F.
e511451f
JD
3841 When the same X window is used for several Gtk+ widgets, we cannot
3842 say for sure based on the X window alone if an event is for the
18e27ea8 3843 frame. This function does additional checks. */
e511451f 3844
18e27ea8 3845bool
b7ad2f74 3846xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
e511451f 3847{
18e27ea8 3848 bool retval = 0;
e511451f 3849
e5f0bc9a 3850 if (f && event->type == ButtonPress && event->xbutton.button < 4)
e511451f
JD
3851 {
3852 /* Check if press occurred outside the edit widget. */
3853 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
383b7c95
JD
3854 GdkWindow *gwin;
3855#ifdef HAVE_GTK3
3856 GdkDevice *gdev = gdk_device_manager_get_client_pointer
3857 (gdk_display_get_device_manager (gdpy));
bdd091e4 3858 gwin = gdk_device_get_window_at_position (gdev, NULL, NULL);
383b7c95
JD
3859#else
3860 gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
3861#endif
3862 retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
e511451f 3863 }
e5f0bc9a
JD
3864 else if (f
3865 && ((event->type == ButtonRelease && event->xbutton.button < 4)
3866 || event->type == MotionNotify))
e511451f
JD
3867 {
3868 /* If we are releasing or moving the scroll bar, it has the grab. */
3bb49aaf
JD
3869 GtkWidget *w = gtk_grab_get_current ();
3870 retval = w != 0 && GTK_IS_SCROLLBAR (w);
e511451f 3871 }
088c8c09 3872
e511451f
JD
3873 return retval;
3874}
3875
3876
f392e843
JD
3877\f
3878/***********************************************************************
3879 Tool bar functions
3880 ***********************************************************************/
3881/* The key for the data we put in the GtkImage widgets. The data is
3882 the image used by Emacs. We use this to see if we need to update
3883 the GtkImage with a new image. */
3884#define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
3885
2334f9e7
JD
3886/* The key for storing the latest modifiers so the activate callback can
3887 get them. */
3888#define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
3889
1e0fe298
JD
3890/* The key for storing the button widget in its proxy menu item. */
3891#define XG_TOOL_BAR_PROXY_BUTTON "emacs-tool-bar-proxy-button"
2334f9e7 3892
98a92193
JD
3893/* The key for the data we put in the GtkImage widgets. The data is
3894 the stock name used by Emacs. We use this to see if we need to update
3895 the GtkImage with a new image. */
3896#define XG_TOOL_BAR_STOCK_NAME "emacs-tool-bar-stock-name"
3897
2154c964
JD
3898/* As above, but this is used for named theme widgets, as opposed to
3899 stock items. */
3900#define XG_TOOL_BAR_ICON_NAME "emacs-tool-bar-icon-name"
3901
98a92193
JD
3902/* Callback function invoked when a tool bar item is pressed.
3903 W is the button widget in the tool bar that got pressed,
3904 CLIENT_DATA is an integer that is the index of the button in the
3905 tool bar. 0 is the first button. */
3906
2334f9e7 3907static gboolean
e4c8d29a
J
3908xg_tool_bar_button_cb (GtkWidget *widget,
3909 GdkEventButton *event,
3910 gpointer user_data)
2334f9e7 3911{
d01a7826 3912 intptr_t state = event->state;
8ac068ac 3913 gpointer ptr = (gpointer) state;
6e1440e6 3914 g_object_set_data (G_OBJECT (widget), XG_TOOL_BAR_LAST_MODIFIER, ptr);
6d45d2a0 3915 return FALSE;
2334f9e7
JD
3916}
3917
3918
6e1440e6
JD
3919/* Callback function invoked when a tool bar item is pressed.
3920 W is the button widget in the tool bar that got pressed,
3921 CLIENT_DATA is an integer that is the index of the button in the
3922 tool bar. 0 is the first button. */
3923
f392e843 3924static void
971de7fb 3925xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
f392e843 3926{
d01a7826 3927 intptr_t idx = (intptr_t) client_data;
1e5524e7 3928 gpointer gmod = g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
d01a7826 3929 intptr_t mod = (intptr_t) gmod;
2334f9e7 3930
7d652d97 3931 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
f392e843
JD
3932 Lisp_Object key, frame;
3933 struct input_event event;
aa4ac494 3934 EVENT_INIT (event);
f392e843 3935
e69b0960 3936 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
f392e843
JD
3937 return;
3938
3939 idx *= TOOL_BAR_ITEM_NSLOTS;
177c0ea7 3940
e69b0960 3941 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
f392e843 3942 XSETFRAME (frame, f);
991bde0d
JD
3943
3944 /* We generate two events here. The first one is to set the prefix
3945 to `(tool_bar)', see keyboard.c. */
f392e843
JD
3946 event.kind = TOOL_BAR_EVENT;
3947 event.frame_or_window = frame;
8e5a8840
JB
3948 event.arg = frame;
3949 kbd_buffer_store_event (&event);
3950
3951 event.kind = TOOL_BAR_EVENT;
991bde0d 3952 event.frame_or_window = frame;
f392e843 3953 event.arg = key;
2334f9e7 3954 /* Convert between the modifier bits GDK uses and the modifier bits
4a02423f 3955 Emacs uses. This assumes GDK and X masks are the same, which they are when
2334f9e7 3956 this is written. */
aad3612f 3957 event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
f392e843 3958 kbd_buffer_store_event (&event);
088c8c09 3959
fcd42c11
DA
3960 /* Return focus to the frame after we have clicked on a detached
3961 tool bar button. */
3962 x_focus_frame (f);
f392e843
JD
3963}
3964
1e0fe298
JD
3965/* Callback function invoked when a tool bar item is pressed in a detached
3966 tool bar or the overflow drop down menu.
3967 We just call xg_tool_bar_callback.
3968 W is the menu item widget that got pressed,
3969 CLIENT_DATA is an integer that is the index of the button in the
3970 tool bar. 0 is the first button. */
3971
3972static void
971de7fb 3973xg_tool_bar_proxy_callback (GtkWidget *w, gpointer client_data)
1e0fe298
JD
3974{
3975 GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
3976 XG_TOOL_BAR_PROXY_BUTTON));
3977 xg_tool_bar_callback (wbutton, client_data);
3978}
3979
69b16610
JD
3980
3981static gboolean
f57e2426
J
3982xg_tool_bar_help_callback (GtkWidget *w,
3983 GdkEventCrossing *event,
3984 gpointer client_data);
69b16610
JD
3985
3986/* This callback is called when a help is to be shown for an item in
3987 the detached tool bar when the detached tool bar it is not expanded. */
3988
3989static gboolean
e4c8d29a
J
3990xg_tool_bar_proxy_help_callback (GtkWidget *w,
3991 GdkEventCrossing *event,
3992 gpointer client_data)
69b16610
JD
3993{
3994 GtkWidget *wbutton = GTK_WIDGET (g_object_get_data (G_OBJECT (w),
3995 XG_TOOL_BAR_PROXY_BUTTON));
088c8c09 3996
eba5eb94 3997 return xg_tool_bar_help_callback (wbutton, event, client_data);
69b16610
JD
3998}
3999
e547b051
J
4000static GtkWidget *
4001xg_get_tool_bar_widgets (GtkWidget *vb, GtkWidget **wimage)
4002{
4003 GList *clist = gtk_container_get_children (GTK_CONTAINER (vb));
7d652d97
PE
4004 GtkWidget *c1 = clist->data;
4005 GtkWidget *c2 = clist->next ? clist->next->data : NULL;
3afff00e 4006
e547b051
J
4007 *wimage = GTK_IS_IMAGE (c1) ? c1 : c2;
4008 g_list_free (clist);
4009 return GTK_IS_LABEL (c1) ? c1 : c2;
4010}
4011
69b16610 4012
1e0fe298
JD
4013/* This callback is called when a tool item should create a proxy item,
4014 such as for the overflow menu. Also called when the tool bar is detached.
4015 If we don't create a proxy menu item, the detached tool bar will be
4016 blank. */
4017
4018static gboolean
971de7fb 4019xg_tool_bar_menu_proxy (GtkToolItem *toolitem, gpointer user_data)
1e0fe298 4020{
4039c786
CY
4021 GtkButton *wbutton = GTK_BUTTON (XG_BIN_CHILD (XG_BIN_CHILD (toolitem)));
4022 GtkWidget *vb = XG_BIN_CHILD (wbutton);
e547b051
J
4023 GtkWidget *c1;
4024 GtkLabel *wlbl = GTK_LABEL (xg_get_tool_bar_widgets (vb, &c1));
4025 GtkImage *wimage = GTK_IMAGE (c1);
f904c0f9 4026 GtkWidget *wmenuitem = gtk_image_menu_item_new_with_label
42f60557 4027 (wlbl ? gtk_label_get_text (wlbl) : "");
1e0fe298
JD
4028 GtkWidget *wmenuimage;
4029
e547b051 4030
8e5a8840 4031 if (gtk_button_get_use_stock (wbutton))
1e0fe298
JD
4032 wmenuimage = gtk_image_new_from_stock (gtk_button_get_label (wbutton),
4033 GTK_ICON_SIZE_MENU);
4034 else
4035 {
1e0fe298
JD
4036 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (wbutton));
4037 GtkImageType store_type = gtk_image_get_storage_type (wimage);
e624c3f9 4038
2b5b82db
J
4039 g_object_set (G_OBJECT (settings), "gtk-menu-images", TRUE, NULL);
4040
1e0fe298
JD
4041 if (store_type == GTK_IMAGE_STOCK)
4042 {
4043 gchar *stock_id;
4044 gtk_image_get_stock (wimage, &stock_id, NULL);
4045 wmenuimage = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
4046 }
4047 else if (store_type == GTK_IMAGE_ICON_SET)
4048 {
4049 GtkIconSet *icon_set;
4050 gtk_image_get_icon_set (wimage, &icon_set, NULL);
4051 wmenuimage = gtk_image_new_from_icon_set (icon_set,
4052 GTK_ICON_SIZE_MENU);
4053 }
4054 else if (store_type == GTK_IMAGE_PIXBUF)
4055 {
4056 gint width, height;
8e5a8840 4057
1e0fe298
JD
4058 if (settings &&
4059 gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
4060 &width, &height))
4061 {
4062 GdkPixbuf *src_pixbuf, *dest_pixbuf;
4063
4064 src_pixbuf = gtk_image_get_pixbuf (wimage);
4065 dest_pixbuf = gdk_pixbuf_scale_simple (src_pixbuf, width, height,
4066 GDK_INTERP_BILINEAR);
4067
4068 wmenuimage = gtk_image_new_from_pixbuf (dest_pixbuf);
4069 }
e624c3f9
JD
4070 else
4071 {
4072 fprintf (stderr, "internal error: GTK_IMAGE_PIXBUF failed\n");
1088b922 4073 emacs_abort ();
e624c3f9
JD
4074 }
4075 }
8e5a8840 4076 else if (store_type == GTK_IMAGE_ICON_NAME)
e624c3f9
JD
4077 {
4078 const gchar *icon_name;
4079 GtkIconSize icon_size;
4080
4081 gtk_image_get_icon_name (wimage, &icon_name, &icon_size);
4082 wmenuimage = gtk_image_new_from_icon_name (icon_name,
4083 GTK_ICON_SIZE_MENU);
4084 }
4085 else
4086 {
4087 fprintf (stderr, "internal error: store_type is %d\n", store_type);
1088b922 4088 emacs_abort ();
1e0fe298
JD
4089 }
4090 }
4091 if (wmenuimage)
4092 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (wmenuitem), wmenuimage);
4093
4094 g_signal_connect (G_OBJECT (wmenuitem),
4095 "activate",
dc2933eb 4096 G_CALLBACK (xg_tool_bar_proxy_callback),
1e0fe298
JD
4097 user_data);
4098
088c8c09 4099
1e0fe298
JD
4100 g_object_set_data (G_OBJECT (wmenuitem), XG_TOOL_BAR_PROXY_BUTTON,
4101 (gpointer) wbutton);
4102 gtk_tool_item_set_proxy_menu_item (toolitem, "Emacs toolbar item", wmenuitem);
e547b051
J
4103 gtk_widget_set_sensitive (wmenuitem,
4104 gtk_widget_get_sensitive (GTK_WIDGET (wbutton)));
69b16610
JD
4105
4106 /* Use enter/leave notify to show help. We use the events
4107 rather than the GtkButton specific signals "enter" and
4108 "leave", so we can have only one callback. The event
4109 will tell us what kind of event it is. */
4110 g_signal_connect (G_OBJECT (wmenuitem),
4111 "enter-notify-event",
4112 G_CALLBACK (xg_tool_bar_proxy_help_callback),
4113 user_data);
4114 g_signal_connect (G_OBJECT (wmenuitem),
4115 "leave-notify-event",
4116 G_CALLBACK (xg_tool_bar_proxy_help_callback),
4117 user_data);
1e0fe298
JD
4118
4119 return TRUE;
4120}
4121
f392e843
JD
4122/* This callback is called when a tool bar is detached. We must set
4123 the height of the tool bar to zero when this happens so frame sizes
4124 are correctly calculated.
4125 WBOX is the handle box widget that enables detach/attach of the tool bar.
4126 W is the tool bar widget.
4127 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
71bacd48 4128
f392e843 4129static void
e4c8d29a
J
4130xg_tool_bar_detach_callback (GtkHandleBox *wbox,
4131 GtkWidget *w,
4132 gpointer client_data)
f392e843 4133{
7d652d97 4134 struct frame *f = client_data;
0f340cab
JD
4135
4136 g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
4137 NULL);
f392e843
JD
4138
4139 if (f)
4140 {
bfeabdc3 4141 GtkRequisition req, req2;
cd78d9b1 4142
0afb4571
J
4143 gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
4144 gtk_widget_get_preferred_size (w, NULL, &req2);
bfeabdc3
JD
4145 req.width -= req2.width;
4146 req.height -= req2.height;
4147 if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
4148 FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
4149 else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
4150 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
4151 else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
4152 FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
4153 else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
4154 FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
4155 xg_height_or_width_changed (f);
f392e843
JD
4156 }
4157}
4158
4159/* This callback is called when a tool bar is reattached. We must set
4160 the height of the tool bar when this happens so frame sizes
4161 are correctly calculated.
4162 WBOX is the handle box widget that enables detach/attach of the tool bar.
4163 W is the tool bar widget.
4164 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
71bacd48 4165
f392e843 4166static void
e4c8d29a
J
4167xg_tool_bar_attach_callback (GtkHandleBox *wbox,
4168 GtkWidget *w,
4169 gpointer client_data)
f392e843 4170{
7d652d97 4171 struct frame *f = client_data;
0f340cab 4172 g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
f392e843
JD
4173
4174 if (f)
4175 {
bfeabdc3 4176 GtkRequisition req, req2;
cd78d9b1 4177
0afb4571
J
4178 gtk_widget_get_preferred_size (GTK_WIDGET (wbox), NULL, &req);
4179 gtk_widget_get_preferred_size (w, NULL, &req2);
bfeabdc3
JD
4180 req.width += req2.width;
4181 req.height += req2.height;
4182 if (FRAME_TOOLBAR_TOP_HEIGHT (f) != 0)
4183 FRAME_TOOLBAR_TOP_HEIGHT (f) = req.height;
4184 else if (FRAME_TOOLBAR_BOTTOM_HEIGHT (f) != 0)
4185 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = req.height;
4186 else if (FRAME_TOOLBAR_RIGHT_WIDTH (f) != 0)
4187 FRAME_TOOLBAR_RIGHT_WIDTH (f) = req.width;
4188 else if (FRAME_TOOLBAR_LEFT_WIDTH (f) != 0)
4189 FRAME_TOOLBAR_LEFT_WIDTH (f) = req.width;
4190 xg_height_or_width_changed (f);
f392e843
JD
4191 }
4192}
4193
4194/* This callback is called when the mouse enters or leaves a tool bar item.
4195 It is used for displaying and hiding the help text.
4196 W is the tool bar item, a button.
4197 EVENT is either an enter event or leave event.
4198 CLIENT_DATA is an integer that is the index of the button in the
4199 tool bar. 0 is the first button.
4200
4201 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 4202
f392e843 4203static gboolean
e4c8d29a
J
4204xg_tool_bar_help_callback (GtkWidget *w,
4205 GdkEventCrossing *event,
4206 gpointer client_data)
f392e843 4207{
d01a7826 4208 intptr_t idx = (intptr_t) client_data;
7d652d97 4209 struct frame *f = g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
f392e843
JD
4210 Lisp_Object help, frame;
4211
e69b0960 4212 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
d9301435 4213 return FALSE;
f392e843
JD
4214
4215 if (event->type == GDK_ENTER_NOTIFY)
4216 {
4217 idx *= TOOL_BAR_ITEM_NSLOTS;
e69b0960 4218 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
f392e843
JD
4219
4220 if (NILP (help))
e69b0960 4221 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
f392e843
JD
4222 }
4223 else
4224 help = Qnil;
4225
4226 XSETFRAME (frame, f);
4227 kbd_buffer_store_help_event (frame, help);
4228
4229 return FALSE;
4230}
4231
4232
f098b121
JD
4233/* This callback is called when a tool bar item shall be redrawn.
4234 It modifies the expose event so that the GtkImage widget redraws the
4235 whole image. This to overcome a bug that makes GtkImage draw the image
4236 in the wrong place when it tries to redraw just a part of the image.
4237 W is the GtkImage to be redrawn.
4238 EVENT is the expose event for W.
4239 CLIENT_DATA is unused.
4240
4241 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 4242
0afb4571 4243#ifndef HAVE_GTK3
f098b121 4244static gboolean
e4c8d29a
J
4245xg_tool_bar_item_expose_callback (GtkWidget *w,
4246 GdkEventExpose *event,
4247 gpointer client_data)
f098b121 4248{
b676f356
JD
4249 gint width, height;
4250
4251 gdk_drawable_get_size (event->window, &width, &height);
b676f356
JD
4252 event->area.x -= width > event->area.width ? width-event->area.width : 0;
4253 event->area.y -= height > event->area.height ? height-event->area.height : 0;
4254
810f2256
JD
4255 event->area.x = max (0, event->area.x);
4256 event->area.y = max (0, event->area.y);
c43923ad 4257
b676f356
JD
4258 event->area.width = max (width, event->area.width);
4259 event->area.height = max (height, event->area.height);
c43923ad 4260
f098b121
JD
4261 return FALSE;
4262}
0afb4571 4263#endif
f098b121 4264
bfeabdc3
JD
4265#ifdef HAVE_GTK_ORIENTABLE_SET_ORIENTATION
4266#define toolbar_set_orientation(w, o) \
4267 gtk_orientable_set_orientation (GTK_ORIENTABLE (w), o)
4268#else
4269#define toolbar_set_orientation(w, o) \
4270 gtk_toolbar_set_orientation (GTK_TOOLBAR (w), o)
4271#endif
4272
5a1d858b
JD
4273#ifdef HAVE_GTK_HANDLE_BOX_NEW
4274#define TOOLBAR_TOP_WIDGET(x) ((x)->handlebox_widget)
4275#else
4276#define TOOLBAR_TOP_WIDGET(x) ((x)->toolbar_widget)
4277#endif
4278
1e39cbfb 4279/* Attach a tool bar to frame F. */
71bacd48 4280
f392e843 4281static void
a10c8269 4282xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
f392e843
JD
4283{
4284 struct x_output *x = f->output_data.x;
18e27ea8 4285 bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
5a1d858b 4286 GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
177c0ea7 4287
bfeabdc3
JD
4288 toolbar_set_orientation (x->toolbar_widget,
4289 into_hbox
4290 ? GTK_ORIENTATION_VERTICAL
4291 : GTK_ORIENTATION_HORIZONTAL);
5a1d858b 4292#ifdef HAVE_GTK_HANDLE_BOX_NEW
bfeabdc3
JD
4293 if (!x->handlebox_widget)
4294 {
5a1d858b 4295 top_widget = x->handlebox_widget = gtk_handle_box_new ();
bfeabdc3
JD
4296 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
4297 G_CALLBACK (xg_tool_bar_detach_callback), f);
4298 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
4299 G_CALLBACK (xg_tool_bar_attach_callback), f);
4300 gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
4301 x->toolbar_widget);
4302 }
5a1d858b 4303#endif
4721152c 4304
088c8c09 4305 if (into_hbox)
bfeabdc3 4306 {
5a1d858b 4307#ifdef HAVE_GTK_HANDLE_BOX_NEW
318a04c6
J
4308 gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
4309 GTK_POS_TOP);
5a1d858b
JD
4310#endif
4311 gtk_box_pack_start (GTK_BOX (x->hbox_widget), top_widget,
bfeabdc3
JD
4312 FALSE, FALSE, 0);
4313
4314 if (EQ (pos, Qleft))
4315 gtk_box_reorder_child (GTK_BOX (x->hbox_widget),
5a1d858b 4316 top_widget,
bfeabdc3 4317 0);
5a1d858b 4318 x->toolbar_in_hbox = true;
bfeabdc3
JD
4319 }
4320 else
4321 {
18e27ea8 4322 bool vbox_pos = x->menubar_widget != 0;
5a1d858b 4323#ifdef HAVE_GTK_HANDLE_BOX_NEW
318a04c6
J
4324 gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (x->handlebox_widget),
4325 GTK_POS_LEFT);
5a1d858b
JD
4326#endif
4327 gtk_box_pack_start (GTK_BOX (x->vbox_widget), top_widget,
bfeabdc3
JD
4328 FALSE, FALSE, 0);
4329
4330 if (EQ (pos, Qtop))
4331 gtk_box_reorder_child (GTK_BOX (x->vbox_widget),
5a1d858b 4332 top_widget,
bfeabdc3 4333 vbox_pos);
5a1d858b 4334 x->toolbar_in_hbox = false;
bfeabdc3 4335 }
5a1d858b 4336 x->toolbar_is_packed = true;
1e39cbfb
JD
4337}
4338
a10c8269 4339static bool xg_update_tool_bar_sizes (struct frame *f);
51b3a99c
JD
4340
4341static void
4342tb_size_cb (GtkWidget *widget,
4343 GdkRectangle *allocation,
4344 gpointer user_data)
4345{
4346 /* When tool bar is created it has one preferred size. But when size is
4347 allocated between widgets, it may get another. So we must update
4348 size hints if tool bar size changes. Seen on Fedora 18 at least. */
7d652d97 4349 struct frame *f = user_data;
51b3a99c
JD
4350 if (xg_update_tool_bar_sizes (f))
4351 x_wm_set_size_hint (f, 0, 0);
4352}
4353
1e39cbfb
JD
4354/* Create a tool bar for frame F. */
4355
4356static void
a10c8269 4357xg_create_tool_bar (struct frame *f)
1e39cbfb
JD
4358{
4359 struct x_output *x = f->output_data.x;
c9adfeaa
SF
4360#if GTK_CHECK_VERSION (3, 3, 6)
4361 GtkStyleContext *gsty;
4362#endif
ca06f160
JD
4363 struct xg_frame_tb_info *tbinfo
4364 = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4365 TB_INFO_KEY);
4366 if (! tbinfo)
4367 {
4368 tbinfo = xmalloc (sizeof (*tbinfo));
4369 tbinfo->last_tool_bar = Qnil;
4370 tbinfo->style = Qnil;
4371 tbinfo->hmargin = tbinfo->vmargin = 0;
4372 tbinfo->dir = GTK_TEXT_DIR_NONE;
4373 tbinfo->n_last_items = 0;
4374 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4375 TB_INFO_KEY,
4376 tbinfo);
4377 }
1e39cbfb
JD
4378
4379 x->toolbar_widget = gtk_toolbar_new ();
f392e843 4380
f098b121
JD
4381 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
4382
f098b121 4383 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
8a52f00a 4384 toolbar_set_orientation (x->toolbar_widget, GTK_ORIENTATION_HORIZONTAL);
51b3a99c
JD
4385 g_signal_connect (x->toolbar_widget, "size-allocate",
4386 G_CALLBACK (tb_size_cb), f);
c9adfeaa
SF
4387#if GTK_CHECK_VERSION (3, 3, 6)
4388 gsty = gtk_widget_get_style_context (x->toolbar_widget);
4389 gtk_style_context_add_class (gsty, "primary-toolbar");
4390#endif
1e39cbfb 4391}
f098b121 4392
177c0ea7 4393
e69b0960 4394#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
f392e843 4395
98a92193
JD
4396/* Find the right-to-left image named by RTL in the tool bar images for F.
4397 Returns IMAGE if RTL is not found. */
4398
4399static Lisp_Object
a10c8269 4400find_rtl_image (struct frame *f, Lisp_Object image, Lisp_Object rtl)
98a92193
JD
4401{
4402 int i;
4403 Lisp_Object file, rtl_name;
4404 struct gcpro gcpro1, gcpro2;
4405 GCPRO2 (file, rtl_name);
4406
4407 rtl_name = Ffile_name_nondirectory (rtl);
4408
4409 for (i = 0; i < f->n_tool_bar_items; ++i)
4410 {
4411 Lisp_Object rtl_image = PROP (TOOL_BAR_ITEM_IMAGES);
8e5a8840 4412 if (!NILP (file = file_for_image (rtl_image)))
98a92193
JD
4413 {
4414 file = call1 (intern ("file-name-sans-extension"),
4415 Ffile_name_nondirectory (file));
67b77c0b 4416 if (! NILP (Fequal (file, rtl_name)))
98a92193
JD
4417 {
4418 image = rtl_image;
4419 break;
4420 }
4421 }
4422 }
4423
4424 return image;
4425}
4426
f904c0f9 4427static GtkToolItem *
a10c8269 4428xg_make_tool_item (struct frame *f,
f904c0f9
JD
4429 GtkWidget *wimage,
4430 GtkWidget **wbutton,
8ea90aa3 4431 const char *label,
18e27ea8 4432 int i, bool horiz, bool text_image)
f904c0f9
JD
4433{
4434 GtkToolItem *ti = gtk_tool_item_new ();
383b7c95
JD
4435 GtkWidget *vb = gtk_box_new (horiz
4436 ? GTK_ORIENTATION_HORIZONTAL
4437 : GTK_ORIENTATION_VERTICAL,
4438 0);
f904c0f9 4439 GtkWidget *wb = gtk_button_new ();
115b96bd 4440 /* The eventbox is here so we can have tooltips on disabled items. */
f904c0f9 4441 GtkWidget *weventbox = gtk_event_box_new ();
c9adfeaa
SF
4442#if GTK_CHECK_VERSION (3, 3, 6)
4443 GtkCssProvider *css_prov = gtk_css_provider_new ();
4444 GtkStyleContext *gsty;
4445
4446 gtk_css_provider_load_from_data (css_prov,
4447 "GtkEventBox {"
4448 " background-color: transparent;"
4449 "}",
4450 -1, NULL);
4451
4452 gsty = gtk_widget_get_style_context (weventbox);
4453 gtk_style_context_add_provider (gsty,
4454 GTK_STYLE_PROVIDER (css_prov),
4455 GTK_STYLE_PROVIDER_PRIORITY_USER);
4456 g_object_unref (css_prov);
4457#endif
f904c0f9 4458
383b7c95
JD
4459 gtk_box_set_homogeneous (GTK_BOX (vb), FALSE);
4460
3afff00e 4461 if (wimage && !text_image)
e547b051 4462 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
3afff00e
CY
4463 if (label)
4464 gtk_box_pack_start (GTK_BOX (vb), gtk_label_new (label), TRUE, TRUE, 0);
8a52f00a
JD
4465 if (wimage && text_image)
4466 gtk_box_pack_start (GTK_BOX (vb), wimage, TRUE, TRUE, 0);
4467
f904c0f9
JD
4468 gtk_button_set_focus_on_click (GTK_BUTTON (wb), FALSE);
4469 gtk_button_set_relief (GTK_BUTTON (wb), GTK_RELIEF_NONE);
4470 gtk_container_add (GTK_CONTAINER (wb), vb);
4471 gtk_container_add (GTK_CONTAINER (weventbox), wb);
4472 gtk_container_add (GTK_CONTAINER (ti), weventbox);
4473
fe0b6370 4474 if (wimage || label)
f904c0f9 4475 {
d01a7826 4476 intptr_t ii = i;
8ac068ac
PE
4477 gpointer gi = (gpointer) ii;
4478
f904c0f9
JD
4479 g_signal_connect (G_OBJECT (ti), "create-menu-proxy",
4480 G_CALLBACK (xg_tool_bar_menu_proxy),
8ac068ac 4481 gi);
f904c0f9
JD
4482
4483 g_signal_connect (G_OBJECT (wb), "clicked",
4484 G_CALLBACK (xg_tool_bar_callback),
8ac068ac 4485 gi);
f904c0f9 4486
f904c0f9
JD
4487 g_object_set_data (G_OBJECT (weventbox), XG_FRAME_DATA, (gpointer)f);
4488
0afb4571 4489#ifndef HAVE_GTK3
f904c0f9
JD
4490 /* Catch expose events to overcome an annoying redraw bug, see
4491 comment for xg_tool_bar_item_expose_callback. */
4492 g_signal_connect (G_OBJECT (ti),
4493 "expose-event",
4494 G_CALLBACK (xg_tool_bar_item_expose_callback),
4495 0);
0afb4571 4496#endif
f904c0f9
JD
4497 gtk_tool_item_set_homogeneous (ti, FALSE);
4498
e1dbe924 4499 /* Callback to save modifier mask (Shift/Control, etc). GTK makes
f904c0f9
JD
4500 no distinction based on modifiers in the activate callback,
4501 so we have to do it ourselves. */
4502 g_signal_connect (wb, "button-release-event",
4503 G_CALLBACK (xg_tool_bar_button_cb),
4504 NULL);
4505
4506 g_object_set_data (G_OBJECT (wb), XG_FRAME_DATA, (gpointer)f);
088c8c09 4507
f904c0f9
JD
4508 /* Use enter/leave notify to show help. We use the events
4509 rather than the GtkButton specific signals "enter" and
4510 "leave", so we can have only one callback. The event
4511 will tell us what kind of event it is. */
f904c0f9
JD
4512 g_signal_connect (G_OBJECT (weventbox),
4513 "enter-notify-event",
4514 G_CALLBACK (xg_tool_bar_help_callback),
8ac068ac 4515 gi);
f904c0f9
JD
4516 g_signal_connect (G_OBJECT (weventbox),
4517 "leave-notify-event",
4518 G_CALLBACK (xg_tool_bar_help_callback),
8ac068ac 4519 gi);
f904c0f9 4520 }
088c8c09 4521
f904c0f9
JD
4522 if (wbutton) *wbutton = wb;
4523
4524 return ti;
4525}
4526
18e27ea8
PE
4527static bool
4528is_box_type (GtkWidget *vb, bool is_horizontal)
383b7c95
JD
4529{
4530#ifdef HAVE_GTK3
18e27ea8 4531 bool ret = 0;
38182d90 4532 if (GTK_IS_BOX (vb))
383b7c95
JD
4533 {
4534 GtkOrientation ori = gtk_orientable_get_orientation (GTK_ORIENTABLE (vb));
4535 ret = (ori == GTK_ORIENTATION_HORIZONTAL && is_horizontal)
4536 || (ori == GTK_ORIENTATION_VERTICAL && ! is_horizontal);
4537 }
4538 return ret;
4539#else
4540 return is_horizontal ? GTK_IS_VBOX (vb) : GTK_IS_HBOX (vb);
4541#endif
4542}
4543
4544
18e27ea8 4545static bool
3afff00e
CY
4546xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
4547 const char *icon_name, const struct image *img,
18e27ea8 4548 const char *label, bool horiz)
f904c0f9 4549{
3afff00e 4550 gpointer old;
e547b051 4551 GtkWidget *wimage;
3afff00e 4552 GtkWidget *vb = XG_BIN_CHILD (wbutton);
e547b051 4553 GtkWidget *wlbl = xg_get_tool_bar_widgets (vb, &wimage);
f904c0f9 4554
3afff00e 4555 /* Check if the tool icon matches. */
fe0b6370 4556 if (stock_name && wimage)
3afff00e
CY
4557 {
4558 old = g_object_get_data (G_OBJECT (wimage),
4559 XG_TOOL_BAR_STOCK_NAME);
4560 if (!old || strcmp (old, stock_name))
4561 return 1;
4562 }
fe0b6370 4563 else if (icon_name && wimage)
f904c0f9 4564 {
3afff00e
CY
4565 old = g_object_get_data (G_OBJECT (wimage),
4566 XG_TOOL_BAR_ICON_NAME);
4567 if (!old || strcmp (old, icon_name))
4568 return 1;
f904c0f9 4569 }
fe0b6370 4570 else if (wimage)
3afff00e 4571 {
1e5524e7 4572 gpointer gold_img = g_object_get_data (G_OBJECT (wimage),
383b7c95 4573 XG_TOOL_BAR_IMAGE_DATA);
1e5524e7 4574 Pixmap old_img = (Pixmap) gold_img;
3afff00e
CY
4575 if (old_img != img->pixmap)
4576 return 1;
4577 }
4578
4579 /* Check button configuration and label. */
383b7c95 4580 if (is_box_type (vb, horiz)
3afff00e
CY
4581 || (label ? (wlbl == NULL) : (wlbl != NULL)))
4582 return 1;
f904c0f9 4583
3afff00e 4584 /* Ensure label is correct. */
fe0b6370 4585 if (label && wlbl)
3afff00e
CY
4586 gtk_label_set_text (GTK_LABEL (wlbl), label);
4587 return 0;
f904c0f9
JD
4588}
4589
18e27ea8 4590static bool
a10c8269 4591xg_update_tool_bar_sizes (struct frame *f)
bfeabdc3
JD
4592{
4593 struct x_output *x = f->output_data.x;
4594 GtkRequisition req;
4595 int nl = 0, nr = 0, nt = 0, nb = 0;
5a1d858b 4596 GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
bfeabdc3 4597
5a1d858b 4598 gtk_widget_get_preferred_size (GTK_WIDGET (top_widget), NULL, &req);
bfeabdc3
JD
4599 if (x->toolbar_in_hbox)
4600 {
4601 int pos;
4602 gtk_container_child_get (GTK_CONTAINER (x->hbox_widget),
5a1d858b 4603 top_widget,
bfeabdc3
JD
4604 "position", &pos, NULL);
4605 if (pos == 0) nl = req.width;
4606 else nr = req.width;
4607 }
4608 else
4609 {
4610 int pos;
4611 gtk_container_child_get (GTK_CONTAINER (x->vbox_widget),
5a1d858b 4612 top_widget,
bfeabdc3
JD
4613 "position", &pos, NULL);
4614 if (pos == 0 || (pos == 1 && x->menubar_widget)) nt = req.height;
4615 else nb = req.height;
4616 }
088c8c09 4617
bfeabdc3
JD
4618 if (nl != FRAME_TOOLBAR_LEFT_WIDTH (f)
4619 || nr != FRAME_TOOLBAR_RIGHT_WIDTH (f)
4620 || nt != FRAME_TOOLBAR_TOP_HEIGHT (f)
4621 || nb != FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
4622 {
4623 FRAME_TOOLBAR_RIGHT_WIDTH (f) = FRAME_TOOLBAR_LEFT_WIDTH (f)
4624 = FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4625 FRAME_TOOLBAR_LEFT_WIDTH (f) = nl;
4626 FRAME_TOOLBAR_RIGHT_WIDTH (f) = nr;
4627 FRAME_TOOLBAR_TOP_HEIGHT (f) = nt;
4628 FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = nb;
4629 return 1;
4630 }
4631
4632 return 0;
4633}
4634
f904c0f9 4635
71bacd48
JD
4636/* Update the tool bar for frame F. Add new buttons and remove old. */
4637
f392e843 4638void
a10c8269 4639update_frame_tool_bar (struct frame *f)
f392e843 4640{
3afff00e 4641 int i, j;
f392e843 4642 struct x_output *x = f->output_data.x;
bfc2a7d1 4643 int hmargin = 0, vmargin = 0;
98a92193 4644 GtkToolbar *wtoolbar;
6e1440e6 4645 GtkToolItem *ti;
98a92193 4646 GtkTextDirection dir;
3afff00e 4647 Lisp_Object style;
18e27ea8 4648 bool text_image, horiz;
ca06f160 4649 struct xg_frame_tb_info *tbinfo;
3afff00e 4650
f392e843
JD
4651 if (! FRAME_GTK_WIDGET (f))
4652 return;
4653
4d7e6e51 4654 block_input ();
177c0ea7 4655
d311d28c 4656 if (RANGED_INTEGERP (1, Vtool_bar_button_margin, INT_MAX))
133c0116
JD
4657 {
4658 hmargin = XFASTINT (Vtool_bar_button_margin);
4659 vmargin = XFASTINT (Vtool_bar_button_margin);
4660 }
4661 else if (CONSP (Vtool_bar_button_margin))
4662 {
d311d28c 4663 if (RANGED_INTEGERP (1, XCAR (Vtool_bar_button_margin), INT_MAX))
133c0116
JD
4664 hmargin = XFASTINT (XCAR (Vtool_bar_button_margin));
4665
d311d28c 4666 if (RANGED_INTEGERP (1, XCDR (Vtool_bar_button_margin), INT_MAX))
133c0116
JD
4667 vmargin = XFASTINT (XCDR (Vtool_bar_button_margin));
4668 }
4669
4670 /* The natural size (i.e. when GTK uses 0 as margin) looks best,
4671 so take DEFAULT_TOOL_BAR_BUTTON_MARGIN to mean "default for GTK",
4672 i.e. zero. This means that margins less than
4673 DEFAULT_TOOL_BAR_BUTTON_MARGIN has no effect. */
4674 hmargin = max (0, hmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
4675 vmargin = max (0, vmargin - DEFAULT_TOOL_BAR_BUTTON_MARGIN);
c43923ad 4676
f392e843
JD
4677 if (! x->toolbar_widget)
4678 xg_create_tool_bar (f);
4679
98a92193 4680 wtoolbar = GTK_TOOLBAR (x->toolbar_widget);
f904c0f9 4681 dir = gtk_widget_get_direction (GTK_WIDGET (wtoolbar));
088c8c09 4682
3afff00e 4683 style = Ftool_bar_get_system_style ();
ca06f160
JD
4684
4685 /* Are we up to date? */
4686 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4687 TB_INFO_KEY);
4688
4689 if (! NILP (tbinfo->last_tool_bar) && ! NILP (f->tool_bar_items)
4690 && tbinfo->n_last_items == f->n_tool_bar_items
4691 && tbinfo->hmargin == hmargin && tbinfo->vmargin == vmargin
4692 && tbinfo->dir == dir
67b77c0b
AS
4693 && ! NILP (Fequal (tbinfo->style, style))
4694 && ! NILP (Fequal (tbinfo->last_tool_bar, f->tool_bar_items)))
ca06f160 4695 {
4d7e6e51 4696 unblock_input ();
ca06f160
JD
4697 return;
4698 }
4699
4700 tbinfo->last_tool_bar = f->tool_bar_items;
4701 tbinfo->n_last_items = f->n_tool_bar_items;
4702 tbinfo->style = style;
4703 tbinfo->hmargin = hmargin;
4704 tbinfo->vmargin = vmargin;
4705 tbinfo->dir = dir;
4706
3afff00e
CY
4707 text_image = EQ (style, Qtext_image_horiz);
4708 horiz = EQ (style, Qboth_horiz) || text_image;
4709
4710 for (i = j = 0; i < f->n_tool_bar_items; ++i)
f392e843 4711 {
18e27ea8
PE
4712 bool enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
4713 bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
f392e843 4714 int idx;
13464394 4715 ptrdiff_t img_id;
98a92193
JD
4716 int icon_size = 0;
4717 struct image *img = NULL;
f392e843 4718 Lisp_Object image;
495effe5 4719 Lisp_Object stock = Qnil;
98a92193
JD
4720 GtkStockItem stock_item;
4721 char *stock_name = NULL;
2154c964 4722 char *icon_name = NULL;
98a92193 4723 Lisp_Object rtl;
b42ff099 4724 GtkWidget *wbutton = NULL;
d4ad8c04 4725 Lisp_Object specified_file;
18e27ea8 4726 bool vert_only = ! NILP (PROP (TOOL_BAR_ITEM_VERT_ONLY));
3afff00e
CY
4727 const char *label
4728 = (EQ (style, Qimage) || (vert_only && horiz)) ? NULL
4729 : STRINGP (PROP (TOOL_BAR_ITEM_LABEL))
4730 ? SSDATA (PROP (TOOL_BAR_ITEM_LABEL))
4731 : "";
d2bd5189 4732
3afff00e 4733 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
6e1440e6 4734
4039c786
CY
4735 /* If this is a separator, use a gtk separator item. */
4736 if (EQ (PROP (TOOL_BAR_ITEM_TYPE), Qt))
4737 {
4738 if (ti == NULL || !GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4739 {
4740 if (ti)
4741 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4742 GTK_WIDGET (ti));
4743 ti = gtk_separator_tool_item_new ();
3afff00e 4744 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
4039c786 4745 }
3afff00e 4746 j++;
4039c786
CY
4747 continue;
4748 }
4749
4750 /* Otherwise, the tool-bar item is an ordinary button. */
4751
4752 if (ti && GTK_IS_SEPARATOR_TOOL_ITEM (ti))
4753 {
4754 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
4755 ti = NULL;
4756 }
f904c0f9 4757
3afff00e 4758 if (ti) wbutton = XG_BIN_CHILD (XG_BIN_CHILD (ti));
f392e843
JD
4759
4760 /* Ignore invalid image specifications. */
4039c786 4761 image = PROP (TOOL_BAR_ITEM_IMAGES);
f392e843
JD
4762 if (!valid_image_p (image))
4763 {
3afff00e
CY
4764 if (ti)
4765 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4766 GTK_WIDGET (ti));
f392e843
JD
4767 continue;
4768 }
4769
d4ad8c04 4770 specified_file = file_for_image (image);
4e6b227d
CY
4771 if (!NILP (specified_file) && !NILP (Ffboundp (Qx_gtk_map_stock)))
4772 stock = call1 (Qx_gtk_map_stock, specified_file);
f392e843 4773
4e6b227d 4774 if (STRINGP (stock))
98a92193
JD
4775 {
4776 stock_name = SSDATA (stock);
2154c964
JD
4777 if (stock_name[0] == 'n' && stock_name[1] == ':')
4778 {
4779 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (wtoolbar));
4780 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (screen);
4781
4782 icon_name = stock_name + 2;
4783 stock_name = NULL;
4784 stock = Qnil;
4785
4786 if (! gtk_icon_theme_has_icon (icon_theme, icon_name))
4787 icon_name = NULL;
4788 else
4789 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
4790 }
4791 else if (gtk_stock_lookup (SSDATA (stock), &stock_item))
f904c0f9 4792 icon_size = gtk_toolbar_get_icon_size (wtoolbar);
8e5a8840 4793 else
2154c964
JD
4794 {
4795 stock = Qnil;
4796 stock_name = NULL;
4797 }
98a92193 4798 }
2154c964
JD
4799
4800 if (stock_name == NULL && icon_name == NULL)
1d1885fc 4801 {
3afff00e
CY
4802 /* No stock image, or stock item not known. Try regular
4803 image. If image is a vector, choose it according to the
98a92193
JD
4804 button state. */
4805 if (dir == GTK_TEXT_DIR_RTL
4806 && !NILP (rtl = PROP (TOOL_BAR_ITEM_RTL_IMAGE))
4807 && STRINGP (rtl))
3afff00e 4808 image = find_rtl_image (f, image, rtl);
98a92193
JD
4809
4810 if (VECTORP (image))
4811 {
4812 if (enabled_p)
4813 idx = (selected_p
4814 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
4815 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
4816 else
4817 idx = (selected_p
4818 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
4819 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
4820
a54e2c05 4821 eassert (ASIZE (image) >= idx);
98a92193
JD
4822 image = AREF (image, idx);
4823 }
4824 else
4825 idx = -1;
4826
4827 img_id = lookup_image (f, image);
4828 img = IMAGE_FROM_ID (f, img_id);
4829 prepare_image_for_display (f, img);
8e5a8840 4830
98a92193
JD
4831 if (img->load_failed_p || img->pixmap == None)
4832 {
f904c0f9 4833 if (ti)
3afff00e
CY
4834 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4835 GTK_WIDGET (ti));
f904c0f9 4836 continue;
6e1440e6 4837 }
1d1885fc 4838 }
177c0ea7 4839
3afff00e
CY
4840 /* If there is an existing widget, check if it's stale; if so,
4841 remove it and make a new tool item from scratch. */
4842 if (ti && xg_tool_item_stale_p (wbutton, stock_name, icon_name,
4843 img, label, horiz))
4844 {
4845 gtk_container_remove (GTK_CONTAINER (wtoolbar),
4846 GTK_WIDGET (ti));
4847 ti = NULL;
4848 }
4849
6e1440e6 4850 if (ti == NULL)
ff24abfe 4851 {
98a92193 4852 GtkWidget *w;
3afff00e
CY
4853
4854 /* Save the image so we can see if an update is needed the
4855 next time we call xg_tool_item_match_p. */
4856 if (EQ (style, Qtext))
4857 w = NULL;
4858 else if (stock_name)
98a92193
JD
4859 {
4860 w = gtk_image_new_from_stock (stock_name, icon_size);
4861 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_STOCK_NAME,
4862 (gpointer) xstrdup (stock_name),
4863 (GDestroyNotify) xfree);
4864 }
8e5a8840 4865 else if (icon_name)
2154c964
JD
4866 {
4867 w = gtk_image_new_from_icon_name (icon_name, icon_size);
4868 g_object_set_data_full (G_OBJECT (w), XG_TOOL_BAR_ICON_NAME,
4869 (gpointer) xstrdup (icon_name),
4870 (GDestroyNotify) xfree);
4871 }
98a92193
JD
4872 else
4873 {
4874 w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
98a92193
JD
4875 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
4876 (gpointer)img->pixmap);
4877 }
4878
3afff00e
CY
4879 if (w) gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
4880 ti = xg_make_tool_item (f, w, &wbutton, label, i, horiz, text_image);
4881 gtk_toolbar_insert (GTK_TOOLBAR (wtoolbar), ti, j);
f904c0f9 4882 }
177c0ea7 4883
f392e843 4884#undef PROP
3afff00e
CY
4885
4886 gtk_widget_set_sensitive (wbutton, enabled_p);
4887 j++;
f392e843
JD
4888 }
4889
3afff00e 4890 /* Remove buttons not longer needed. */
6e1440e6 4891 do
f392e843 4892 {
21a76236 4893 ti = gtk_toolbar_get_nth_item (GTK_TOOLBAR (wtoolbar), j);
3afff00e
CY
4894 if (ti)
4895 gtk_container_remove (GTK_CONTAINER (wtoolbar), GTK_WIDGET (ti));
6e1440e6 4896 } while (ti != NULL);
f392e843 4897
bfeabdc3 4898 if (f->n_tool_bar_items != 0)
f392e843 4899 {
5a1d858b 4900 if (! x->toolbar_is_packed)
e69b0960 4901 xg_pack_tool_bar (f, f->tool_bar_position);
5a1d858b 4902 gtk_widget_show_all (TOOLBAR_TOP_WIDGET (x));
bfeabdc3
JD
4903 if (xg_update_tool_bar_sizes (f))
4904 xg_height_or_width_changed (f);
f392e843 4905 }
bfeabdc3 4906
4d7e6e51 4907 unblock_input ();
f392e843
JD
4908}
4909
71bacd48
JD
4910/* Deallocate all resources for the tool bar on frame F.
4911 Remove the tool bar. */
4912
f392e843 4913void
a10c8269 4914free_frame_tool_bar (struct frame *f)
f392e843
JD
4915{
4916 struct x_output *x = f->output_data.x;
4917
4918 if (x->toolbar_widget)
4919 {
ca06f160 4920 struct xg_frame_tb_info *tbinfo;
5a1d858b
JD
4921 GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
4922
4d7e6e51 4923 block_input ();
d3b2a6da
JD
4924 /* We may have created the toolbar_widget in xg_create_tool_bar, but
4925 not the x->handlebox_widget which is created in xg_pack_tool_bar. */
5a1d858b 4926 if (x->toolbar_is_packed)
bfeabdc3
JD
4927 {
4928 if (x->toolbar_in_hbox)
4929 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
5a1d858b 4930 top_widget);
bfeabdc3
JD
4931 else
4932 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
5a1d858b 4933 top_widget);
bfeabdc3 4934 }
d3b2a6da
JD
4935 else
4936 gtk_widget_destroy (x->toolbar_widget);
4937
f392e843 4938 x->toolbar_widget = 0;
5a1d858b
JD
4939 TOOLBAR_TOP_WIDGET (x) = 0;
4940 x->toolbar_is_packed = false;
bfeabdc3
JD
4941 FRAME_TOOLBAR_TOP_HEIGHT (f) = FRAME_TOOLBAR_BOTTOM_HEIGHT (f) = 0;
4942 FRAME_TOOLBAR_LEFT_WIDTH (f) = FRAME_TOOLBAR_RIGHT_WIDTH (f) = 0;
4943
ca06f160
JD
4944 tbinfo = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4945 TB_INFO_KEY);
4946 if (tbinfo)
4947 {
4948 xfree (tbinfo);
4949 g_object_set_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
4950 TB_INFO_KEY,
4951 NULL);
4952 }
4953
bfeabdc3 4954 xg_height_or_width_changed (f);
f392e843 4955
4d7e6e51 4956 unblock_input ();
f392e843
JD
4957 }
4958}
4959
18e27ea8 4960void
a10c8269 4961xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
bfeabdc3
JD
4962{
4963 struct x_output *x = f->output_data.x;
5a1d858b 4964 GtkWidget *top_widget = TOOLBAR_TOP_WIDGET (x);
bfeabdc3 4965
5a1d858b 4966 if (! x->toolbar_widget || ! top_widget)
18e27ea8 4967 return;
bfeabdc3 4968
4d7e6e51 4969 block_input ();
5a1d858b
JD
4970 g_object_ref (top_widget);
4971 if (x->toolbar_is_packed)
4972 {
4973 if (x->toolbar_in_hbox)
4974 gtk_container_remove (GTK_CONTAINER (x->hbox_widget),
4975 top_widget);
4976 else
4977 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
4978 top_widget);
4979 }
4980
bfeabdc3 4981 xg_pack_tool_bar (f, pos);
5a1d858b 4982 g_object_unref (top_widget);
bfeabdc3
JD
4983 if (xg_update_tool_bar_sizes (f))
4984 xg_height_or_width_changed (f);
4985
4d7e6e51 4986 unblock_input ();
bfeabdc3
JD
4987}
4988
f392e843
JD
4989
4990\f
4991/***********************************************************************
4992 Initializing
f904c0f9 4993***********************************************************************/
f392e843 4994void
971de7fb 4995xg_initialize (void)
f392e843 4996{
9f6fcdc5 4997 GtkBindingSet *binding_set;
383b7c95 4998 GtkSettings *settings;
9f6fcdc5 4999
430e6c77
JD
5000#if HAVE_XFT
5001 /* Work around a bug with corrupted data if libXft gets unloaded. This way
5002 we keep it permanently linked in. */
5003 XftInit (0);
5004#endif
879ffad9
JD
5005
5006 gdpy_def = NULL;
f392e843 5007 xg_ignore_gtk_scrollbar = 0;
8b745d92 5008#ifdef HAVE_GTK_TEAROFF_MENU_ITEM_NEW
da18b5ac 5009 xg_detached_menus = 0;
8b745d92 5010#endif
f392e843
JD
5011 xg_menu_cb_list.prev = xg_menu_cb_list.next =
5012 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
5013
81e302ef
JD
5014 id_to_widget.max_size = id_to_widget.used = 0;
5015 id_to_widget.widgets = 0;
5016
383b7c95
JD
5017 settings = gtk_settings_get_for_screen (gdk_display_get_default_screen
5018 (gdk_display_get_default ()));
f392e843
JD
5019 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
5020 bindings. It doesn't seem to be any way to remove properties,
efec3894 5021 so we set it to "" which in means "no key". */
383b7c95 5022 gtk_settings_set_string_property (settings,
f392e843 5023 "gtk-menu-bar-accel",
efec3894 5024 "",
f392e843 5025 EMACS_CLASS);
81e302ef
JD
5026
5027 /* Make GTK text input widgets use Emacs style keybindings. This is
5028 Emacs after all. */
383b7c95 5029 gtk_settings_set_string_property (settings,
81e302ef
JD
5030 "gtk-key-theme-name",
5031 "Emacs",
5032 EMACS_CLASS);
9f6fcdc5
JD
5033
5034 /* Make dialogs close on C-g. Since file dialog inherits from
5035 dialog, this works for them also. */
dc2933eb 5036 binding_set = gtk_binding_set_by_class (g_type_class_ref (GTK_TYPE_DIALOG));
0afb4571 5037 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
9f6fcdc5
JD
5038 "close", 0);
5039
5040 /* Make menus close on C-g. */
dc2933eb
JD
5041 binding_set = gtk_binding_set_by_class (g_type_class_ref
5042 (GTK_TYPE_MENU_SHELL));
0afb4571 5043 gtk_binding_entry_add_signal (binding_set, GDK_KEY_g, GDK_CONTROL_MASK,
9f6fcdc5 5044 "cancel", 0);
c195f2de 5045 update_theme_scrollbar_width ();
f2045622 5046
48660ca5 5047#ifdef HAVE_FREETYPE
f2045622 5048 x_last_font_name = NULL;
48660ca5 5049#endif
f392e843
JD
5050}
5051
5052#endif /* USE_GTK */