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