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