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