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