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