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