* gtkutil.c (xg_file_sel_ok, xg_file_sel_cancel)
[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);
1153 gtk_widget_destroy (GTK_WIDGET (p->pointer));
1154 return Qnil;
1155}
1156
1157typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
f9d64bb3
JD
1158
1159#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
457a8155
JD
1160
1161/* Return the selected file for file chooser dialog W.
1162 The returned string must be free:d. */
1163
1164static char *
1165xg_get_file_name_from_chooser (w)
1166 GtkWidget *w;
1167{
1168 return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
1169}
1170
f9d64bb3
JD
1171/* Read a file name from the user using a file chooser dialog.
1172 F is the current frame.
1173 PROMPT is a prompt to show to the user. May not be NULL.
1174 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1175 If MUSTMATCH_P is non-zero, the returned file name must be an existing
457a8155
JD
1176 file. *FUNC is set to a function that can be used to retrieve the
1177 selected file name from the returned widget.
f9d64bb3 1178
457a8155 1179 Returns the created widget. */
f9d64bb3 1180
457a8155
JD
1181static GtkWidget *
1182xg_get_file_with_chooser (f, prompt, default_filename,
1183 mustmatch_p, only_dir_p, func)
f9d64bb3
JD
1184 FRAME_PTR f;
1185 char *prompt;
1186 char *default_filename;
1187 int mustmatch_p, only_dir_p;
457a8155 1188 xg_get_file_func *func;
f9d64bb3
JD
1189{
1190 GtkWidget *filewin;
1191 GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
f9d64bb3
JD
1192 GtkFileChooserAction action = (mustmatch_p ?
1193 GTK_FILE_CHOOSER_ACTION_OPEN :
1194 GTK_FILE_CHOOSER_ACTION_SAVE);
1195
1196 if (only_dir_p)
1197 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1198
1199 filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
1200 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1201 (mustmatch_p || only_dir_p ?
8cfd0f36 1202 GTK_STOCK_OPEN : GTK_STOCK_OK),
f9d64bb3
JD
1203 GTK_RESPONSE_OK,
1204 NULL);
1205
f9d64bb3 1206 if (default_filename)
a872928c
JD
1207 {
1208 Lisp_Object file;
1209 struct gcpro gcpro1;
1210 GCPRO1 (file);
1211
1212 /* File chooser does not understand ~/... in the file name. It must be
1213 an absolute name starting with /. */
1214 if (default_filename[0] != '/')
1215 {
1216 file = Fexpand_file_name (build_string (default_filename), Qnil);
1217 default_filename = SDATA (file);
1218 }
1219
1220 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
1221 default_filename);
1222
1223 UNGCPRO;
1224 }
f9d64bb3 1225
457a8155
JD
1226 *func = xg_get_file_name_from_chooser;
1227 return filewin;
f9d64bb3
JD
1228}
1229#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */
1230
1231#ifdef HAVE_GTK_FILE_SELECTION_NEW
f392e843 1232
457a8155
JD
1233/* Return the selected file for file selector dialog W.
1234 The returned string must be free:d. */
71bacd48 1235
457a8155
JD
1236static char *
1237xg_get_file_name_from_selector (w)
f392e843 1238 GtkWidget *w;
f392e843 1239{
457a8155
JD
1240 GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
1241 return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
f392e843
JD
1242}
1243
457a8155 1244/* Create a file selection dialog.
f392e843
JD
1245 F is the current frame.
1246 PROMPT is a prompt to show to the user. May not be NULL.
1247 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1248 If MUSTMATCH_P is non-zero, the returned file name must be an existing
457a8155
JD
1249 file. *FUNC is set to a function that can be used to retrieve the
1250 selected file name from the returned widget.
f392e843 1251
457a8155 1252 Returns the created widget. */
71bacd48 1253
457a8155 1254static GtkWidget *
f9d64bb3 1255xg_get_file_with_selection (f, prompt, default_filename,
457a8155 1256 mustmatch_p, only_dir_p, func)
f392e843
JD
1257 FRAME_PTR f;
1258 char *prompt;
1259 char *default_filename;
f9d64bb3 1260 int mustmatch_p, only_dir_p;
457a8155 1261 xg_get_file_func *func;
f392e843
JD
1262{
1263 GtkWidget *filewin;
1264 GtkFileSelection *filesel;
177c0ea7 1265
f392e843
JD
1266 filewin = gtk_file_selection_new (prompt);
1267 filesel = GTK_FILE_SELECTION (filewin);
1268
f392e843
JD
1269 if (default_filename)
1270 gtk_file_selection_set_filename (filesel, default_filename);
1271
1272 if (mustmatch_p)
1273 {
1274 /* The selection_entry part of filesel is not documented. */
1275 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1276 gtk_file_selection_hide_fileop_buttons (filesel);
1277 }
1278
457a8155 1279 *func = xg_get_file_name_from_selector;
177c0ea7 1280
457a8155 1281 return filewin;
f392e843 1282}
f9d64bb3
JD
1283#endif /* HAVE_GTK_FILE_SELECTION_NEW */
1284
1285/* Read a file name from the user using a file dialog, either the old
1286 file selection dialog, or the new file chooser dialog. Which to use
1287 depends on what the GTK version used has, and what the value of
1288 gtk-use-old-file-dialog.
1289 F is the current frame.
1290 PROMPT is a prompt to show to the user. May not be NULL.
1291 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1292 If MUSTMATCH_P is non-zero, the returned file name must be an existing
1293 file.
1294
1295 Returns a file name or NULL if no file was selected.
1296 The returned string must be freed by the caller. */
1297
1298char *
1299xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
1300 FRAME_PTR f;
1301 char *prompt;
1302 char *default_filename;
1303 int mustmatch_p, only_dir_p;
1304{
457a8155
JD
1305 GtkWidget *w;
1306 int count = SPECPDL_INDEX ();
1307 char *fn = 0;
1308 int filesel_done = 0;
1309 xg_get_file_func func;
1310
f9d64bb3
JD
1311#ifdef HAVE_GTK_FILE_BOTH
1312 if (use_old_gtk_file_dialog)
457a8155
JD
1313 w = xg_get_file_with_selection (f, prompt, default_filename,
1314 mustmatch_p, only_dir_p, &func);
1315 else
1316 w = xg_get_file_with_chooser (f, prompt, default_filename,
1317 mustmatch_p, only_dir_p, &func);
f9d64bb3
JD
1318
1319#else /* not HAVE_GTK_FILE_BOTH */
1320
1321#ifdef HAVE_GTK_FILE_SELECTION_DIALOG_NEW
457a8155
JD
1322 w = xg_get_file_with_selection (f, prompt, default_filename,
1323 mustmatch_p, only_dir_p, &func);
f9d64bb3
JD
1324#endif
1325#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
457a8155
JD
1326 w = xg_get_file_with_chooser (f, prompt, default_filename,
1327 mustmatch_p, only_dir_p, &func);
f9d64bb3
JD
1328#endif
1329
1330#endif /* HAVE_GTK_FILE_BOTH */
457a8155
JD
1331
1332 xg_set_screen (w, f);
1333 gtk_widget_set_name (w, "emacs-filedialog");
1334 gtk_window_set_transient_for (GTK_WINDOW (w),
1335 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1336 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1337 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
1338
1339 g_signal_connect (G_OBJECT (w),
1340 "response",
1341 G_CALLBACK (xg_file_response_cb),
1342 &filesel_done);
1343
1344 /* Don't destroy the widget if closed by the window manager close button. */
1345 g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
1346
1347 gtk_widget_show (w);
1348
1349 record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0));
1350 while (! filesel_done)
1351 {
1352 x_menu_wait_for_event (0);
1353 gtk_main_iteration ();
1354 }
1355
1356 if (filesel_done == GTK_RESPONSE_OK)
1357 fn = (*func) (w);
1358
1359 unbind_to (count, Qnil);
1360
1361 return fn;
f9d64bb3 1362}
f392e843
JD
1363
1364\f
1365/***********************************************************************
1366 Menu functions.
1367 ***********************************************************************/
1368
1369/* The name of menu items that can be used for citomization. Since GTK
1370 RC files are very crude and primitive, we have to set this on all
1371 menu item names so a user can easily cutomize menu items. */
1372
1373#define MENU_ITEM_NAME "emacs-menuitem"
1374
1375
1376/* Linked list of all allocated struct xg_menu_cb_data. Used for marking
1377 during GC. The next member points to the items. */
1378static xg_list_node xg_menu_cb_list;
1379
1380/* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
1381 during GC. The next member points to the items. */
1382static xg_list_node xg_menu_item_cb_list;
1383
1384/* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
1385 F is the frame CL_DATA will be initialized for.
1386 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1387
1388 The menu bar and all sub menus under the menu bar in a frame
1389 share the same structure, hence the reference count.
177c0ea7 1390
f392e843
JD
1391 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
1392 allocated xg_menu_cb_data if CL_DATA is NULL. */
71bacd48 1393
f392e843
JD
1394static xg_menu_cb_data *
1395make_cl_data (cl_data, f, highlight_cb)
1396 xg_menu_cb_data *cl_data;
1397 FRAME_PTR f;
1398 GCallback highlight_cb;
1399{
1400 if (! cl_data)
1401 {
1402 cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
1403 cl_data->f = f;
1404 cl_data->menu_bar_vector = f->menu_bar_vector;
1405 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1406 cl_data->highlight_cb = highlight_cb;
1407 cl_data->ref_count = 0;
1408
1409 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
1410 }
1411
1412 cl_data->ref_count++;
1413
1414 return cl_data;
1415}
1416
1417/* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
1418 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1419
1420 When the menu bar is updated, menu items may have been added and/or
1421 removed, so menu_bar_vector and menu_bar_items_used change. We must
1422 then update CL_DATA since it is used to determine which menu
1423 item that is invoked in the menu.
1424 HIGHLIGHT_CB could change, there is no check that the same
1425 function is given when modifying a menu bar as was given when
1426 creating the menu bar. */
71bacd48 1427
f392e843
JD
1428static void
1429update_cl_data (cl_data, f, highlight_cb)
1430 xg_menu_cb_data *cl_data;
1431 FRAME_PTR f;
1432 GCallback highlight_cb;
1433{
1434 if (cl_data)
1435 {
1436 cl_data->f = f;
1437 cl_data->menu_bar_vector = f->menu_bar_vector;
1438 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1439 cl_data->highlight_cb = highlight_cb;
1440 }
1441}
1442
1443/* Decrease reference count for CL_DATA.
1444 If reference count is zero, free CL_DATA. */
71bacd48 1445
f392e843
JD
1446static void
1447unref_cl_data (cl_data)
1448 xg_menu_cb_data *cl_data;
1449{
1450 if (cl_data && cl_data->ref_count > 0)
1451 {
1452 cl_data->ref_count--;
1453 if (cl_data->ref_count == 0)
1454 {
1455 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
1456 xfree (cl_data);
1457 }
1458 }
1459}
1460
1461/* Function that marks all lisp data during GC. */
71bacd48 1462
f392e843
JD
1463void
1464xg_mark_data ()
1465{
1466 xg_list_node *iter;
1467
1468 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
631f2082 1469 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
f392e843
JD
1470
1471 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
1472 {
1473 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
1474
1475 if (! NILP (cb_data->help))
631f2082 1476 mark_object (cb_data->help);
f392e843
JD
1477 }
1478}
1479
1480
1481/* Callback called when a menu item is destroyed. Used to free data.
1482 W is the widget that is being destroyed (not used).
1483 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
71bacd48 1484
f392e843
JD
1485static void
1486menuitem_destroy_callback (w, client_data)
1487 GtkWidget *w;
1488 gpointer client_data;
1489{
1490 if (client_data)
1491 {
1492 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1493 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
1494 xfree (data);
1495 }
1496}
1497
1498/* Callback called when the pointer enters/leaves a menu item.
1499 W is the menu item.
1500 EVENT is either an enter event or leave event.
1501 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
1502
1503 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 1504
f392e843
JD
1505static gboolean
1506menuitem_highlight_callback (w, event, client_data)
1507 GtkWidget *w;
1508 GdkEventCrossing *event;
1509 gpointer client_data;
1510{
1511 if (client_data)
1512 {
1513 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1514 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
177c0ea7 1515
f392e843
JD
1516 if (! NILP (data->help) && data->cl_data->highlight_cb)
1517 {
1518 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
1519 (*func) (w, call_data);
1520 }
1521 }
1522
1523 return FALSE;
1524}
1525
1526/* Callback called when a menu is destroyed. Used to free data.
1527 W is the widget that is being destroyed (not used).
1528 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
71bacd48 1529
f392e843
JD
1530static void
1531menu_destroy_callback (w, client_data)
1532 GtkWidget *w;
1533 gpointer client_data;
1534{
1535 unref_cl_data ((xg_menu_cb_data*) client_data);
1536}
1537
1538/* Callback called when a menu does a grab or ungrab. That means the
1539 menu has been activated or deactivated.
1540 Used to start a timer so the small timeout the menus in GTK uses before
1541 popping down a menu is seen by Emacs (see xg_process_timeouts above).
1542 W is the widget that does the grab (not used).
1543 UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
1544 CLIENT_DATA is NULL (not used). */
71bacd48 1545
f392e843
JD
1546static void
1547menu_grab_callback (GtkWidget *widget,
1548 gboolean ungrab_p,
1549 gpointer client_data)
1550{
1551 /* Keep track of total number of grabs. */
1552 static int cnt;
1553
1554 if (ungrab_p) cnt--;
1555 else cnt++;
1556
1557 if (cnt > 0 && ! xg_timer) xg_start_timer ();
1558 else if (cnt == 0 && xg_timer) xg_stop_timer ();
1559}
1560
1561/* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
1562 must be non-NULL) and can be inserted into a menu item.
1563
1564 Returns the GtkHBox. */
71bacd48 1565
f392e843
JD
1566static GtkWidget *
1567make_widget_for_menu_item (utf8_label, utf8_key)
1568 char *utf8_label;
1569 char *utf8_key;
1570{
1571 GtkWidget *wlbl;
1572 GtkWidget *wkey;
1573 GtkWidget *wbox;
177c0ea7 1574
f392e843 1575 wbox = gtk_hbox_new (FALSE, 0);
4b1b4443 1576 wlbl = gtk_label_new (utf8_label);
f392e843
JD
1577 wkey = gtk_label_new (utf8_key);
1578
1579 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
1580 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
177c0ea7 1581
f392e843
JD
1582 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
1583 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
1584
1585 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
1586 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
7863d625 1587 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
f392e843
JD
1588
1589 return wbox;
1590}
1591
1592/* Make and return a menu item widget with the key to the right.
1593 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
1594 UTF8_KEY is the text representing the key binding.
1595 ITEM is the widget_value describing the menu item.
177c0ea7 1596
f392e843
JD
1597 GROUP is an in/out parameter. If the menu item to be created is not
1598 part of any radio menu group, *GROUP contains NULL on entry and exit.
1599 If the menu item to be created is part of a radio menu group, on entry
1600 *GROUP contains the group to use, or NULL if this is the first item
1601 in the group. On exit, *GROUP contains the radio item group.
1602
1603 Unfortunately, keys don't line up as nicely as in Motif,
1604 but the MacOS X version doesn't either, so I guess that is OK. */
71bacd48 1605
f392e843
JD
1606static GtkWidget *
1607make_menu_item (utf8_label, utf8_key, item, group)
1608 char *utf8_label;
1609 char *utf8_key;
1610 widget_value *item;
1611 GSList **group;
1612{
1613 GtkWidget *w;
1614 GtkWidget *wtoadd = 0;
177c0ea7 1615
adcb132c
JD
1616 /* It has been observed that some menu items have a NULL name field.
1617 This will lead to this function being called with a NULL utf8_label.
1618 GTK crashes on that so we set a blank label. Why there is a NULL
1619 name remains to be investigated. */
1620 if (! utf8_label) utf8_label = " ";
1621
f392e843
JD
1622 if (utf8_key)
1623 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
177c0ea7 1624
f392e843
JD
1625 if (item->button_type == BUTTON_TYPE_TOGGLE)
1626 {
1627 *group = NULL;
1628 if (utf8_key) w = gtk_check_menu_item_new ();
4b1b4443 1629 else w = gtk_check_menu_item_new_with_label (utf8_label);
f392e843
JD
1630 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
1631 }
1632 else if (item->button_type == BUTTON_TYPE_RADIO)
1633 {
1634 if (utf8_key) w = gtk_radio_menu_item_new (*group);
4b1b4443 1635 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
f392e843
JD
1636 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
1637 if (item->selected)
1638 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1639 }
1640 else
1641 {
1642 *group = NULL;
1643 if (utf8_key) w = gtk_menu_item_new ();
4b1b4443 1644 else w = gtk_menu_item_new_with_label (utf8_label);
f392e843 1645 }
177c0ea7 1646
f392e843
JD
1647 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
1648 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
1649
1650 return w;
1651}
1652
5fd6f727 1653/* Return non-zero if LABEL specifies a separator (GTK only has one
f392e843 1654 separator type) */
71bacd48 1655
f392e843 1656static int
5fd6f727
JD
1657xg_separator_p (char *label)
1658{
1659 if (! label) return 0;
1660 else if (strlen (label) > 3
1661 && strncmp (label, "--", 2) == 0
1662 && label[2] != '-')
1663 {
1664 static char* separator_names[] = {
1665 "space",
1666 "no-line",
1667 "single-line",
1668 "double-line",
1669 "single-dashed-line",
1670 "double-dashed-line",
1671 "shadow-etched-in",
1672 "shadow-etched-out",
1673 "shadow-etched-in-dash",
1674 "shadow-etched-out-dash",
1675 "shadow-double-etched-in",
1676 "shadow-double-etched-out",
1677 "shadow-double-etched-in-dash",
1678 "shadow-double-etched-out-dash",
1679 0,
1680 };
1681
1682 int i;
1683
1684 label += 2;
1685 for (i = 0; separator_names[i]; ++i)
1686 if (strcmp (label, separator_names[i]) == 0)
1687 return 1;
1688 }
1689 else
1690 {
1691 /* Old-style separator, maybe. It's a separator if it contains
1692 only dashes. */
1693 while (*label == '-')
1694 ++label;
1695 if (*label == 0) return 1;
1696 }
0a1d6de0 1697
5fd6f727 1698 return 0;
f392e843
JD
1699}
1700
da18b5ac
JD
1701static int xg_detached_menus;
1702
1703/* Returns non-zero if there are detached menus. */
71bacd48 1704
da18b5ac
JD
1705int
1706xg_have_tear_offs ()
1707{
1708 return xg_detached_menus > 0;
1709}
f392e843
JD
1710
1711/* Callback invoked when a detached menu window is removed. Here we
da18b5ac 1712 decrease the xg_detached_menus count.
f392e843 1713 WIDGET is the top level window that is removed (the parent of the menu).
da18b5ac 1714 CLIENT_DATA is not used. */
71bacd48 1715
da18b5ac
JD
1716static void
1717tearoff_remove (widget, client_data)
f392e843 1718 GtkWidget *widget;
f392e843
JD
1719 gpointer client_data;
1720{
da18b5ac 1721 if (xg_detached_menus > 0) --xg_detached_menus;
f392e843
JD
1722}
1723
da18b5ac
JD
1724/* Callback invoked when a menu is detached. It increases the
1725 xg_detached_menus count.
f392e843 1726 WIDGET is the GtkTearoffMenuItem.
177c0ea7 1727 CLIENT_DATA is not used. */
71bacd48 1728
f392e843
JD
1729static void
1730tearoff_activate (widget, client_data)
1731 GtkWidget *widget;
1732 gpointer client_data;
1733{
1734 GtkWidget *menu = gtk_widget_get_parent (widget);
da18b5ac
JD
1735 if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
1736 {
1737 ++xg_detached_menus;
1738 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
1739 "destroy",
1740 G_CALLBACK (tearoff_remove), 0);
1741 }
f392e843
JD
1742}
1743
f392e843 1744
f392e843
JD
1745/* Create a menu item widget, and connect the callbacks.
1746 ITEM decribes the menu item.
1747 F is the frame the created menu belongs to.
1748 SELECT_CB is the callback to use when a menu item is selected.
1749 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1750 CL_DATA points to the callback data to be used for this menu.
1751 GROUP is an in/out parameter. If the menu item to be created is not
1752 part of any radio menu group, *GROUP contains NULL on entry and exit.
1753 If the menu item to be created is part of a radio menu group, on entry
1754 *GROUP contains the group to use, or NULL if this is the first item
1755 in the group. On exit, *GROUP contains the radio item group.
1756
1757 Returns the created GtkWidget. */
71bacd48 1758
f392e843
JD
1759static GtkWidget *
1760xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
1761 widget_value *item;
1762 FRAME_PTR f;
1763 GCallback select_cb;
1764 GCallback highlight_cb;
1765 xg_menu_cb_data *cl_data;
1766 GSList **group;
1767{
1768 char *utf8_label;
1769 char *utf8_key;
1770 GtkWidget *w;
1771 xg_menu_item_cb_data *cb_data;
1772
1773 utf8_label = get_utf8_string (item->name);
1774 utf8_key = get_utf8_string (item->key);
1775
1776 w = make_menu_item (utf8_label, utf8_key, item, group);
1777
1778 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1779 if (utf8_key && utf8_key != item->key) g_free (utf8_key);
1780
1781 cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
1782
1783 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
1784
1785 cb_data->unhighlight_id = cb_data->highlight_id = cb_data->select_id = 0;
1786 cb_data->help = item->help;
1787 cb_data->cl_data = cl_data;
1788 cb_data->call_data = item->call_data;
177c0ea7 1789
f392e843
JD
1790 g_signal_connect (G_OBJECT (w),
1791 "destroy",
1792 G_CALLBACK (menuitem_destroy_callback),
1793 cb_data);
1794
1795 /* Put cb_data in widget, so we can get at it when modifying menubar */
1796 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
1797
1798 /* final item, not a submenu */
1799 if (item->call_data && ! item->contents)
1800 {
1801 if (select_cb)
1802 cb_data->select_id
1803 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
1804 }
1805
1806 if (! NILP (item->help) && highlight_cb)
1807 {
1808 /* We use enter/leave notify instead of select/deselect because
1809 select/deselect doesn't go well with detached menus. */
1810 cb_data->highlight_id
1811 = g_signal_connect (G_OBJECT (w),
1812 "enter-notify-event",
1813 G_CALLBACK (menuitem_highlight_callback),
1814 cb_data);
1815 cb_data->unhighlight_id
1816 = g_signal_connect (G_OBJECT (w),
1817 "leave-notify-event",
1818 G_CALLBACK (menuitem_highlight_callback),
1819 cb_data);
1820 }
1821
1822 return w;
1823}
1824
9d6194d6
AS
1825static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
1826 GCallback, GCallback, int, int, int,
1827 GtkWidget *, xg_menu_cb_data *, char *));
1828
f392e843
JD
1829/* Create a full menu tree specified by DATA.
1830 F is the frame the created menu belongs to.
1831 SELECT_CB is the callback to use when a menu item is selected.
1832 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
1833 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1834 POP_UP_P is non-zero if we shall create a popup menu.
1835 MENU_BAR_P is non-zero if we shall create a menu bar.
1836 ADD_TEAROFF_P is non-zero if we shall add a teroff menu item. Ignored
1837 if MENU_BAR_P is non-zero.
1838 TOPMENU is the topmost GtkWidget that others shall be placed under.
1839 It may be NULL, in that case we create the appropriate widget
1840 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
1841 CL_DATA is the callback data we shall use for this menu, or NULL
1842 if we haven't set the first callback yet.
1843 NAME is the name to give to the top level menu if this function
1844 creates it. May be NULL to not set any name.
1845
1846 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
1847 not NULL.
1848
1849 This function calls itself to create submenus. */
1850
1851static GtkWidget *
1852create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
1853 pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name)
1854 widget_value *data;
1855 FRAME_PTR f;
1856 GCallback select_cb;
1857 GCallback deactivate_cb;
1858 GCallback highlight_cb;
1859 int pop_up_p;
1860 int menu_bar_p;
1861 int add_tearoff_p;
1862 GtkWidget *topmenu;
1863 xg_menu_cb_data *cl_data;
1864 char *name;
1865{
1866 widget_value *item;
1867 GtkWidget *wmenu = topmenu;
1868 GSList *group = NULL;
1869
1870 if (! topmenu)
1871 {
810f2256
JD
1872 if (! menu_bar_p)
1873 {
1874 wmenu = gtk_menu_new ();
1875 xg_set_screen (wmenu, f);
1876 }
f392e843
JD
1877 else wmenu = gtk_menu_bar_new ();
1878
1879 /* Put cl_data on the top menu for easier access. */
1880 cl_data = make_cl_data (cl_data, f, highlight_cb);
1881 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
1882 g_signal_connect (G_OBJECT (wmenu), "destroy",
1883 G_CALLBACK (menu_destroy_callback), cl_data);
177c0ea7 1884
f392e843
JD
1885 if (name)
1886 gtk_widget_set_name (wmenu, name);
1887
1888 if (deactivate_cb)
1889 g_signal_connect (G_OBJECT (wmenu),
1890 "deactivate", deactivate_cb, 0);
1891
1892 g_signal_connect (G_OBJECT (wmenu),
1893 "grab-notify", G_CALLBACK (menu_grab_callback), 0);
1894 }
177c0ea7 1895
f392e843
JD
1896 if (! menu_bar_p && add_tearoff_p)
1897 {
1898 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
1899 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
1900
1901 g_signal_connect (G_OBJECT (tearoff), "activate",
1902 G_CALLBACK (tearoff_activate), 0);
1903 }
1904
1905 for (item = data; item; item = item->next)
1906 {
1907 GtkWidget *w;
177c0ea7 1908
f392e843
JD
1909 if (pop_up_p && !item->contents && !item->call_data
1910 && !xg_separator_p (item->name))
1911 {
1912 char *utf8_label;
1913 /* A title for a popup. We do the same as GTK does when
1914 creating titles, but it does not look good. */
1915 group = NULL;
1916 utf8_label = get_utf8_string (item->name);
1917
1918 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
4b1b4443 1919 w = gtk_menu_item_new_with_label (utf8_label);
f392e843
JD
1920 gtk_widget_set_sensitive (w, FALSE);
1921 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1922 }
1923 else if (xg_separator_p (item->name))
1924 {
1925 group = NULL;
1926 /* GTK only have one separator type. */
1927 w = gtk_separator_menu_item_new ();
1928 }
1929 else
1930 {
1931 w = xg_create_one_menuitem (item,
1932 f,
1933 item->contents ? 0 : select_cb,
1934 highlight_cb,
1935 cl_data,
1936 &group);
1937
1938 if (item->contents)
1939 {
1940 GtkWidget *submenu = create_menus (item->contents,
1941 f,
1942 select_cb,
1943 deactivate_cb,
1944 highlight_cb,
1945 0,
1946 0,
da18b5ac 1947 add_tearoff_p,
f392e843
JD
1948 0,
1949 cl_data,
1950 0);
1951 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
1952 }
f392e843
JD
1953 }
1954
1955 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
1956 gtk_widget_set_name (w, MENU_ITEM_NAME);
1957 }
1958
1959 return wmenu;
1960}
1961
1962/* Create a menubar, popup menu or dialog, depending on the TYPE argument.
1963 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
1964 with some text and buttons.
1965 F is the frame the created item belongs to.
1966 NAME is the name to use for the top widget.
1967 VAL is a widget_value structure describing items to be created.
1968 SELECT_CB is the callback to use when a menu item is selected or
1969 a dialog button is pressed.
1970 DEACTIVATE_CB is the callback to use when an item is deactivated.
1971 For a menu, when a sub menu is not shown anymore, for a dialog it is
1972 called when the dialog is popped down.
1973 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1974
1975 Returns the widget created. */
71bacd48 1976
f392e843
JD
1977GtkWidget *
1978xg_create_widget (type, name, f, val,
1979 select_cb, deactivate_cb, highlight_cb)
1980 char *type;
1981 char *name;
1982 FRAME_PTR f;
1983 widget_value *val;
1984 GCallback select_cb;
1985 GCallback deactivate_cb;
1986 GCallback highlight_cb;
1987{
1988 GtkWidget *w = 0;
da18b5ac
JD
1989 int menu_bar_p = strcmp (type, "menubar") == 0;
1990 int pop_up_p = strcmp (type, "popup") == 0;
1991
f392e843
JD
1992 if (strcmp (type, "dialog") == 0)
1993 {
1994 w = create_dialog (val, select_cb, deactivate_cb);
810f2256 1995 xg_set_screen (w, f);
f392e843
JD
1996 gtk_window_set_transient_for (GTK_WINDOW (w),
1997 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1998 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
810f2256 1999 gtk_widget_set_name (w, "emacs-dialog");
457a8155 2000 gtk_window_set_modal (GTK_WINDOW (w), TRUE);
f392e843 2001 }
da18b5ac 2002 else if (menu_bar_p || pop_up_p)
f392e843
JD
2003 {
2004 w = create_menus (val->contents,
2005 f,
2006 select_cb,
2007 deactivate_cb,
2008 highlight_cb,
da18b5ac
JD
2009 pop_up_p,
2010 menu_bar_p,
2011 menu_bar_p,
f392e843
JD
2012 0,
2013 0,
2014 name);
2015
2016 /* Set the cursor to an arrow for popup menus when they are mapped.
2017 This is done by default for menu bar menus. */
da18b5ac 2018 if (pop_up_p)
f392e843
JD
2019 {
2020 /* Must realize so the GdkWindow inside the widget is created. */
2021 gtk_widget_realize (w);
810f2256 2022 xg_set_cursor (w, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
f392e843
JD
2023 }
2024 }
2025 else
2026 {
2027 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
2028 type);
2029 }
2030
2031 return w;
2032}
2033
0a1d6de0 2034/* Return the label for menu item WITEM. */
71bacd48 2035
f392e843
JD
2036static const char *
2037xg_get_menu_item_label (witem)
2038 GtkMenuItem *witem;
2039{
2040 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
2041 return gtk_label_get_label (wlabel);
2042}
2043
0a1d6de0 2044/* Return non-zero if the menu item WITEM has the text LABEL. */
71bacd48 2045
f392e843
JD
2046static int
2047xg_item_label_same_p (witem, label)
2048 GtkMenuItem *witem;
2049 char *label;
2050{
0a1d6de0 2051 int is_same = 0;
f392e843 2052 char *utf8_label = get_utf8_string (label);
0a1d6de0
JD
2053 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
2054
2055 if (! old_label && ! utf8_label)
2056 is_same = 1;
2057 else if (old_label && utf8_label)
2058 is_same = strcmp (utf8_label, old_label) == 0;
2059
2060 if (utf8_label && utf8_label != label) g_free (utf8_label);
f392e843
JD
2061
2062 return is_same;
2063}
2064
71bacd48
JD
2065/* Destroy widgets in LIST. */
2066
f392e843 2067static void
71bacd48 2068xg_destroy_widgets (list)
f392e843
JD
2069 GList *list;
2070{
f392e843
JD
2071 GList *iter;
2072
49853a4d 2073 for (iter = list; iter; iter = g_list_next (iter))
f392e843
JD
2074 {
2075 GtkWidget *w = GTK_WIDGET (iter->data);
2076
71bacd48 2077 /* Destroying the widget will remove it from the container it is in. */
f392e843
JD
2078 gtk_widget_destroy (w);
2079 }
f392e843
JD
2080}
2081
2082/* Update the top level names in MENUBAR (i.e. not submenus).
2083 F is the frame the menu bar belongs to.
49853a4d
JD
2084 *LIST is a list with the current menu bar names (menu item widgets).
2085 ITER is the item within *LIST that shall be updated.
2086 POS is the numerical position, starting at 0, of ITER in *LIST.
f392e843
JD
2087 VAL describes what the menu bar shall look like after the update.
2088 SELECT_CB is the callback to use when a menu item is selected.
2089 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
49853a4d 2090 CL_DATA points to the callback data to be used for this menu bar.
f392e843
JD
2091
2092 This function calls itself to walk through the menu bar names. */
71bacd48 2093
f392e843 2094static void
49853a4d
JD
2095xg_update_menubar (menubar, f, list, iter, pos, val,
2096 select_cb, highlight_cb, cl_data)
f392e843
JD
2097 GtkWidget *menubar;
2098 FRAME_PTR f;
49853a4d
JD
2099 GList **list;
2100 GList *iter;
2101 int pos;
f392e843
JD
2102 widget_value *val;
2103 GCallback select_cb;
2104 GCallback highlight_cb;
2105 xg_menu_cb_data *cl_data;
2106{
49853a4d 2107 if (! iter && ! val)
f392e843 2108 return;
49853a4d 2109 else if (iter && ! val)
f392e843 2110 {
49853a4d 2111 /* Item(s) have been removed. Remove all remaining items. */
71bacd48 2112 xg_destroy_widgets (iter);
f392e843
JD
2113
2114 /* All updated. */
2115 val = 0;
49853a4d 2116 iter = 0;
f392e843 2117 }
49853a4d 2118 else if (! iter && val)
f392e843
JD
2119 {
2120 /* Item(s) added. Add all new items in one call. */
2121 create_menus (val, f, select_cb, 0, highlight_cb,
2122 0, 1, 0, menubar, cl_data, 0);
2123
2124 /* All updated. */
2125 val = 0;
49853a4d 2126 iter = 0;
f392e843 2127 }
49853a4d
JD
2128 /* Below this neither iter or val is NULL */
2129 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
f392e843
JD
2130 {
2131 /* This item is still the same, check next item. */
2132 val = val->next;
49853a4d
JD
2133 iter = g_list_next (iter);
2134 ++pos;
f392e843
JD
2135 }
2136 else /* This item is changed. */
2137 {
49853a4d 2138 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
f392e843 2139 GtkMenuItem *witem2 = 0;
f392e843 2140 int val_in_menubar = 0;
49853a4d
JD
2141 int iter_in_new_menubar = 0;
2142 GList *iter2;
f392e843
JD
2143 widget_value *cur;
2144
f392e843 2145 /* See if the changed entry (val) is present later in the menu bar */
49853a4d
JD
2146 for (iter2 = iter;
2147 iter2 && ! val_in_menubar;
2148 iter2 = g_list_next (iter2))
f392e843 2149 {
49853a4d 2150 witem2 = GTK_MENU_ITEM (iter2->data);
f392e843
JD
2151 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2152 }
2153
49853a4d 2154 /* See if the current entry (iter) is present later in the
f392e843 2155 specification for the new menu bar. */
49853a4d
JD
2156 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2157 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
f392e843 2158
49853a4d 2159 if (val_in_menubar && ! iter_in_new_menubar)
f392e843 2160 {
49853a4d
JD
2161 int nr = pos;
2162
f392e843
JD
2163 /* This corresponds to:
2164 Current: A B C
2165 New: A C
2166 Remove B. */
177c0ea7 2167
f392e843
JD
2168 gtk_widget_ref (GTK_WIDGET (witem));
2169 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2170 gtk_widget_destroy (GTK_WIDGET (witem));
2171
2172 /* Must get new list since the old changed. */
49853a4d
JD
2173 g_list_free (*list);
2174 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2175 while (nr-- > 0) iter = g_list_next (iter);
f392e843 2176 }
49853a4d 2177 else if (! val_in_menubar && ! iter_in_new_menubar)
f392e843
JD
2178 {
2179 /* This corresponds to:
2180 Current: A B C
2181 New: A X C
2182 Rename B to X. This might seem to be a strange thing to do,
2183 since if there is a menu under B it will be totally wrong for X.
2184 But consider editing a C file. Then there is a C-mode menu
2185 (corresponds to B above).
2186 If then doing C-x C-f the minibuf menu (X above) replaces the
2187 C-mode menu. When returning from the minibuffer, we get
2188 back the C-mode menu. Thus we do:
2189 Rename B to X (C-mode to minibuf menu)
2190 Rename X to B (minibuf to C-mode menu).
2191 If the X menu hasn't been invoked, the menu under B
2192 is up to date when leaving the minibuffer. */
2193 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
2194 char *utf8_label = get_utf8_string (val->name);
da18b5ac 2195 GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
177c0ea7 2196
4b1b4443 2197 gtk_label_set_text (wlabel, utf8_label);
f392e843 2198
da18b5ac
JD
2199 /* If this item has a submenu that has been detached, change
2200 the title in the WM decorations also. */
2201 if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
2202 /* Set the title of the detached window. */
2203 gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
2204
49853a4d 2205 iter = g_list_next (iter);
f392e843 2206 val = val->next;
49853a4d 2207 ++pos;
f392e843 2208 }
49853a4d 2209 else if (! val_in_menubar && iter_in_new_menubar)
f392e843
JD
2210 {
2211 /* This corresponds to:
2212 Current: A B C
2213 New: A X B C
2214 Insert X. */
2215
49853a4d 2216 int nr = pos;
f392e843
JD
2217 GList *group = 0;
2218 GtkWidget *w = xg_create_one_menuitem (val,
2219 f,
2220 select_cb,
2221 highlight_cb,
2222 cl_data,
2223 &group);
2224
2225 gtk_widget_set_name (w, MENU_ITEM_NAME);
2226 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2227
49853a4d
JD
2228 g_list_free (*list);
2229 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2230 while (nr-- > 0) iter = g_list_next (iter);
2231 iter = g_list_next (iter);
f392e843 2232 val = val->next;
49853a4d 2233 ++pos;
f392e843 2234 }
49853a4d 2235 else /* if (val_in_menubar && iter_in_new_menubar) */
f392e843 2236 {
49853a4d 2237 int nr = pos;
f392e843
JD
2238 /* This corresponds to:
2239 Current: A B C
2240 New: A C B
2241 Move C before B */
2242
2243 gtk_widget_ref (GTK_WIDGET (witem2));
2244 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2245 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2246 GTK_WIDGET (witem2), pos);
2247 gtk_widget_unref (GTK_WIDGET (witem2));
2248
49853a4d
JD
2249 g_list_free (*list);
2250 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2251 while (nr-- > 0) iter = g_list_next (iter);
f392e843 2252 val = val->next;
49853a4d 2253 ++pos;
f392e843 2254 }
f392e843
JD
2255 }
2256
2257 /* Update the rest of the menu bar. */
49853a4d
JD
2258 xg_update_menubar (menubar, f, list, iter, pos, val,
2259 select_cb, highlight_cb, cl_data);
f392e843
JD
2260}
2261
2262/* Update the menu item W so it corresponds to VAL.
2263 SELECT_CB is the callback to use when a menu item is selected.
2264 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2265 CL_DATA is the data to set in the widget for menu invokation. */
71bacd48 2266
f392e843
JD
2267static void
2268xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
2269 widget_value *val;
2270 GtkWidget *w;
2271 GCallback select_cb;
2272 GCallback highlight_cb;
2273 xg_menu_cb_data *cl_data;
2274{
2275 GtkWidget *wchild;
2276 GtkLabel *wlbl = 0;
2277 GtkLabel *wkey = 0;
2278 char *utf8_label;
2279 char *utf8_key;
0a1d6de0
JD
2280 const char *old_label = 0;
2281 const char *old_key = 0;
f392e843 2282 xg_menu_item_cb_data *cb_data;
177c0ea7
JB
2283
2284 wchild = gtk_bin_get_child (GTK_BIN (w));
f392e843
JD
2285 utf8_label = get_utf8_string (val->name);
2286 utf8_key = get_utf8_string (val->key);
2287
2288 /* See if W is a menu item with a key. See make_menu_item above. */
2289 if (GTK_IS_HBOX (wchild))
2290 {
2291 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2292
2293 wlbl = GTK_LABEL (list->data);
2294 wkey = GTK_LABEL (list->next->data);
49853a4d
JD
2295 g_list_free (list);
2296
f392e843
JD
2297 if (! utf8_key)
2298 {
2299 /* Remove the key and keep just the label. */
2300 gtk_widget_ref (GTK_WIDGET (wlbl));
2301 gtk_container_remove (GTK_CONTAINER (w), wchild);
2302 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
2303 wkey = 0;
2304 }
49853a4d 2305
f392e843
JD
2306 }
2307 else /* Just a label. */
2308 {
2309 wlbl = GTK_LABEL (wchild);
2310
2311 /* Check if there is now a key. */
2312 if (utf8_key)
2313 {
2314 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2315 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
49853a4d 2316
f392e843
JD
2317 wlbl = GTK_LABEL (list->data);
2318 wkey = GTK_LABEL (list->next->data);
49853a4d 2319 g_list_free (list);
f392e843
JD
2320
2321 gtk_container_remove (GTK_CONTAINER (w), wchild);
2322 gtk_container_add (GTK_CONTAINER (w), wtoadd);
2323 }
2324 }
2325
177c0ea7 2326
0a1d6de0
JD
2327 if (wkey) old_key = gtk_label_get_label (wkey);
2328 if (wlbl) old_label = gtk_label_get_label (wlbl);
177c0ea7 2329
0a1d6de0 2330 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
f392e843
JD
2331 gtk_label_set_text (wkey, utf8_key);
2332
0a1d6de0 2333 if (! old_label || strcmp (utf8_label, old_label) != 0)
4b1b4443 2334 gtk_label_set_text (wlbl, utf8_label);
f392e843 2335
0a1d6de0
JD
2336 if (utf8_key && utf8_key != val->key) g_free (utf8_key);
2337 if (utf8_label && utf8_label != val->name) g_free (utf8_label);
177c0ea7 2338
f392e843
JD
2339 if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
2340 gtk_widget_set_sensitive (w, FALSE);
2341 else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
2342 gtk_widget_set_sensitive (w, TRUE);
2343
2344 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
2345 XG_ITEM_DATA);
2346 if (cb_data)
2347 {
2348 cb_data->call_data = val->call_data;
2349 cb_data->help = val->help;
2350 cb_data->cl_data = cl_data;
177c0ea7 2351
f392e843
JD
2352 /* We assume the callback functions don't change. */
2353 if (val->call_data && ! val->contents)
2354 {
2355 /* This item shall have a select callback. */
2356 if (! cb_data->select_id)
2357 cb_data->select_id
2358 = g_signal_connect (G_OBJECT (w), "activate",
2359 select_cb, cb_data);
2360 }
2361 else if (cb_data->select_id)
2362 {
2363 g_signal_handler_disconnect (w, cb_data->select_id);
2364 cb_data->select_id = 0;
2365 }
2366
2367 if (NILP (cb_data->help))
2368 {
2369 /* Shall not have help. Remove if any existed previously. */
2370 if (cb_data->highlight_id)
2371 {
2372 g_signal_handler_disconnect (G_OBJECT (w),
2373 cb_data->highlight_id);
2374 cb_data->highlight_id = 0;
2375 }
2376 if (cb_data->unhighlight_id)
2377 {
2378 g_signal_handler_disconnect (G_OBJECT (w),
2379 cb_data->unhighlight_id);
2380 cb_data->unhighlight_id = 0;
2381 }
2382 }
2383 else if (! cb_data->highlight_id && highlight_cb)
2384 {
2385 /* Have help now, but didn't previously. Add callback. */
2386 cb_data->highlight_id
2387 = g_signal_connect (G_OBJECT (w),
2388 "enter-notify-event",
2389 G_CALLBACK (menuitem_highlight_callback),
2390 cb_data);
2391 cb_data->unhighlight_id
2392 = g_signal_connect (G_OBJECT (w),
2393 "leave-notify-event",
2394 G_CALLBACK (menuitem_highlight_callback),
2395 cb_data);
2396 }
2397 }
2398}
2399
2400/* Update the toggle menu item W so it corresponds to VAL. */
71bacd48 2401
f392e843
JD
2402static void
2403xg_update_toggle_item (val, w)
2404 widget_value *val;
2405 GtkWidget *w;
2406{
2407 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2408}
2409
2410/* Update the radio menu item W so it corresponds to VAL. */
71bacd48 2411
f392e843
JD
2412static void
2413xg_update_radio_item (val, w)
2414 widget_value *val;
2415 GtkWidget *w;
2416{
2417 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2418}
2419
2420/* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
2421 SUBMENU may be NULL, in that case a new menu is created.
2422 F is the frame the menu bar belongs to.
2423 VAL describes the contents of the menu bar.
2424 SELECT_CB is the callback to use when a menu item is selected.
2425 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2426 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2427 CL_DATA is the call back data to use for any newly created items.
2428
2429 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
2430 was NULL. */
2431
2432static GtkWidget *
2433xg_update_submenu (submenu, f, val,
2434 select_cb, deactivate_cb, highlight_cb, cl_data)
2435 GtkWidget *submenu;
2436 FRAME_PTR f;
2437 widget_value *val;
2438 GCallback select_cb;
2439 GCallback deactivate_cb;
2440 GCallback highlight_cb;
2441 xg_menu_cb_data *cl_data;
2442{
2443 GtkWidget *newsub = submenu;
2444 GList *list = 0;
2445 GList *iter;
2446 widget_value *cur;
2447 int has_tearoff_p = 0;
2448 GList *first_radio = 0;
177c0ea7 2449
f392e843
JD
2450 if (submenu)
2451 list = gtk_container_get_children (GTK_CONTAINER (submenu));
177c0ea7 2452
f392e843
JD
2453 for (cur = val, iter = list;
2454 cur && iter;
2455 iter = g_list_next (iter), cur = cur->next)
2456 {
2457 GtkWidget *w = GTK_WIDGET (iter->data);
2458
2459 /* Skip tearoff items, they have no counterpart in val. */
2460 if (GTK_IS_TEAROFF_MENU_ITEM (w))
2461 {
2462 has_tearoff_p = 1;
2463 iter = g_list_next (iter);
2464 if (iter) w = GTK_WIDGET (iter->data);
2465 else break;
2466 }
2467
2468 /* Remember first radio button in a group. If we get a mismatch in
2469 a radio group we must rebuild the whole group so that the connections
2470 in GTK becomes correct. */
2471 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
2472 first_radio = iter;
2473 else if (cur->button_type != BUTTON_TYPE_RADIO
2474 && ! GTK_IS_RADIO_MENU_ITEM (w))
2475 first_radio = 0;
2476
2477 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
2478 {
2479 if (! xg_separator_p (cur->name))
2480 break;
2481 }
2482 else if (GTK_IS_CHECK_MENU_ITEM (w))
2483 {
2484 if (cur->button_type != BUTTON_TYPE_TOGGLE)
2485 break;
2486 xg_update_toggle_item (cur, w);
2487 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2488 }
2489 else if (GTK_IS_RADIO_MENU_ITEM (w))
2490 {
2491 if (cur->button_type != BUTTON_TYPE_RADIO)
2492 break;
2493 xg_update_radio_item (cur, w);
2494 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2495 }
2496 else if (GTK_IS_MENU_ITEM (w))
2497 {
2498 GtkMenuItem *witem = GTK_MENU_ITEM (w);
2499 GtkWidget *sub;
2500
2501 if (cur->button_type != BUTTON_TYPE_NONE ||
2502 xg_separator_p (cur->name))
2503 break;
2504
2505 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2506
2507 sub = gtk_menu_item_get_submenu (witem);
2508 if (sub && ! cur->contents)
2509 {
2510 /* Not a submenu anymore. */
2511 gtk_widget_ref (sub);
2512 gtk_menu_item_remove_submenu (witem);
2513 gtk_widget_destroy (sub);
2514 }
2515 else if (cur->contents)
2516 {
2517 GtkWidget *nsub;
2518
2519 nsub = xg_update_submenu (sub, f, cur->contents,
2520 select_cb, deactivate_cb,
2521 highlight_cb, cl_data);
2522
2523 /* If this item just became a submenu, we must set it. */
2524 if (nsub != sub)
2525 gtk_menu_item_set_submenu (witem, nsub);
2526 }
2527 }
2528 else
2529 {
2530 /* Structural difference. Remove everything from here and down
2531 in SUBMENU. */
2532 break;
2533 }
2534 }
2535
2536 /* Remove widgets from first structual change. */
2537 if (iter)
2538 {
2539 /* If we are adding new menu items below, we must remove from
2540 first radio button so that radio groups become correct. */
71bacd48
JD
2541 if (cur && first_radio) xg_destroy_widgets (first_radio);
2542 else xg_destroy_widgets (iter);
f392e843 2543 }
177c0ea7 2544
f392e843
JD
2545 if (cur)
2546 {
2547 /* More items added. Create them. */
2548 newsub = create_menus (cur,
2549 f,
2550 select_cb,
2551 deactivate_cb,
2552 highlight_cb,
2553 0,
2554 0,
2555 ! has_tearoff_p,
2556 submenu,
2557 cl_data,
2558 0);
2559 }
177c0ea7 2560
49853a4d
JD
2561 if (list) g_list_free (list);
2562
f392e843
JD
2563 return newsub;
2564}
2565
2566/* Update the MENUBAR.
2567 F is the frame the menu bar belongs to.
2568 VAL describes the contents of the menu bar.
2569 If DEEP_P is non-zero, rebuild all but the top level menu names in
2570 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
2571 SELECT_CB is the callback to use when a menu item is selected.
2572 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2573 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
71bacd48 2574
f392e843
JD
2575void
2576xg_modify_menubar_widgets (menubar, f, val, deep_p,
2577 select_cb, deactivate_cb, highlight_cb)
2578 GtkWidget *menubar;
2579 FRAME_PTR f;
2580 widget_value *val;
2581 int deep_p;
2582 GCallback select_cb;
2583 GCallback deactivate_cb;
2584 GCallback highlight_cb;
2585{
2586 xg_menu_cb_data *cl_data;
2587 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
f392e843
JD
2588
2589 if (! list) return;
177c0ea7 2590
f392e843
JD
2591 cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
2592 XG_FRAME_DATA);
2593
da18b5ac
JD
2594 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
2595 select_cb, highlight_cb, cl_data);
2596
2597 if (deep_p);
f392e843
JD
2598 {
2599 widget_value *cur;
2600
2601 /* Update all sub menus.
da18b5ac 2602 We must keep the submenus (GTK menu item widgets) since the
f392e843
JD
2603 X Window in the XEvent that activates the menu are those widgets. */
2604
2605 /* Update cl_data, menu_item things in F may have changed. */
2606 update_cl_data (cl_data, f, highlight_cb);
2607
2608 for (cur = val->contents; cur; cur = cur->next)
2609 {
49853a4d 2610 GList *iter;
f392e843
JD
2611 GtkWidget *sub = 0;
2612 GtkWidget *newsub;
2613 GtkMenuItem *witem;
2614
2615 /* Find sub menu that corresponds to val and update it. */
2616 for (iter = list ; iter; iter = g_list_next (iter))
2617 {
2618 witem = GTK_MENU_ITEM (iter->data);
2619 if (xg_item_label_same_p (witem, cur->name))
2620 {
2621 sub = gtk_menu_item_get_submenu (witem);
2622 break;
2623 }
2624 }
177c0ea7 2625
f392e843
JD
2626 newsub = xg_update_submenu (sub,
2627 f,
2628 cur->contents,
2629 select_cb,
2630 deactivate_cb,
2631 highlight_cb,
2632 cl_data);
2633 /* sub may still be NULL. If we just updated non deep and added
2634 a new menu bar item, it has no sub menu yet. So we set the
2635 newly created sub menu under witem. */
2636 if (newsub != sub)
810f2256
JD
2637 {
2638 xg_set_screen (newsub, f);
2639 gtk_menu_item_set_submenu (witem, newsub);
2640 }
f392e843
JD
2641 }
2642 }
2643
49853a4d 2644 g_list_free (list);
f392e843
JD
2645 gtk_widget_show_all (menubar);
2646}
2647
2648/* Recompute all the widgets of frame F, when the menu bar has been
2649 changed. Value is non-zero if widgets were updated. */
2650
2651int
2652xg_update_frame_menubar (f)
2653 FRAME_PTR f;
2654{
2655 struct x_output *x = f->output_data.x;
2656 GtkRequisition req;
177c0ea7 2657
f392e843
JD
2658 if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
2659 return 0;
2660
2661 BLOCK_INPUT;
2662
2663 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
2664 FALSE, FALSE, 0);
2665 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
2666
2667 gtk_widget_show_all (x->menubar_widget);
2668 gtk_widget_size_request (x->menubar_widget, &req);
2669
2670 FRAME_MENUBAR_HEIGHT (f) = req.height;
2671
2672 /* The height has changed, resize outer widget and set columns
2673 rows to what we had before adding the menu bar. */
be786000 2674 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
177c0ea7 2675
f392e843
JD
2676 SET_FRAME_GARBAGED (f);
2677 UNBLOCK_INPUT;
a8303f92
AS
2678
2679 return 1;
f392e843
JD
2680}
2681
2682/* Get rid of the menu bar of frame F, and free its storage.
2683 This is used when deleting a frame, and when turning off the menu bar. */
2684
2685void
2686free_frame_menubar (f)
2687 FRAME_PTR f;
2688{
2689 struct x_output *x = f->output_data.x;
2690
2691 if (x->menubar_widget)
2692 {
2693 BLOCK_INPUT;
2694
2695 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
2696 /* The menubar and its children shall be deleted when removed from
2697 the container. */
2698 x->menubar_widget = 0;
2699 FRAME_MENUBAR_HEIGHT (f) = 0;
2700
2701 /* The height has changed, resize outer widget and set columns
2702 rows to what we had before removing the menu bar. */
be786000 2703 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
2704
2705 SET_FRAME_GARBAGED (f);
2706 UNBLOCK_INPUT;
2707 }
2708}
2709
2710
2711\f
2712/***********************************************************************
2713 Scroll bar functions
2714 ***********************************************************************/
2715
2716
2717/* Setting scroll bar values invokes the callback. Use this variable
2718 to indicate that callback should do nothing. */
71bacd48 2719
f392e843
JD
2720int xg_ignore_gtk_scrollbar;
2721
f392e843
JD
2722/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
2723 32 bits. But we want to store pointers, and they may be larger
2724 than 32 bits. Keep a mapping from integer index to widget pointers
2725 to get around the 32 bit limitation. */
71bacd48 2726
f392e843
JD
2727static struct
2728{
2729 GtkWidget **widgets;
2730 int max_size;
2731 int used;
81e302ef 2732} id_to_widget;
f392e843
JD
2733
2734/* Grow this much every time we need to allocate more */
71bacd48 2735
f392e843
JD
2736#define ID_TO_WIDGET_INCR 32
2737
2738/* Store the widget pointer W in id_to_widget and return the integer index. */
71bacd48 2739
f392e843
JD
2740static int
2741xg_store_widget_in_map (w)
2742 GtkWidget *w;
2743{
2744 int i;
2745
2746 if (id_to_widget.max_size == id_to_widget.used)
2747 {
2748 int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
2749
2750 id_to_widget.widgets = xrealloc (id_to_widget.widgets,
2751 sizeof (GtkWidget *)*new_size);
2752
2753 for (i = id_to_widget.max_size; i < new_size; ++i)
2754 id_to_widget.widgets[i] = 0;
2755 id_to_widget.max_size = new_size;
2756 }
2757
2758 /* Just loop over the array and find a free place. After all,
2759 how many scroll bars are we creating? Should be a small number.
2760 The check above guarantees we will find a free place. */
2761 for (i = 0; i < id_to_widget.max_size; ++i)
2762 {
2763 if (! id_to_widget.widgets[i])
2764 {
2765 id_to_widget.widgets[i] = w;
2766 ++id_to_widget.used;
2767
2768 return i;
2769 }
2770 }
2771
2772 /* Should never end up here */
2773 abort ();
2774}
2775
2776/* Remove pointer at IDX from id_to_widget.
2777 Called when scroll bar is destroyed. */
71bacd48 2778
f392e843
JD
2779static void
2780xg_remove_widget_from_map (idx)
2781 int idx;
2782{
2783 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2784 {
2785 id_to_widget.widgets[idx] = 0;
2786 --id_to_widget.used;
2787 }
2788}
2789
2790/* Get the widget pointer at IDX from id_to_widget. */
71bacd48 2791
f392e843
JD
2792static GtkWidget *
2793xg_get_widget_from_map (idx)
2794 int idx;
2795{
2796 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2797 return id_to_widget.widgets[idx];
2798
2799 return 0;
2800}
2801
810f2256 2802/* Return the scrollbar id for X Window WID on display DPY.
3a8a22fc 2803 Return -1 if WID not in id_to_widget. */
71bacd48 2804
3a8a22fc 2805int
810f2256
JD
2806xg_get_scroll_id_for_window (dpy, wid)
2807 Display *dpy;
3a8a22fc
JD
2808 Window wid;
2809{
2810 int idx;
2811 GtkWidget *w;
2812
810f2256 2813 w = xg_win_to_widget (dpy, wid);
3a8a22fc
JD
2814
2815 if (w)
2816 {
2817 for (idx = 0; idx < id_to_widget.max_size; ++idx)
2818 if (id_to_widget.widgets[idx] == w)
2819 return idx;
2820 }
2821
2822 return -1;
2823}
2824
f392e843
JD
2825/* Callback invoked when scroll bar WIDGET is destroyed.
2826 DATA is the index into id_to_widget for WIDGET.
cea9be54 2827 We free pointer to last scroll bar values here and remove the index. */
71bacd48 2828
f392e843
JD
2829static void
2830xg_gtk_scroll_destroy (widget, data)
2831 GtkWidget *widget;
2832 gpointer data;
2833{
2834 gpointer p;
2835 int id = (int)data;
177c0ea7 2836
f392e843
JD
2837 p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
2838 if (p) xfree (p);
2839 xg_remove_widget_from_map (id);
2840}
2841
2842/* Callback for button press/release events. Used to start timer so that
2843 the scroll bar repetition timer in GTK gets handeled.
17097258 2844 Also, sets bar->dragging to Qnil when dragging (button release) is done.
f392e843
JD
2845 WIDGET is the scroll bar widget the event is for (not used).
2846 EVENT contains the event.
17097258 2847 USER_DATA points to the struct scrollbar structure.
f392e843
JD
2848
2849 Returns FALSE to tell GTK that it shall continue propagate the event
2850 to widgets. */
71bacd48 2851
f392e843
JD
2852static gboolean
2853scroll_bar_button_cb (widget, event, user_data)
2854 GtkWidget *widget;
2855 GdkEventButton *event;
2856 gpointer user_data;
2857{
2858 if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
2859 xg_start_timer ();
17097258
JD
2860 else if (event->type == GDK_BUTTON_RELEASE)
2861 {
2862 struct scroll_bar *bar = (struct scroll_bar *) user_data;
2863 if (xg_timer) xg_stop_timer ();
2864 bar->dragging = Qnil;
2865 }
2866
f392e843
JD
2867 return FALSE;
2868}
2869
2870/* Create a scroll bar widget for frame F. Store the scroll bar
2871 in BAR.
2872 SCROLL_CALLBACK is the callback to invoke when the value of the
2873 bar changes.
2874 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
2875 to set resources for the widget. */
71bacd48 2876
f392e843
JD
2877void
2878xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
2879 FRAME_PTR f;
2880 struct scroll_bar *bar;
2881 GCallback scroll_callback;
2882 char *scroll_bar_name;
2883{
2884 GtkWidget *wscroll;
1755a397 2885 GtkWidget *webox;
f392e843
JD
2886 GtkObject *vadj;
2887 int scroll_id;
177c0ea7 2888
f392e843
JD
2889 /* Page, step increment values are not so important here, they
2890 will be corrected in x_set_toolkit_scroll_bar_thumb. */
2891 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
2892 0.1, 0.1, 0.1);
2893
2894 wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
1755a397 2895 webox = gtk_event_box_new ();
f392e843
JD
2896 gtk_widget_set_name (wscroll, scroll_bar_name);
2897 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
177c0ea7 2898
f392e843 2899 scroll_id = xg_store_widget_in_map (wscroll);
177c0ea7 2900
f979dc05 2901 g_signal_connect (G_OBJECT (wscroll),
f392e843
JD
2902 "value-changed",
2903 scroll_callback,
f979dc05 2904 (gpointer) bar);
f392e843
JD
2905 g_signal_connect (G_OBJECT (wscroll),
2906 "destroy",
2907 G_CALLBACK (xg_gtk_scroll_destroy),
f979dc05 2908 (gpointer) scroll_id);
f392e843
JD
2909
2910 /* Connect to button press and button release to detect if any scroll bar
2911 has the pointer. */
2912 g_signal_connect (G_OBJECT (wscroll),
2913 "button-press-event",
2914 G_CALLBACK (scroll_bar_button_cb),
f979dc05 2915 (gpointer) bar);
f392e843
JD
2916 g_signal_connect (G_OBJECT (wscroll),
2917 "button-release-event",
2918 G_CALLBACK (scroll_bar_button_cb),
f979dc05 2919 (gpointer) bar);
177c0ea7 2920
1755a397
JD
2921 /* The scroll bar widget does not draw on a window of its own. Instead
2922 it draws on the parent window, in this case the edit widget. So
2923 whenever the edit widget is cleared, the scroll bar needs to redraw
2924 also, which causes flicker. Put an event box between the edit widget
2925 and the scroll bar, so the scroll bar instead draws itself on the
2926 event box window. */
2927 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
2928 gtk_container_add (GTK_CONTAINER (webox), wscroll);
2929
f392e843
JD
2930
2931 /* Set the cursor to an arrow. */
1755a397 2932 xg_set_cursor (webox, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
f392e843
JD
2933
2934 SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
2935}
2936
2937/* Make the scroll bar represented by SCROLLBAR_ID visible. */
71bacd48 2938
f392e843
JD
2939void
2940xg_show_scroll_bar (scrollbar_id)
2941 int scrollbar_id;
2942{
2943 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2944 if (w)
1755a397 2945 gtk_widget_show_all (gtk_widget_get_parent (w));
f392e843
JD
2946}
2947
2948/* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
71bacd48 2949
f392e843
JD
2950void
2951xg_remove_scroll_bar (f, scrollbar_id)
2952 FRAME_PTR f;
2953 int scrollbar_id;
2954{
2955 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2956 if (w)
2957 {
1755a397 2958 GtkWidget *wparent = gtk_widget_get_parent (w);
f392e843 2959 gtk_widget_destroy (w);
1755a397 2960 gtk_widget_destroy (wparent);
f392e843
JD
2961 SET_FRAME_GARBAGED (f);
2962 }
2963}
2964
f392e843
JD
2965/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
2966 in frame F.
2967 TOP/LEFT are the new pixel positions where the bar shall appear.
2968 WIDTH, HEIGHT is the size in pixels the bar shall have. */
71bacd48 2969
f392e843 2970void
1755a397 2971xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height)
f392e843
JD
2972 FRAME_PTR f;
2973 int scrollbar_id;
2974 int top;
2975 int left;
2976 int width;
2977 int height;
2978{
f392e843 2979
49853a4d 2980 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
7863d625 2981
49853a4d
JD
2982 if (wscroll)
2983 {
cea9be54 2984 GtkWidget *wfixed = f->output_data.x->edit_widget;
1755a397 2985 GtkWidget *wparent = gtk_widget_get_parent (wscroll);
7863d625 2986
ab2d724b 2987 /* Move and resize to new values. */
ab2d724b 2988 gtk_widget_set_size_request (wscroll, width, height);
1755a397 2989 gtk_fixed_move (GTK_FIXED (wfixed), wparent, left, top);
17097258 2990
49853a4d
JD
2991 SET_FRAME_GARBAGED (f);
2992 cancel_mouse_face (f);
2993 }
f392e843
JD
2994}
2995
2996/* Set the thumb size and position of scroll bar BAR. We are currently
2997 displaying PORTION out of a whole WHOLE, and our position POSITION. */
71bacd48 2998
f392e843
JD
2999void
3000xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
3001 struct scroll_bar *bar;
3002 int portion, position, whole;
3003{
3004 GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
3005
3006 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
3007
17097258 3008 if (wscroll && NILP (bar->dragging))
f392e843
JD
3009 {
3010 GtkAdjustment *adj;
3011 gdouble shown;
3012 gdouble top;
3013 int size, value;
7863d625
JD
3014 int new_step;
3015 int changed = 0;
177c0ea7 3016
f392e843
JD
3017 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
3018
17097258
JD
3019 /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
3020 rather than the real portion value. This makes the thumb less likely
3021 to resize and that looks better. */
be786000 3022 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
17097258
JD
3023 /* When the thumb is at the bottom, position == whole.
3024 So we need to increase `whole' to make space for the thumb. */
3025 whole += portion;
3026
f392e843
JD
3027 if (whole <= 0)
3028 top = 0, shown = 1;
3029 else
3030 {
f392e843 3031 top = (gdouble) position / whole;
7863d625 3032 shown = (gdouble) portion / whole;
f392e843
JD
3033 }
3034
7863d625
JD
3035 size = shown * XG_SB_RANGE;
3036 size = min (size, XG_SB_RANGE);
f392e843
JD
3037 size = max (size, 1);
3038
7863d625
JD
3039 value = top * XG_SB_RANGE;
3040 value = min (value, XG_SB_MAX - size);
f392e843
JD
3041 value = max (value, XG_SB_MIN);
3042
7863d625 3043 /* Assume all lines are of equal size. */
be786000 3044 new_step = size / max (1, FRAME_LINES (f));
2a2071c3
JD
3045
3046 if ((int) adj->page_size != size
2a2071c3
JD
3047 || (int) adj->step_increment != new_step)
3048 {
7863d625
JD
3049 adj->page_size = size;
3050 adj->step_increment = new_step;
2a2071c3 3051 /* Assume a page increment is about 95% of the page size */
7863d625
JD
3052 adj->page_increment = (int) (0.95*adj->page_size);
3053 changed = 1;
2a2071c3 3054 }
17097258 3055
7863d625
JD
3056 if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3057 {
3058 GtkWidget *wfixed = f->output_data.x->edit_widget;
17097258 3059
7863d625 3060 BLOCK_INPUT;
cea9be54 3061
7863d625
JD
3062 /* gtk_range_set_value invokes the callback. Set
3063 ignore_gtk_scrollbar to make the callback do nothing */
3064 xg_ignore_gtk_scrollbar = 1;
f392e843 3065
7863d625
JD
3066 if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
3067 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
3068 else if (changed)
3069 gtk_adjustment_changed (adj);
3070
3071 xg_ignore_gtk_scrollbar = 0;
3072
3073 UNBLOCK_INPUT;
3074 }
3075 }
f392e843
JD
3076}
3077
3078\f
3079/***********************************************************************
3080 Tool bar functions
3081 ***********************************************************************/
3082/* The key for the data we put in the GtkImage widgets. The data is
3083 the image used by Emacs. We use this to see if we need to update
3084 the GtkImage with a new image. */
3085#define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
3086
3087/* Callback function invoked when a tool bar item is pressed.
3088 W is the button widget in the tool bar that got pressed,
3089 CLIENT_DATA is an integer that is the index of the button in the
3090 tool bar. 0 is the first button. */
71bacd48 3091
f392e843
JD
3092static void
3093xg_tool_bar_callback (w, client_data)
3094 GtkWidget *w;
3095 gpointer client_data;
3096{
3097 int idx = (int)client_data;
3098 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
3099 Lisp_Object key, frame;
3100 struct input_event event;
aa4ac494 3101 EVENT_INIT (event);
f392e843
JD
3102
3103 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
3104 return;
3105
3106 idx *= TOOL_BAR_ITEM_NSLOTS;
177c0ea7 3107
f392e843
JD
3108 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
3109 XSETFRAME (frame, f);
3110 event.kind = TOOL_BAR_EVENT;
3111 event.frame_or_window = frame;
3112 event.arg = frame;
3113 kbd_buffer_store_event (&event);
3114
3115 event.kind = TOOL_BAR_EVENT;
3116 event.frame_or_window = frame;
3117 event.arg = key;
3118 event.modifiers = 0; /* These are not available. */
3119 kbd_buffer_store_event (&event);
3120}
3121
3122/* This callback is called when a tool bar is detached. We must set
3123 the height of the tool bar to zero when this happens so frame sizes
3124 are correctly calculated.
3125 WBOX is the handle box widget that enables detach/attach of the tool bar.
3126 W is the tool bar widget.
3127 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
71bacd48 3128
f392e843
JD
3129static void
3130xg_tool_bar_detach_callback (wbox, w, client_data)
3131 GtkHandleBox *wbox;
3132 GtkWidget *w;
3133 gpointer client_data;
3134{
3135 FRAME_PTR f = (FRAME_PTR) client_data;
3136
3137 if (f)
3138 {
3139 /* When detaching a tool bar, not everything dissapear. There are
3140 a few pixels left that are used to drop the tool bar back into
3141 place. */
3142 int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox));
3143 FRAME_TOOLBAR_HEIGHT (f) = 2;
3144
3145 /* The height has changed, resize outer widget and set columns
3146 rows to what we had before detaching the tool bar. */
be786000 3147 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
3148 }
3149}
3150
3151/* This callback is called when a tool bar is reattached. We must set
3152 the height of the tool bar when this happens so frame sizes
3153 are correctly calculated.
3154 WBOX is the handle box widget that enables detach/attach of the tool bar.
3155 W is the tool bar widget.
3156 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
71bacd48 3157
f392e843
JD
3158static void
3159xg_tool_bar_attach_callback (wbox, w, client_data)
3160 GtkHandleBox *wbox;
3161 GtkWidget *w;
3162 gpointer client_data;
3163{
3164 FRAME_PTR f = (FRAME_PTR) client_data;
3165
3166 if (f)
3167 {
3168 GtkRequisition req;
3169
3170 gtk_widget_size_request (w, &req);
3171 FRAME_TOOLBAR_HEIGHT (f) = req.height;
3172
3173 /* The height has changed, resize outer widget and set columns
3174 rows to what we had before detaching the tool bar. */
be786000 3175 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
3176 }
3177}
3178
3179/* This callback is called when the mouse enters or leaves a tool bar item.
3180 It is used for displaying and hiding the help text.
3181 W is the tool bar item, a button.
3182 EVENT is either an enter event or leave event.
3183 CLIENT_DATA is an integer that is the index of the button in the
3184 tool bar. 0 is the first button.
3185
3186 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 3187
f392e843
JD
3188static gboolean
3189xg_tool_bar_help_callback (w, event, client_data)
3190 GtkWidget *w;
3191 GdkEventCrossing *event;
3192 gpointer client_data;
3193{
3194 int idx = (int)client_data;
3195 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
3196 Lisp_Object help, frame;
3197
3198 if (! GTK_IS_BUTTON (w))
3199 {
3200 return FALSE;
3201 }
3202
3203 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
d9301435 3204 return FALSE;
f392e843
JD
3205
3206 if (event->type == GDK_ENTER_NOTIFY)
3207 {
3208 idx *= TOOL_BAR_ITEM_NSLOTS;
3209 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
3210
3211 if (NILP (help))
3212 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
3213 }
3214 else
3215 help = Qnil;
3216
3217 XSETFRAME (frame, f);
3218 kbd_buffer_store_help_event (frame, help);
3219
3220 return FALSE;
3221}
3222
3223
f098b121
JD
3224/* This callback is called when a tool bar item shall be redrawn.
3225 It modifies the expose event so that the GtkImage widget redraws the
3226 whole image. This to overcome a bug that makes GtkImage draw the image
3227 in the wrong place when it tries to redraw just a part of the image.
3228 W is the GtkImage to be redrawn.
3229 EVENT is the expose event for W.
3230 CLIENT_DATA is unused.
3231
3232 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 3233
f098b121 3234static gboolean
3a8a22fc 3235xg_tool_bar_item_expose_callback (w, event, client_data)
f098b121
JD
3236 GtkWidget *w;
3237 GdkEventExpose *event;
3238 gpointer client_data;
3239{
b676f356
JD
3240 gint width, height;
3241
3242 gdk_drawable_get_size (event->window, &width, &height);
3243
3244 event->area.x -= width > event->area.width ? width-event->area.width : 0;
3245 event->area.y -= height > event->area.height ? height-event->area.height : 0;
3246
810f2256
JD
3247 event->area.x = max (0, event->area.x);
3248 event->area.y = max (0, event->area.y);
b676f356
JD
3249
3250 event->area.width = max (width, event->area.width);
3251 event->area.height = max (height, event->area.height);
3252
f098b121
JD
3253 return FALSE;
3254}
3255
3a8a22fc
JD
3256/* This callback is called when a tool bar shall be redrawn.
3257 We need to update the tool bar from here in case the image cache
3258 has deleted the pixmaps used in the tool bar.
3259 W is the GtkToolbar to be redrawn.
3260 EVENT is the expose event for W.
3261 CLIENT_DATA is pointing to the frame for this tool bar.
3262
3263 Returns FALSE to tell GTK to keep processing this event. */
71bacd48 3264
3a8a22fc
JD
3265static gboolean
3266xg_tool_bar_expose_callback (w, event, client_data)
3267 GtkWidget *w;
3268 GdkEventExpose *event;
3269 gpointer client_data;
3270{
810f2256 3271 update_frame_tool_bar ((FRAME_PTR) client_data);
3a8a22fc
JD
3272 return FALSE;
3273}
3274
71bacd48
JD
3275/* Create a tool bar for frame F. */
3276
f392e843
JD
3277static void
3278xg_create_tool_bar (f)
3279 FRAME_PTR f;
3280{
3281 struct x_output *x = f->output_data.x;
3282 GtkRequisition req;
3283 int vbox_pos = x->menubar_widget ? 1 : 0;
3284
3285 x->toolbar_widget = gtk_toolbar_new ();
3286 x->handlebox_widget = gtk_handle_box_new ();
3287 gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
3288 x->toolbar_widget);
177c0ea7 3289
f392e843
JD
3290 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
3291 FALSE, FALSE, 0);
177c0ea7 3292
f392e843
JD
3293 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
3294 vbox_pos);
3295
f098b121
JD
3296 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
3297
3298 /* We only have icons, so override any user setting. We could
3299 use the caption property of the toolbar item (see update_frame_tool_bar
3300 below), but some of those strings are long, making the toolbar so
3301 long it does not fit on the screen. The GtkToolbar widget makes every
3302 item equal size, so the longest caption determine the size of every
3303 tool bar item. I think the creators of the GtkToolbar widget
3304 counted on 4 or 5 character long strings. */
3305 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
3306 gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
3307 GTK_ORIENTATION_HORIZONTAL);
3308
f392e843
JD
3309 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
3310 G_CALLBACK (xg_tool_bar_detach_callback), f);
3311 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
3312 G_CALLBACK (xg_tool_bar_attach_callback), f);
3a8a22fc
JD
3313 g_signal_connect (G_OBJECT (x->toolbar_widget),
3314 "expose-event",
3315 G_CALLBACK (xg_tool_bar_expose_callback),
3316 f);
f392e843
JD
3317
3318 gtk_widget_show_all (x->handlebox_widget);
3319
3320 gtk_widget_size_request (x->toolbar_widget, &req);
3321 FRAME_TOOLBAR_HEIGHT (f) = req.height;
177c0ea7 3322
f392e843
JD
3323 /* The height has changed, resize outer widget and set columns
3324 rows to what we had before adding the tool bar. */
be786000 3325 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
177c0ea7 3326
f392e843
JD
3327 SET_FRAME_GARBAGED (f);
3328}
3329
71bacd48
JD
3330/* Update the tool bar for frame F. Add new buttons and remove old. */
3331
f392e843
JD
3332void
3333update_frame_tool_bar (f)
3334 FRAME_PTR f;
3335{
3336 int i;
3337 GtkRequisition old_req, new_req;
3338 GList *icon_list;
49853a4d 3339 GList *iter;
f392e843
JD
3340 struct x_output *x = f->output_data.x;
3341
3342 if (! FRAME_GTK_WIDGET (f))
3343 return;
3344
3345 BLOCK_INPUT;
177c0ea7 3346
f392e843
JD
3347 if (! x->toolbar_widget)
3348 xg_create_tool_bar (f);
3349
3350 gtk_widget_size_request (x->toolbar_widget, &old_req);
3351
3352 icon_list = gtk_container_get_children (GTK_CONTAINER (x->toolbar_widget));
49853a4d 3353 iter = icon_list;
177c0ea7 3354
f392e843
JD
3355 for (i = 0; i < f->n_tool_bar_items; ++i)
3356 {
3357#define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
3358
3359 int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
3360 int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
3361 int idx;
3362 int img_id;
3363 struct image *img;
3364 Lisp_Object image;
49853a4d 3365 GtkWidget *wicon = iter ? GTK_WIDGET (iter->data) : 0;
26b74a0b 3366
49853a4d 3367 if (iter) iter = g_list_next (iter);
f392e843
JD
3368
3369 /* If image is a vector, choose the image according to the
3370 button state. */
3371 image = PROP (TOOL_BAR_ITEM_IMAGES);
3372 if (VECTORP (image))
3373 {
3374 if (enabled_p)
3375 idx = (selected_p
3376 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
3377 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
3378 else
3379 idx = (selected_p
3380 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
3381 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
3382
3383 xassert (ASIZE (image) >= idx);
3384 image = AREF (image, idx);
3385 }
3386 else
3387 idx = -1;
3388
3389 /* Ignore invalid image specifications. */
3390 if (!valid_image_p (image))
3391 {
3392 if (wicon) gtk_widget_hide (wicon);
3393 continue;
3394 }
3395
3396 img_id = lookup_image (f, image);
3397 img = IMAGE_FROM_ID (f, img_id);
1d1885fc 3398 prepare_image_for_display (f, img);
f392e843 3399
1d1885fc
JD
3400 if (img->load_failed_p || img->pixmap == None)
3401 {
3402 if (wicon) gtk_widget_hide (wicon);
3403 continue;
3404 }
177c0ea7 3405
f392e843
JD
3406 if (! wicon)
3407 {
5b166323 3408 GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
f392e843 3409
f392e843
JD
3410 gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
3411 0, 0, 0,
3412 w,
3413 GTK_SIGNAL_FUNC (xg_tool_bar_callback),
3414 (gpointer)i);
177c0ea7 3415
f392e843
JD
3416 /* Save the image so we can see if an update is needed when
3417 this function is called again. */
3418 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
26b74a0b 3419 (gpointer)img->pixmap);
f392e843 3420
f098b121 3421 /* Catch expose events to overcome an annoying redraw bug, see
3a8a22fc 3422 comment for xg_tool_bar_item_expose_callback. */
f098b121
JD
3423 g_signal_connect (G_OBJECT (w),
3424 "expose-event",
3a8a22fc 3425 G_CALLBACK (xg_tool_bar_item_expose_callback),
f098b121
JD
3426 0);
3427
f392e843
JD
3428 /* We must set sensitive on the button that is the parent
3429 of the GtkImage parent. Go upwards until we find the button. */
3430 while (! GTK_IS_BUTTON (w))
3431 w = gtk_widget_get_parent (w);
177c0ea7 3432
f392e843
JD
3433 if (w)
3434 {
3435 /* Save the frame in the button so the xg_tool_bar_callback
3436 can get at it. */
3437 g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
3438 gtk_widget_set_sensitive (w, enabled_p);
3439
3440 /* Use enter/leave notify to show help. We use the events
3441 rather than the GtkButton specific signals "enter" and
3442 "leave", so we can have only one callback. The event
3443 will tell us what kind of event it is. */
3444 g_signal_connect (G_OBJECT (w),
3445 "enter-notify-event",
3446 G_CALLBACK (xg_tool_bar_help_callback),
3447 (gpointer)i);
3448 g_signal_connect (G_OBJECT (w),
3449 "leave-notify-event",
3450 G_CALLBACK (xg_tool_bar_help_callback),
3451 (gpointer)i);
3452 }
3453 }
3454 else
3455 {
3456 /* The child of the tool bar is a button. Inside that button
3457 is a vbox. Inside that vbox is the GtkImage. */
3458 GtkWidget *wvbox = gtk_bin_get_child (GTK_BIN (wicon));
49853a4d
JD
3459 GList *chlist = gtk_container_get_children (GTK_CONTAINER (wvbox));
3460 GtkImage *wimage = GTK_IMAGE (chlist->data);
26b74a0b
JD
3461 Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
3462 XG_TOOL_BAR_IMAGE_DATA);
49853a4d 3463 g_list_free (chlist);
f392e843 3464
26b74a0b 3465 if (old_img != img->pixmap)
5b166323 3466 (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
f392e843
JD
3467
3468 g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
26b74a0b 3469 (gpointer)img->pixmap);
f392e843
JD
3470
3471 gtk_widget_set_sensitive (wicon, enabled_p);
3472 gtk_widget_show (wicon);
3473 }
177c0ea7 3474
f392e843
JD
3475#undef PROP
3476 }
3477
3478 /* Remove buttons not longer needed. We just hide them so they
3479 can be reused later on. */
49853a4d 3480 while (iter)
f392e843 3481 {
49853a4d 3482 GtkWidget *w = GTK_WIDGET (iter->data);
f392e843 3483 gtk_widget_hide (w);
49853a4d 3484 iter = g_list_next (iter);
f392e843
JD
3485 }
3486
3487 gtk_widget_size_request (x->toolbar_widget, &new_req);
3488 if (old_req.height != new_req.height)
3489 {
3490 FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
be786000 3491 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
3492 }
3493
49853a4d
JD
3494 if (icon_list) g_list_free (icon_list);
3495
f392e843
JD
3496 UNBLOCK_INPUT;
3497}
3498
71bacd48
JD
3499/* Deallocate all resources for the tool bar on frame F.
3500 Remove the tool bar. */
3501
f392e843
JD
3502void
3503free_frame_tool_bar (f)
3504 FRAME_PTR f;
3505{
3506 struct x_output *x = f->output_data.x;
3507
3508 if (x->toolbar_widget)
3509 {
f392e843
JD
3510 BLOCK_INPUT;
3511 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
3512 x->handlebox_widget);
3513 x->toolbar_widget = 0;
3514 x->handlebox_widget = 0;
3515 FRAME_TOOLBAR_HEIGHT (f) = 0;
3516
3517 /* The height has changed, resize outer widget and set columns
3518 rows to what we had before removing the tool bar. */
be786000 3519 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
3520
3521 SET_FRAME_GARBAGED (f);
3522 UNBLOCK_INPUT;
3523 }
3524}
3525
3526
3527\f
3528/***********************************************************************
3529 Initializing
3530 ***********************************************************************/
3531void
3532xg_initialize ()
3533{
3534 xg_ignore_gtk_scrollbar = 0;
da18b5ac 3535 xg_detached_menus = 0;
f392e843
JD
3536 xg_menu_cb_list.prev = xg_menu_cb_list.next =
3537 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
3538
81e302ef
JD
3539 id_to_widget.max_size = id_to_widget.used = 0;
3540 id_to_widget.widgets = 0;
3541
f392e843
JD
3542 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
3543 bindings. It doesn't seem to be any way to remove properties,
3544 so we set it to VoidSymbol which in X means "no key". */
3545 gtk_settings_set_string_property (gtk_settings_get_default (),
3546 "gtk-menu-bar-accel",
3547 "VoidSymbol",
3548 EMACS_CLASS);
81e302ef
JD
3549
3550 /* Make GTK text input widgets use Emacs style keybindings. This is
3551 Emacs after all. */
3552 gtk_settings_set_string_property (gtk_settings_get_default (),
3553 "gtk-key-theme-name",
3554 "Emacs",
3555 EMACS_CLASS);
f392e843
JD
3556}
3557
3558#endif /* USE_GTK */
ab5796a9
MB
3559
3560/* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3
3561 (do not change this comment) */