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