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