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