*** empty log message ***
[emacs.git] / src / gtkutil.c
blob84aa9f46d4d15dc32341d048f9bebbc682751b7e
1 /* Functions for creating and updating GTK widgets.
2 Copyright (C) 2003
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
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 2, or (at your option)
10 any later version.
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.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 #include "config.h"
24 #ifdef USE_GTK
25 #include <string.h>
26 #include <stdio.h>
27 #include "lisp.h"
28 #include "xterm.h"
29 #include "blockinput.h"
30 #include "window.h"
31 #include "atimer.h"
32 #include "gtkutil.h"
33 #include "termhooks.h"
34 #include "keyboard.h"
35 #include "charset.h"
36 #include "coding.h"
37 #include <gdk/gdkkeysyms.h>
40 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
41 (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
44 /***********************************************************************
45 Display handling functions
46 ***********************************************************************/
48 #ifdef HAVE_GTK_MULTIDISPLAY
50 /* Return the GdkDisplay that corresponds to the X display DPY. */
51 static GdkDisplay *
52 xg_get_gdk_display (dpy)
53 Display *dpy;
55 return gdk_x11_lookup_xdisplay (dpy);
58 /* When the GTK widget W is to be created on a display for F that
59 is not the default display, set the display for W.
60 W can be a GtkMenu or a GtkWindow widget. */
61 static void
62 xg_set_screen (w, f)
63 GtkWidget *w;
64 FRAME_PTR f;
66 if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ())
68 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
69 GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
71 if (GTK_IS_MENU (w))
72 gtk_menu_set_screen (GTK_MENU (w), gscreen);
73 else
74 gtk_window_set_screen (GTK_WINDOW (w), gscreen);
79 #else /* not HAVE_GTK_MULTIDISPLAY */
81 /* Make some defines so we can use the GTK 2.2 functions when
82 compiling with GTK 2.0. */
83 #define xg_set_screen(w, f)
84 #define gdk_xid_table_lookup_for_display(dpy, w) gdk_xid_table_lookup (w)
85 #define gdk_pixmap_foreign_new_for_display(dpy, p) gdk_pixmap_foreign_new (p)
86 #define gdk_cursor_new_for_display(dpy, c) gdk_cursor_new (c)
87 #define gdk_x11_lookup_xdisplay(dpy) 0
88 #define GdkDisplay void
90 #endif /* not HAVE_GTK_MULTIDISPLAY */
92 /* Open a display named by DISPLAY_NAME. The display is returned in *DPY.
93 *DPY is set to NULL if the display can't be opened.
95 Returns non-zero if display could be opened, zero if display could not
96 be opened, and less than zero if the GTK version doesn't support
97 multipe displays. */
98 int
99 xg_display_open (display_name, dpy)
100 char *display_name;
101 Display **dpy;
103 #ifdef HAVE_GTK_MULTIDISPLAY
104 GdkDisplay *gdpy;
106 gdpy = gdk_display_open (display_name);
107 *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
109 return gdpy != NULL;
111 #else /* not HAVE_GTK_MULTIDISPLAY */
113 return -1;
114 #endif /* not HAVE_GTK_MULTIDISPLAY */
118 void
119 xg_display_close (Display *dpy)
121 #ifdef HAVE_GTK_MULTIDISPLAY
122 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
124 /* GTK 2.2 has a bug that makes gdk_display_close crash (bug
125 http://bugzilla.gnome.org/show_bug.cgi?id=85715). This way
126 we can continue running, but there will be memory leaks. */
128 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 4
130 /* If this is the default display, we must change it before calling
131 dispose, otherwise it will crash. */
132 if (gdk_display_get_default () == gdpy)
134 struct x_display_info *dpyinfo;
135 Display *new_dpy = 0;
136 GdkDisplay *gdpy_new;
138 /* Find another display. */
139 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
140 if (dpyinfo->display != dpy)
142 new_dpy = dpyinfo->display;
143 break;
146 if (! new_dpy) return; /* Emacs will exit anyway. */
148 gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
149 gdk_display_manager_set_default_display (gdk_display_manager_get (),
150 gdpy_new);
153 g_object_run_dispose (G_OBJECT (gdpy));
155 #else
156 /* I hope this will be fixed in GTK 2.4. It is what bug 85715 says. */
157 gdk_display_close (gdpy);
158 #endif
159 #endif /* HAVE_GTK_MULTIDISPLAY */
163 /***********************************************************************
164 Utility functions
165 ***********************************************************************/
166 /* The timer for scroll bar repetition and menu bar timeouts.
167 NULL if no timer is started. */
168 static struct atimer *xg_timer;
171 /* The next two variables and functions are taken from lwlib. */
172 static widget_value *widget_value_free_list;
173 static int malloc_cpt;
175 /* Allocate a widget_value structure, either by taking one from the
176 widget_value_free_list or by malloc:ing a new one.
178 Return a pointer to the allocated structure. */
179 widget_value *
180 malloc_widget_value ()
182 widget_value *wv;
183 if (widget_value_free_list)
185 wv = widget_value_free_list;
186 widget_value_free_list = wv->free_list;
187 wv->free_list = 0;
189 else
191 wv = (widget_value *) malloc (sizeof (widget_value));
192 malloc_cpt++;
194 memset (wv, 0, sizeof (widget_value));
195 return wv;
198 /* This is analogous to free. It frees only what was allocated
199 by malloc_widget_value, and no substructures. */
200 void
201 free_widget_value (wv)
202 widget_value *wv;
204 if (wv->free_list)
205 abort ();
207 if (malloc_cpt > 25)
209 /* When the number of already allocated cells is too big,
210 We free it. */
211 free (wv);
212 malloc_cpt--;
214 else
216 wv->free_list = widget_value_free_list;
217 widget_value_free_list = wv;
222 /* Create and return the cursor to be used for popup menus and
223 scroll bars on display DPY. */
224 GdkCursor *
225 xg_create_default_cursor (dpy)
226 Display *dpy;
228 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
229 return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
232 /* For the image defined in IMG, make and return a GtkImage. For displays with
233 8 planes or less we must make a GdkPixbuf and apply the mask manually.
234 Otherwise the highlightning and dimming the tool bar code in GTK does
235 will look bad. For display with more than 8 planes we just use the
236 pixmap and mask directly. For monochrome displays, GTK doesn't seem
237 able to use external pixmaps, it looks bad whatever we do.
238 The image is defined on the display where frame F is.
239 WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
240 If OLD_WIDGET is NULL, a new widget is constructed and returned.
241 If OLD_WIDGET is not NULL, that widget is modified. */
242 static GtkWidget *
243 xg_get_image_for_pixmap (f, img, widget, old_widget)
244 FRAME_PTR f;
245 struct image *img;
246 GtkWidget *widget;
247 GtkImage *old_widget;
249 GdkPixmap *gpix;
250 GdkPixmap *gmask;
251 GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
253 gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
254 gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0;
256 if (x_screen_planes (f) > 8 || x_screen_planes (f) == 1)
258 if (! old_widget)
259 old_widget = GTK_IMAGE (gtk_image_new_from_pixmap (gpix, gmask));
260 else
261 gtk_image_set_from_pixmap (old_widget, gpix, gmask);
263 else
265 int x, y, width, height, rowstride, mask_rowstride;
266 GdkPixbuf *icon_buf, *tmp_buf;
267 guchar *pixels;
268 guchar *mask_pixels;
270 gdk_drawable_get_size (gpix, &width, &height);
271 tmp_buf = gdk_pixbuf_get_from_drawable (NULL,
272 gpix,
273 gtk_widget_get_colormap (widget),
274 0, 0, 0, 0, width, height);
275 icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
276 g_object_unref (G_OBJECT (tmp_buf));
278 if (gmask)
280 GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
281 gmask,
282 NULL,
283 0, 0, 0, 0,
284 width, height);
285 guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
286 guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
287 int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
288 int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
289 int y;
291 for (y = 0; y < height; ++y)
293 guchar *iconptr, *maskptr;
294 int x;
296 iconptr = pixels + y * rowstride;
297 maskptr = mask_pixels + y * mask_rowstride;
299 for (x = 0; x < width; ++x)
301 /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking
302 just R is sufficient. */
303 if (maskptr[0] == 0)
304 iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */
306 iconptr += rowstride/width;
307 maskptr += mask_rowstride/width;
311 g_object_unref (G_OBJECT (gmask));
312 g_object_unref (G_OBJECT (mask_buf));
315 g_object_unref (G_OBJECT (gpix));
317 if (! old_widget)
318 old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
319 else
320 gtk_image_set_from_pixbuf (old_widget, icon_buf);
322 g_object_unref (G_OBJECT (icon_buf));
325 return GTK_WIDGET (old_widget);
329 /* Set CURSOR on W and all widgets W contain. We must do like this
330 for scroll bars and menu because they create widgets internally,
331 and it is those widgets that are visible. */
332 static void
333 xg_set_cursor (w, cursor)
334 GtkWidget *w;
335 GdkCursor *cursor;
337 GList *children = gdk_window_peek_children (w->window);
339 gdk_window_set_cursor (w->window, cursor);
341 /* The scroll bar widget has more than one GDK window (had to look at
342 the source to figure this out), and there is no way to set cursor
343 on widgets in GTK. So we must set the cursor for all GDK windows.
344 Ditto for menus. */
346 for ( ; children; children = g_list_next (children))
347 gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
350 /* Timer function called when a timeout occurs for xg_timer.
351 This function processes all GTK events in a recursive event loop.
352 This is done because GTK timer events are not seen by Emacs event
353 detection, Emacs only looks for X events. When a scroll bar has the
354 pointer (detected by button press/release events below) an Emacs
355 timer is started, and this function can then check if the GTK timer
356 has expired by calling the GTK event loop.
357 Also, when a menu is active, it has a small timeout before it
358 pops down the sub menu under it. */
359 static void
360 xg_process_timeouts (timer)
361 struct atimer *timer;
363 BLOCK_INPUT;
364 /* Ideally we would like to just handle timer events, like the Xt version
365 of this does in xterm.c, but there is no such feature in GTK. */
366 while (gtk_events_pending ())
367 gtk_main_iteration ();
368 UNBLOCK_INPUT;
371 /* Start the xg_timer with an interval of 0.1 seconds, if not already started.
372 xg_process_timeouts is called when the timer expires. The timer
373 started is continuous, i.e. runs until xg_stop_timer is called. */
374 static void
375 xg_start_timer ()
377 if (! xg_timer)
379 EMACS_TIME interval;
380 EMACS_SET_SECS_USECS (interval, 0, 100000);
381 xg_timer = start_atimer (ATIMER_CONTINUOUS,
382 interval,
383 xg_process_timeouts,
388 /* Stop the xg_timer if started. */
389 static void
390 xg_stop_timer ()
392 if (xg_timer)
394 cancel_atimer (xg_timer);
395 xg_timer = 0;
399 /* Insert NODE into linked LIST. */
400 static void
401 xg_list_insert (xg_list_node *list, xg_list_node *node)
403 xg_list_node *list_start = list->next;
405 if (list_start) list_start->prev = node;
406 node->next = list_start;
407 node->prev = 0;
408 list->next = node;
411 /* Remove NODE from linked LIST. */
412 static void
413 xg_list_remove (xg_list_node *list, xg_list_node *node)
415 xg_list_node *list_start = list->next;
416 if (node == list_start)
418 list->next = node->next;
419 if (list->next) list->next->prev = 0;
421 else
423 node->prev->next = node->next;
424 if (node->next) node->next->prev = node->prev;
428 /* Allocate and return a utf8 version of STR. If STR is already
429 utf8 or NULL, just return STR.
430 If not, a new string is allocated and the caller must free the result
431 with g_free. */
432 static char *
433 get_utf8_string (str)
434 char *str;
436 char *utf8_str = str;
438 /* If not UTF-8, try current locale. */
439 if (str && !g_utf8_validate (str, -1, NULL))
440 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
442 return utf8_str;
447 /***********************************************************************
448 General functions for creating widgets, resizing, events, e.t.c.
449 ***********************************************************************/
451 /* Make a geometry string and pass that to GTK. It seems this is the
452 only way to get geometry position right if the user explicitly
453 asked for a position when starting Emacs.
454 F is the frame we shall set geometry for. */
455 static void
456 xg_set_geometry (f)
457 FRAME_PTR f;
459 if (f->size_hint_flags & USPosition)
461 int left = f->left_pos;
462 int xneg = f->size_hint_flags & XNegative;
463 int top = f->top_pos;
464 int yneg = f->size_hint_flags & YNegative;
465 char geom_str[32];
467 if (xneg)
468 left = -left;
469 if (yneg)
470 top = -top;
472 sprintf (geom_str, "=%dx%d%c%d%c%d",
473 FRAME_PIXEL_WIDTH (f),
474 FRAME_TOTAL_PIXEL_HEIGHT (f),
475 (xneg ? '-' : '+'), left,
476 (yneg ? '-' : '+'), top);
478 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
479 geom_str))
480 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
485 /* Resize the outer window of frame F after chainging the height.
486 This happend when the menu bar or the tool bar is added or removed.
487 COLUMNS/ROWS is the size the edit area shall have after the resize. */
488 static void
489 xg_resize_outer_widget (f, columns, rows)
490 FRAME_PTR f;
491 int columns;
492 int rows;
494 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
495 FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
497 /* base_height is now changed. */
498 x_wm_set_size_hint (f, 0, 0);
500 /* If we are not mapped yet, set geometry once again, as window
501 height now have changed. */
502 if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f)))
503 xg_set_geometry (f);
505 xg_frame_set_char_size (f, columns, rows);
506 gdk_window_process_all_updates ();
509 /* This gets called after the frame F has been cleared. Since that is
510 done with X calls, we need to redraw GTK widget (scroll bars). */
511 void
512 xg_frame_cleared (f)
513 FRAME_PTR f;
515 GtkWidget *w = f->output_data.x->widget;
517 if (w)
519 gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE);
520 gtk_container_foreach (GTK_CONTAINER (w),
521 (GtkCallback) gtk_widget_queue_draw,
523 gdk_window_process_all_updates ();
527 /* Function to handle resize of our widgets. Since Emacs has some layouts
528 that does not fit well with GTK standard containers, we do most layout
529 manually.
530 F is the frame to resize.
531 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
532 void
533 xg_resize_widgets (f, pixelwidth, pixelheight)
534 FRAME_PTR f;
535 int pixelwidth, pixelheight;
537 int mbheight = FRAME_MENUBAR_HEIGHT (f);
538 int tbheight = FRAME_TOOLBAR_HEIGHT (f);
539 int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
540 - mbheight - tbheight));
541 int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
543 if (FRAME_GTK_WIDGET (f)
544 && (columns != FRAME_COLS (f) || rows != FRAME_LINES (f)
545 || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f)))
547 struct x_output *x = f->output_data.x;
548 GtkAllocation all;
550 all.y = mbheight + tbheight;
551 all.x = 0;
553 all.width = pixelwidth;
554 all.height = pixelheight - mbheight - tbheight;
556 gtk_widget_size_allocate (x->edit_widget, &all);
558 change_frame_size (f, rows, columns, 0, 1, 0);
559 SET_FRAME_GARBAGED (f);
560 cancel_mouse_face (f);
565 /* Update our widget size to be COLS/ROWS characters for frame F. */
566 void
567 xg_frame_set_char_size (f, cols, rows)
568 FRAME_PTR f;
569 int cols;
570 int rows;
572 int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
573 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
574 int pixelwidth;
576 /* Take into account the size of the scroll bar. Always use the
577 number of columns occupied by the scroll bar here otherwise we
578 might end up with a frame width that is not a multiple of the
579 frame's character width which is bad for vertically split
580 windows. */
581 f->scroll_bar_actual_width
582 = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
584 compute_fringe_widths (f, 0);
586 /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it
587 after calculating that value. */
588 pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
590 /* Must resize our top level widget. Font size may have changed,
591 but not rows/cols. */
592 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
593 pixelwidth, pixelheight);
594 xg_resize_widgets (f, pixelwidth, pixelheight);
595 x_wm_set_size_hint (f, 0, 0);
596 SET_FRAME_GARBAGED (f);
597 cancel_mouse_face (f);
600 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
601 Must be done like this, because GtkWidget:s can have "hidden"
602 X Window that aren't accessible.
604 Return 0 if no widget match WDESC. */
605 GtkWidget *
606 xg_win_to_widget (dpy, wdesc)
607 Display *dpy;
608 Window wdesc;
610 gpointer gdkwin;
611 GtkWidget *gwdesc = 0;
613 BLOCK_INPUT;
615 gdkwin = gdk_xid_table_lookup_for_display (gdk_x11_lookup_xdisplay (dpy),
616 wdesc);
617 if (gdkwin)
619 GdkEvent event;
620 event.any.window = gdkwin;
621 gwdesc = gtk_get_event_widget (&event);
624 UNBLOCK_INPUT;
625 return gwdesc;
628 /* Fill in the GdkColor C so that it represents PIXEL.
629 W is the widget that color will be used for. Used to find colormap. */
630 static void
631 xg_pix_to_gcolor (w, pixel, c)
632 GtkWidget *w;
633 unsigned long pixel;
634 GdkColor *c;
636 GdkColormap *map = gtk_widget_get_colormap (w);
637 gdk_colormap_query_color (map, pixel, c);
640 /* Turning off double buffering for our GtkFixed widget has the side
641 effect of turning it off also for its children (scroll bars).
642 But we want those to be double buffered to not flicker so handle
643 expose manually here.
644 WIDGET is the GtkFixed widget that gets exposed.
645 EVENT is the expose event.
646 USER_DATA is unused.
648 Return TRUE to tell GTK that this expose event has been fully handeled
649 and that GTK shall do nothing more with it. */
650 static gboolean
651 xg_fixed_handle_expose (GtkWidget *widget,
652 GdkEventExpose *event,
653 gpointer user_data)
655 GList *iter;
657 for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter))
659 GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
660 GtkWidget *child = child_data->widget;
661 GdkWindow *window = child->window;
662 GdkRegion *region = gtk_widget_region_intersect (child, event->region);
664 if (! gdk_region_empty (region))
666 GdkEvent child_event;
667 child_event.expose = *event;
668 child_event.expose.region = region;
670 /* Turn on double buffering, i.e. draw to an off screen area. */
671 gdk_window_begin_paint_region (window, region);
673 /* Tell child to redraw itself. */
674 gdk_region_get_clipbox (region, &child_event.expose.area);
675 gtk_widget_send_expose (child, &child_event);
676 gdk_window_process_updates (window, TRUE);
678 /* Copy off screen area to the window. */
679 gdk_window_end_paint (window);
682 gdk_region_destroy (region);
685 return TRUE;
688 /* Create and set up the GTK widgets for frame F.
689 Return 0 if creation failed, non-zero otherwise. */
691 xg_create_frame_widgets (f)
692 FRAME_PTR f;
694 GtkWidget *wtop;
695 GtkWidget *wvbox;
696 GtkWidget *wfixed;
697 GdkColor bg;
698 GtkRcStyle *style;
699 int i;
700 char *title = 0;
702 BLOCK_INPUT;
704 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
705 xg_set_screen (wtop, f);
707 wvbox = gtk_vbox_new (FALSE, 0);
708 wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */
710 if (! wtop || ! wvbox || ! wfixed)
712 if (wtop) gtk_widget_destroy (wtop);
713 if (wvbox) gtk_widget_destroy (wvbox);
714 if (wfixed) gtk_widget_destroy (wfixed);
716 return 0;
719 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
720 gtk_widget_set_name (wtop, EMACS_CLASS);
721 gtk_widget_set_name (wvbox, "pane");
722 gtk_widget_set_name (wfixed, SDATA (Vx_resource_name));
724 /* If this frame has a title or name, set it in the title bar. */
725 if (! NILP (f->title)) title = SDATA (ENCODE_UTF_8 (f->title));
726 else if (! NILP (f->name)) title = SDATA (ENCODE_UTF_8 (f->name));
728 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
730 FRAME_GTK_OUTER_WIDGET (f) = wtop;
731 FRAME_GTK_WIDGET (f) = wfixed;
732 f->output_data.x->vbox_widget = wvbox;
734 gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
736 gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f),
737 FRAME_PIXEL_HEIGHT (f));
739 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
740 gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
742 if (FRAME_EXTERNAL_TOOL_BAR (f))
743 update_frame_tool_bar (f);
745 /* The tool bar is created but first there are no items in it.
746 This causes it to be zero height. Later items are added, but then
747 the frame is already mapped, so there is a "jumping" resize.
748 This makes geometry handling difficult, for example -0-0 will end
749 up in the wrong place as tool bar height has not been taken into account.
750 So we cheat a bit by setting a height that is what it will have
751 later on when tool bar items are added. */
752 if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0)
753 FRAME_TOOLBAR_HEIGHT (f) = 34;
756 /* We don't want this widget double buffered, because we draw on it
757 with regular X drawing primitives, so from a GTK/GDK point of
758 view, the widget is totally blank. When an expose comes, this
759 will make the widget blank, and then Emacs redraws it. This flickers
760 a lot, so we turn off double buffering. */
761 gtk_widget_set_double_buffered (wfixed, FALSE);
763 /* Turning off double buffering above has the side effect of turning
764 it off also for its children (scroll bars). But we want those
765 to be double buffered to not flicker so handle expose manually. */
766 g_signal_connect (G_OBJECT (wfixed), "expose-event",
767 G_CALLBACK (xg_fixed_handle_expose), 0);
769 /* GTK documents says use gtk_window_set_resizable. But then a user
770 can't shrink the window from its starting size. */
771 gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
772 gtk_window_set_wmclass (GTK_WINDOW (wtop),
773 SDATA (Vx_resource_name),
774 SDATA (Vx_resource_class));
776 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
777 GTK is to destroy the widget. We want Emacs to do that instead. */
778 g_signal_connect (G_OBJECT (wtop), "delete-event",
779 G_CALLBACK (gtk_true), 0);
781 /* Convert our geometry parameters into a geometry string
782 and specify it.
783 GTK will itself handle calculating the real position this way. */
784 xg_set_geometry (f);
786 gtk_widget_add_events (wfixed,
787 GDK_POINTER_MOTION_MASK
788 | GDK_EXPOSURE_MASK
789 | GDK_BUTTON_PRESS_MASK
790 | GDK_BUTTON_RELEASE_MASK
791 | GDK_KEY_PRESS_MASK
792 | GDK_ENTER_NOTIFY_MASK
793 | GDK_LEAVE_NOTIFY_MASK
794 | GDK_FOCUS_CHANGE_MASK
795 | GDK_STRUCTURE_MASK
796 | GDK_VISIBILITY_NOTIFY_MASK);
798 /* Must realize the windows so the X window gets created. It is used
799 by callers of this function. */
800 gtk_widget_realize (wfixed);
801 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
803 /* Since GTK clears its window by filling with the background color,
804 we must keep X and GTK background in sync. */
805 xg_pix_to_gcolor (wfixed, f->output_data.x->background_pixel, &bg);
806 gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
808 /* Also, do not let any background pixmap to be set, this looks very
809 bad as Emacs overwrites the background pixmap with its own idea
810 of background color. */
811 style = gtk_widget_get_modifier_style (wfixed);
813 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
814 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
815 gtk_widget_modify_style (wfixed, style);
817 /* GTK does not set any border, and they look bad with GTK. */
818 f->border_width = 0;
819 f->internal_border_width = 0;
821 UNBLOCK_INPUT;
823 return 1;
826 /* Set the normal size hints for the window manager, for frame F.
827 FLAGS is the flags word to use--or 0 meaning preserve the flags
828 that the window now has.
829 If USER_POSITION is nonzero, we set the User Position
830 flag (this is useful when FLAGS is 0). */
831 void
832 x_wm_set_size_hint (f, flags, user_position)
833 FRAME_PTR f;
834 long flags;
835 int user_position;
837 if (FRAME_GTK_OUTER_WIDGET (f))
839 /* Must use GTK routines here, otherwise GTK resets the size hints
840 to its own defaults. */
841 GdkGeometry size_hints;
842 gint hint_flags = 0;
843 int base_width, base_height;
844 int min_rows = 0, min_cols = 0;
845 int win_gravity = f->win_gravity;
847 if (flags)
849 memset (&size_hints, 0, sizeof (size_hints));
850 f->output_data.x->size_hints = size_hints;
851 f->output_data.x->hint_flags = hint_flags;
853 else
854 flags = f->size_hint_flags;
856 size_hints = f->output_data.x->size_hints;
857 hint_flags = f->output_data.x->hint_flags;
859 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
860 size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
861 size_hints.height_inc = FRAME_LINE_HEIGHT (f);
863 hint_flags |= GDK_HINT_BASE_SIZE;
864 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
865 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
866 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
868 check_frame_size (f, &min_rows, &min_cols);
870 size_hints.base_width = base_width;
871 size_hints.base_height = base_height;
872 size_hints.min_width = base_width + min_cols * size_hints.width_inc;
873 size_hints.min_height = base_height + min_rows * size_hints.height_inc;
876 /* These currently have a one to one mapping with the X values, but I
877 don't think we should rely on that. */
878 hint_flags |= GDK_HINT_WIN_GRAVITY;
879 size_hints.win_gravity = 0;
880 if (win_gravity == NorthWestGravity)
881 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
882 else if (win_gravity == NorthGravity)
883 size_hints.win_gravity = GDK_GRAVITY_NORTH;
884 else if (win_gravity == NorthEastGravity)
885 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
886 else if (win_gravity == WestGravity)
887 size_hints.win_gravity = GDK_GRAVITY_WEST;
888 else if (win_gravity == CenterGravity)
889 size_hints.win_gravity = GDK_GRAVITY_CENTER;
890 else if (win_gravity == EastGravity)
891 size_hints.win_gravity = GDK_GRAVITY_EAST;
892 else if (win_gravity == SouthWestGravity)
893 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
894 else if (win_gravity == SouthGravity)
895 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
896 else if (win_gravity == SouthEastGravity)
897 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
898 else if (win_gravity == StaticGravity)
899 size_hints.win_gravity = GDK_GRAVITY_STATIC;
901 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
902 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
903 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
905 if (user_position)
907 hint_flags &= ~GDK_HINT_POS;
908 hint_flags |= GDK_HINT_USER_POS;
911 BLOCK_INPUT;
913 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
914 FRAME_GTK_OUTER_WIDGET (f),
915 &size_hints,
916 hint_flags);
918 f->output_data.x->size_hints = size_hints;
919 f->output_data.x->hint_flags = hint_flags;
920 UNBLOCK_INPUT;
924 /* Change background color of a frame.
925 Since GTK uses the background colour to clear the window, we must
926 keep the GTK and X colors in sync.
927 F is the frame to change,
928 BG is the pixel value to change to. */
929 void
930 xg_set_background_color (f, bg)
931 FRAME_PTR f;
932 unsigned long bg;
934 if (FRAME_GTK_WIDGET (f))
936 GdkColor gdk_bg;
938 BLOCK_INPUT;
939 xg_pix_to_gcolor (FRAME_GTK_WIDGET (f), bg, &gdk_bg);
940 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &gdk_bg);
941 UNBLOCK_INPUT;
947 /***********************************************************************
948 Dialog functions
949 ***********************************************************************/
950 /* Return the dialog title to use for a dialog of type KEY.
951 This is the encoding used by lwlib. We use the same for GTK. */
952 static char *
953 get_dialog_title (char key)
955 char *title = "";
957 switch (key) {
958 case 'E': case 'e':
959 title = "Error";
960 break;
962 case 'I': case 'i':
963 title = "Information";
964 break;
966 case 'L': case 'l':
967 title = "Prompt";
968 break;
970 case 'P': case 'p':
971 title = "Prompt";
972 break;
974 case 'Q': case 'q':
975 title = "Question";
976 break;
979 return title;
982 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
983 the dialog, but return TRUE so the event does not propagate further
984 in GTK. This prevents GTK from destroying the dialog widget automatically
985 and we can always destrou the widget manually, regardles of how
986 it was popped down (button press or WM_DELETE_WINDOW).
987 W is the dialog widget.
988 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
989 user_data is NULL (not used).
991 Returns TRUE to end propagation of event. */
992 static gboolean
993 dialog_delete_callback (w, event, user_data)
994 GtkWidget *w;
995 GdkEvent *event;
996 gpointer user_data;
998 gtk_widget_unmap (w);
999 return TRUE;
1002 /* Create a popup dialog window. See also xg_create_widget below.
1003 WV is a widget_value describing the dialog.
1004 SELECT_CB is the callback to use when a button has been pressed.
1005 DEACTIVATE_CB is the callback to use when the dialog pops down.
1007 Returns the GTK dialog widget. */
1008 static GtkWidget *
1009 create_dialog (wv, select_cb, deactivate_cb)
1010 widget_value *wv;
1011 GCallback select_cb;
1012 GCallback deactivate_cb;
1014 char *title = get_dialog_title (wv->name[0]);
1015 int total_buttons = wv->name[1] - '0';
1016 int right_buttons = wv->name[4] - '0';
1017 int left_buttons;
1018 int button_nr = 0;
1019 int button_spacing = 10;
1020 GtkWidget *wdialog = gtk_dialog_new ();
1021 widget_value *item;
1022 GtkBox *cur_box;
1023 GtkWidget *wvbox;
1024 GtkWidget *whbox_up;
1025 GtkWidget *whbox_down;
1027 /* If the number of buttons is greater than 4, make two rows of buttons
1028 instead. This looks better. */
1029 int make_two_rows = total_buttons > 4;
1031 if (right_buttons == 0) right_buttons = total_buttons/2;
1032 left_buttons = total_buttons - right_buttons;
1034 gtk_window_set_title (GTK_WINDOW (wdialog), title);
1035 gtk_widget_set_name (wdialog, "emacs-dialog");
1037 cur_box = GTK_BOX (GTK_DIALOG (wdialog)->action_area);
1039 if (make_two_rows)
1041 wvbox = gtk_vbox_new (TRUE, button_spacing);
1042 whbox_up = gtk_hbox_new (FALSE, 0);
1043 whbox_down = gtk_hbox_new (FALSE, 0);
1045 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
1046 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
1047 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
1049 cur_box = GTK_BOX (whbox_up);
1052 g_signal_connect (G_OBJECT (wdialog), "delete-event",
1053 G_CALLBACK (dialog_delete_callback), 0);
1055 if (deactivate_cb)
1057 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
1058 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
1061 for (item = wv->contents; item; item = item->next)
1063 char *utf8_label = get_utf8_string (item->value);
1064 GtkWidget *w;
1065 GtkRequisition req;
1067 if (item->name && strcmp (item->name, "message") == 0)
1069 /* This is the text part of the dialog. */
1070 w = gtk_label_new (utf8_label);
1071 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
1072 gtk_label_new (""),
1073 FALSE, FALSE, 0);
1074 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w,
1075 TRUE, TRUE, 0);
1076 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
1078 /* Try to make dialog look better. Must realize first so
1079 the widget can calculate the size it needs. */
1080 gtk_widget_realize (w);
1081 gtk_widget_size_request (w, &req);
1082 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
1083 req.height);
1084 if (item->value && strlen (item->value) > 0)
1085 button_spacing = 2*req.width/strlen (item->value);
1087 else
1089 /* This is one button to add to the dialog. */
1090 w = gtk_button_new_with_label (utf8_label);
1091 if (! item->enabled)
1092 gtk_widget_set_sensitive (w, FALSE);
1093 if (select_cb)
1094 g_signal_connect (G_OBJECT (w), "clicked",
1095 select_cb, item->call_data);
1097 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
1098 if (++button_nr == left_buttons)
1100 if (make_two_rows)
1101 cur_box = GTK_BOX (whbox_down);
1102 else
1103 gtk_box_pack_start (cur_box,
1104 gtk_label_new (""),
1105 TRUE, TRUE,
1106 button_spacing);
1110 if (utf8_label && utf8_label != item->value)
1111 g_free (utf8_label);
1114 return wdialog;
1118 enum
1120 XG_FILE_NOT_DONE,
1121 XG_FILE_OK,
1122 XG_FILE_CANCEL,
1123 XG_FILE_DESTROYED,
1126 /* Callback function invoked when the Ok button is pressed in
1127 a file dialog.
1128 W is the file dialog widget,
1129 ARG points to an integer where we record what has happend. */
1130 static void
1131 xg_file_sel_ok (w, arg)
1132 GtkWidget *w;
1133 gpointer arg;
1135 *(int*)arg = XG_FILE_OK;
1138 /* Callback function invoked when the Cancel button is pressed in
1139 a file dialog.
1140 W is the file dialog widget,
1141 ARG points to an integer where we record what has happend. */
1142 static void
1143 xg_file_sel_cancel (w, arg)
1144 GtkWidget *w;
1145 gpointer arg;
1147 *(int*)arg = XG_FILE_CANCEL;
1150 /* Callback function invoked when the file dialog is destroyed (i.e.
1151 popped down). We must keep track of this, because if this
1152 happens, GTK destroys the widget. But if for example, Ok is pressed,
1153 the dialog is popped down, but the dialog widget is not destroyed.
1154 W is the file dialog widget,
1155 ARG points to an integer where we record what has happend. */
1156 static void
1157 xg_file_sel_destroy (w, arg)
1158 GtkWidget *w;
1159 gpointer arg;
1161 *(int*)arg = XG_FILE_DESTROYED;
1164 /* Read a file name from the user using a file dialog.
1165 F is the current frame.
1166 PROMPT is a prompt to show to the user. May not be NULL.
1167 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
1168 If MUSTMATCH_P is non-zero, the returned file name must be an existing
1169 file.
1171 Returns a file name or NULL if no file was selected.
1172 The returned string must be freed by the caller. */
1173 char *
1174 xg_get_file_name (f, prompt, default_filename, mustmatch_p)
1175 FRAME_PTR f;
1176 char *prompt;
1177 char *default_filename;
1178 int mustmatch_p;
1180 GtkWidget *filewin;
1181 GtkFileSelection *filesel;
1182 int filesel_done = XG_FILE_NOT_DONE;
1183 char *fn = 0;
1185 filewin = gtk_file_selection_new (prompt);
1186 filesel = GTK_FILE_SELECTION (filewin);
1188 xg_set_screen (filewin, f);
1190 gtk_widget_set_name (filewin, "emacs-filedialog");
1192 gtk_window_set_transient_for (GTK_WINDOW (filewin),
1193 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1194 gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
1196 g_signal_connect (G_OBJECT (filesel->ok_button),
1197 "clicked",
1198 G_CALLBACK (xg_file_sel_ok),
1199 &filesel_done);
1200 g_signal_connect (G_OBJECT (filesel->cancel_button),
1201 "clicked",
1202 G_CALLBACK (xg_file_sel_cancel),
1203 &filesel_done);
1204 g_signal_connect (G_OBJECT (filesel),
1205 "destroy",
1206 G_CALLBACK (xg_file_sel_destroy),
1207 &filesel_done);
1209 if (default_filename)
1210 gtk_file_selection_set_filename (filesel, default_filename);
1212 if (mustmatch_p)
1214 /* The selection_entry part of filesel is not documented. */
1215 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
1216 gtk_file_selection_hide_fileop_buttons (filesel);
1220 gtk_widget_show_all (filewin);
1222 while (filesel_done == XG_FILE_NOT_DONE)
1223 gtk_main_iteration ();
1225 if (filesel_done == XG_FILE_OK)
1226 fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
1228 if (filesel_done != XG_FILE_DESTROYED)
1229 gtk_widget_destroy (filewin);
1231 return fn;
1235 /***********************************************************************
1236 Menu functions.
1237 ***********************************************************************/
1239 /* The name of menu items that can be used for citomization. Since GTK
1240 RC files are very crude and primitive, we have to set this on all
1241 menu item names so a user can easily cutomize menu items. */
1243 #define MENU_ITEM_NAME "emacs-menuitem"
1246 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
1247 during GC. The next member points to the items. */
1248 static xg_list_node xg_menu_cb_list;
1250 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
1251 during GC. The next member points to the items. */
1252 static xg_list_node xg_menu_item_cb_list;
1254 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
1255 F is the frame CL_DATA will be initialized for.
1256 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1258 The menu bar and all sub menus under the menu bar in a frame
1259 share the same structure, hence the reference count.
1261 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
1262 allocated xg_menu_cb_data if CL_DATA is NULL. */
1263 static xg_menu_cb_data *
1264 make_cl_data (cl_data, f, highlight_cb)
1265 xg_menu_cb_data *cl_data;
1266 FRAME_PTR f;
1267 GCallback highlight_cb;
1269 if (! cl_data)
1271 cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
1272 cl_data->f = f;
1273 cl_data->menu_bar_vector = f->menu_bar_vector;
1274 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1275 cl_data->highlight_cb = highlight_cb;
1276 cl_data->ref_count = 0;
1278 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
1281 cl_data->ref_count++;
1283 return cl_data;
1286 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
1287 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1289 When the menu bar is updated, menu items may have been added and/or
1290 removed, so menu_bar_vector and menu_bar_items_used change. We must
1291 then update CL_DATA since it is used to determine which menu
1292 item that is invoked in the menu.
1293 HIGHLIGHT_CB could change, there is no check that the same
1294 function is given when modifying a menu bar as was given when
1295 creating the menu bar. */
1296 static void
1297 update_cl_data (cl_data, f, highlight_cb)
1298 xg_menu_cb_data *cl_data;
1299 FRAME_PTR f;
1300 GCallback highlight_cb;
1302 if (cl_data)
1304 cl_data->f = f;
1305 cl_data->menu_bar_vector = f->menu_bar_vector;
1306 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1307 cl_data->highlight_cb = highlight_cb;
1311 /* Decrease reference count for CL_DATA.
1312 If reference count is zero, free CL_DATA. */
1313 static void
1314 unref_cl_data (cl_data)
1315 xg_menu_cb_data *cl_data;
1317 if (cl_data && cl_data->ref_count > 0)
1319 cl_data->ref_count--;
1320 if (cl_data->ref_count == 0)
1322 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
1323 xfree (cl_data);
1328 /* Function that marks all lisp data during GC. */
1329 void
1330 xg_mark_data ()
1332 xg_list_node *iter;
1334 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
1335 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
1337 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
1339 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
1341 if (! NILP (cb_data->help))
1342 mark_object (cb_data->help);
1347 /* Callback called when a menu item is destroyed. Used to free data.
1348 W is the widget that is being destroyed (not used).
1349 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
1350 static void
1351 menuitem_destroy_callback (w, client_data)
1352 GtkWidget *w;
1353 gpointer client_data;
1355 if (client_data)
1357 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1358 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
1359 xfree (data);
1363 /* Callback called when the pointer enters/leaves a menu item.
1364 W is the menu item.
1365 EVENT is either an enter event or leave event.
1366 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
1368 Returns FALSE to tell GTK to keep processing this event. */
1369 static gboolean
1370 menuitem_highlight_callback (w, event, client_data)
1371 GtkWidget *w;
1372 GdkEventCrossing *event;
1373 gpointer client_data;
1375 if (client_data)
1377 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1378 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
1380 if (! NILP (data->help) && data->cl_data->highlight_cb)
1382 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
1383 (*func) (w, call_data);
1387 return FALSE;
1390 /* Callback called when a menu is destroyed. Used to free data.
1391 W is the widget that is being destroyed (not used).
1392 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
1393 static void
1394 menu_destroy_callback (w, client_data)
1395 GtkWidget *w;
1396 gpointer client_data;
1398 unref_cl_data ((xg_menu_cb_data*) client_data);
1401 /* Callback called when a menu does a grab or ungrab. That means the
1402 menu has been activated or deactivated.
1403 Used to start a timer so the small timeout the menus in GTK uses before
1404 popping down a menu is seen by Emacs (see xg_process_timeouts above).
1405 W is the widget that does the grab (not used).
1406 UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
1407 CLIENT_DATA is NULL (not used). */
1408 static void
1409 menu_grab_callback (GtkWidget *widget,
1410 gboolean ungrab_p,
1411 gpointer client_data)
1413 /* Keep track of total number of grabs. */
1414 static int cnt;
1416 if (ungrab_p) cnt--;
1417 else cnt++;
1419 if (cnt > 0 && ! xg_timer) xg_start_timer ();
1420 else if (cnt == 0 && xg_timer) xg_stop_timer ();
1423 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
1424 must be non-NULL) and can be inserted into a menu item.
1426 Returns the GtkHBox. */
1427 static GtkWidget *
1428 make_widget_for_menu_item (utf8_label, utf8_key)
1429 char *utf8_label;
1430 char *utf8_key;
1432 GtkWidget *wlbl;
1433 GtkWidget *wkey;
1434 GtkWidget *wbox;
1436 wbox = gtk_hbox_new (FALSE, 0);
1437 wlbl = gtk_label_new (utf8_label);
1438 wkey = gtk_label_new (utf8_key);
1440 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
1441 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
1443 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
1444 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
1446 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
1447 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
1448 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
1450 return wbox;
1453 /* Make and return a menu item widget with the key to the right.
1454 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
1455 UTF8_KEY is the text representing the key binding.
1456 ITEM is the widget_value describing the menu item.
1458 GROUP is an in/out parameter. If the menu item to be created is not
1459 part of any radio menu group, *GROUP contains NULL on entry and exit.
1460 If the menu item to be created is part of a radio menu group, on entry
1461 *GROUP contains the group to use, or NULL if this is the first item
1462 in the group. On exit, *GROUP contains the radio item group.
1464 Unfortunately, keys don't line up as nicely as in Motif,
1465 but the MacOS X version doesn't either, so I guess that is OK. */
1466 static GtkWidget *
1467 make_menu_item (utf8_label, utf8_key, item, group)
1468 char *utf8_label;
1469 char *utf8_key;
1470 widget_value *item;
1471 GSList **group;
1473 GtkWidget *w;
1474 GtkWidget *wtoadd = 0;
1476 /* It has been observed that some menu items have a NULL name field.
1477 This will lead to this function being called with a NULL utf8_label.
1478 GTK crashes on that so we set a blank label. Why there is a NULL
1479 name remains to be investigated. */
1480 if (! utf8_label) utf8_label = " ";
1482 if (utf8_key)
1483 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
1485 if (item->button_type == BUTTON_TYPE_TOGGLE)
1487 *group = NULL;
1488 if (utf8_key) w = gtk_check_menu_item_new ();
1489 else w = gtk_check_menu_item_new_with_label (utf8_label);
1490 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
1492 else if (item->button_type == BUTTON_TYPE_RADIO)
1494 if (utf8_key) w = gtk_radio_menu_item_new (*group);
1495 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
1496 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
1497 if (item->selected)
1498 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1500 else
1502 *group = NULL;
1503 if (utf8_key) w = gtk_menu_item_new ();
1504 else w = gtk_menu_item_new_with_label (utf8_label);
1507 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
1508 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
1510 return w;
1513 /* Return non-zero if LABEL specifies a separator (GTK only has one
1514 separator type) */
1515 static int
1516 xg_separator_p (char *label)
1518 if (! label) return 0;
1519 else if (strlen (label) > 3
1520 && strncmp (label, "--", 2) == 0
1521 && label[2] != '-')
1523 static char* separator_names[] = {
1524 "space",
1525 "no-line",
1526 "single-line",
1527 "double-line",
1528 "single-dashed-line",
1529 "double-dashed-line",
1530 "shadow-etched-in",
1531 "shadow-etched-out",
1532 "shadow-etched-in-dash",
1533 "shadow-etched-out-dash",
1534 "shadow-double-etched-in",
1535 "shadow-double-etched-out",
1536 "shadow-double-etched-in-dash",
1537 "shadow-double-etched-out-dash",
1541 int i;
1543 label += 2;
1544 for (i = 0; separator_names[i]; ++i)
1545 if (strcmp (label, separator_names[i]) == 0)
1546 return 1;
1548 else
1550 /* Old-style separator, maybe. It's a separator if it contains
1551 only dashes. */
1552 while (*label == '-')
1553 ++label;
1554 if (*label == 0) return 1;
1557 return 0;
1560 static int xg_detached_menus;
1562 /* Returns non-zero if there are detached menus. */
1564 xg_have_tear_offs ()
1566 return xg_detached_menus > 0;
1569 /* Callback invoked when a detached menu window is removed. Here we
1570 decrease the xg_detached_menus count.
1571 WIDGET is the top level window that is removed (the parent of the menu).
1572 CLIENT_DATA is not used. */
1573 static void
1574 tearoff_remove (widget, client_data)
1575 GtkWidget *widget;
1576 gpointer client_data;
1578 if (xg_detached_menus > 0) --xg_detached_menus;
1581 /* Callback invoked when a menu is detached. It increases the
1582 xg_detached_menus count.
1583 WIDGET is the GtkTearoffMenuItem.
1584 CLIENT_DATA is not used. */
1585 static void
1586 tearoff_activate (widget, client_data)
1587 GtkWidget *widget;
1588 gpointer client_data;
1590 GtkWidget *menu = gtk_widget_get_parent (widget);
1591 if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
1593 ++xg_detached_menus;
1594 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
1595 "destroy",
1596 G_CALLBACK (tearoff_remove), 0);
1601 /* Create a menu item widget, and connect the callbacks.
1602 ITEM decribes the menu item.
1603 F is the frame the created menu belongs to.
1604 SELECT_CB is the callback to use when a menu item is selected.
1605 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1606 CL_DATA points to the callback data to be used for this menu.
1607 GROUP is an in/out parameter. If the menu item to be created is not
1608 part of any radio menu group, *GROUP contains NULL on entry and exit.
1609 If the menu item to be created is part of a radio menu group, on entry
1610 *GROUP contains the group to use, or NULL if this is the first item
1611 in the group. On exit, *GROUP contains the radio item group.
1613 Returns the created GtkWidget. */
1614 static GtkWidget *
1615 xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
1616 widget_value *item;
1617 FRAME_PTR f;
1618 GCallback select_cb;
1619 GCallback highlight_cb;
1620 xg_menu_cb_data *cl_data;
1621 GSList **group;
1623 char *utf8_label;
1624 char *utf8_key;
1625 GtkWidget *w;
1626 xg_menu_item_cb_data *cb_data;
1628 utf8_label = get_utf8_string (item->name);
1629 utf8_key = get_utf8_string (item->key);
1631 w = make_menu_item (utf8_label, utf8_key, item, group);
1633 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1634 if (utf8_key && utf8_key != item->key) g_free (utf8_key);
1636 cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
1638 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
1640 cb_data->unhighlight_id = cb_data->highlight_id = cb_data->select_id = 0;
1641 cb_data->help = item->help;
1642 cb_data->cl_data = cl_data;
1643 cb_data->call_data = item->call_data;
1645 g_signal_connect (G_OBJECT (w),
1646 "destroy",
1647 G_CALLBACK (menuitem_destroy_callback),
1648 cb_data);
1650 /* Put cb_data in widget, so we can get at it when modifying menubar */
1651 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
1653 /* final item, not a submenu */
1654 if (item->call_data && ! item->contents)
1656 if (select_cb)
1657 cb_data->select_id
1658 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
1661 if (! NILP (item->help) && highlight_cb)
1663 /* We use enter/leave notify instead of select/deselect because
1664 select/deselect doesn't go well with detached menus. */
1665 cb_data->highlight_id
1666 = g_signal_connect (G_OBJECT (w),
1667 "enter-notify-event",
1668 G_CALLBACK (menuitem_highlight_callback),
1669 cb_data);
1670 cb_data->unhighlight_id
1671 = g_signal_connect (G_OBJECT (w),
1672 "leave-notify-event",
1673 G_CALLBACK (menuitem_highlight_callback),
1674 cb_data);
1677 return w;
1680 static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
1681 GCallback, GCallback, int, int, int,
1682 GtkWidget *, xg_menu_cb_data *, char *));
1684 /* Create a full menu tree specified by DATA.
1685 F is the frame the created menu belongs to.
1686 SELECT_CB is the callback to use when a menu item is selected.
1687 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
1688 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1689 POP_UP_P is non-zero if we shall create a popup menu.
1690 MENU_BAR_P is non-zero if we shall create a menu bar.
1691 ADD_TEAROFF_P is non-zero if we shall add a teroff menu item. Ignored
1692 if MENU_BAR_P is non-zero.
1693 TOPMENU is the topmost GtkWidget that others shall be placed under.
1694 It may be NULL, in that case we create the appropriate widget
1695 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
1696 CL_DATA is the callback data we shall use for this menu, or NULL
1697 if we haven't set the first callback yet.
1698 NAME is the name to give to the top level menu if this function
1699 creates it. May be NULL to not set any name.
1701 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
1702 not NULL.
1704 This function calls itself to create submenus. */
1706 static GtkWidget *
1707 create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
1708 pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name)
1709 widget_value *data;
1710 FRAME_PTR f;
1711 GCallback select_cb;
1712 GCallback deactivate_cb;
1713 GCallback highlight_cb;
1714 int pop_up_p;
1715 int menu_bar_p;
1716 int add_tearoff_p;
1717 GtkWidget *topmenu;
1718 xg_menu_cb_data *cl_data;
1719 char *name;
1721 widget_value *item;
1722 GtkWidget *wmenu = topmenu;
1723 GSList *group = NULL;
1725 if (! topmenu)
1727 if (! menu_bar_p)
1729 wmenu = gtk_menu_new ();
1730 xg_set_screen (wmenu, f);
1732 else wmenu = gtk_menu_bar_new ();
1734 /* Put cl_data on the top menu for easier access. */
1735 cl_data = make_cl_data (cl_data, f, highlight_cb);
1736 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
1737 g_signal_connect (G_OBJECT (wmenu), "destroy",
1738 G_CALLBACK (menu_destroy_callback), cl_data);
1740 if (name)
1741 gtk_widget_set_name (wmenu, name);
1743 if (deactivate_cb)
1744 g_signal_connect (G_OBJECT (wmenu),
1745 "deactivate", deactivate_cb, 0);
1747 g_signal_connect (G_OBJECT (wmenu),
1748 "grab-notify", G_CALLBACK (menu_grab_callback), 0);
1751 if (! menu_bar_p && add_tearoff_p)
1753 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
1754 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
1756 g_signal_connect (G_OBJECT (tearoff), "activate",
1757 G_CALLBACK (tearoff_activate), 0);
1760 for (item = data; item; item = item->next)
1762 GtkWidget *w;
1764 if (pop_up_p && !item->contents && !item->call_data
1765 && !xg_separator_p (item->name))
1767 char *utf8_label;
1768 /* A title for a popup. We do the same as GTK does when
1769 creating titles, but it does not look good. */
1770 group = NULL;
1771 utf8_label = get_utf8_string (item->name);
1773 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
1774 w = gtk_menu_item_new_with_label (utf8_label);
1775 gtk_widget_set_sensitive (w, FALSE);
1776 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1778 else if (xg_separator_p (item->name))
1780 group = NULL;
1781 /* GTK only have one separator type. */
1782 w = gtk_separator_menu_item_new ();
1784 else
1786 w = xg_create_one_menuitem (item,
1788 item->contents ? 0 : select_cb,
1789 highlight_cb,
1790 cl_data,
1791 &group);
1793 if (item->contents)
1795 GtkWidget *submenu = create_menus (item->contents,
1797 select_cb,
1798 deactivate_cb,
1799 highlight_cb,
1802 add_tearoff_p,
1804 cl_data,
1806 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
1810 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
1811 gtk_widget_set_name (w, MENU_ITEM_NAME);
1814 return wmenu;
1817 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
1818 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
1819 with some text and buttons.
1820 F is the frame the created item belongs to.
1821 NAME is the name to use for the top widget.
1822 VAL is a widget_value structure describing items to be created.
1823 SELECT_CB is the callback to use when a menu item is selected or
1824 a dialog button is pressed.
1825 DEACTIVATE_CB is the callback to use when an item is deactivated.
1826 For a menu, when a sub menu is not shown anymore, for a dialog it is
1827 called when the dialog is popped down.
1828 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1830 Returns the widget created. */
1831 GtkWidget *
1832 xg_create_widget (type, name, f, val,
1833 select_cb, deactivate_cb, highlight_cb)
1834 char *type;
1835 char *name;
1836 FRAME_PTR f;
1837 widget_value *val;
1838 GCallback select_cb;
1839 GCallback deactivate_cb;
1840 GCallback highlight_cb;
1842 GtkWidget *w = 0;
1843 int menu_bar_p = strcmp (type, "menubar") == 0;
1844 int pop_up_p = strcmp (type, "popup") == 0;
1846 if (strcmp (type, "dialog") == 0)
1848 w = create_dialog (val, select_cb, deactivate_cb);
1849 xg_set_screen (w, f);
1850 gtk_window_set_transient_for (GTK_WINDOW (w),
1851 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1852 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1853 gtk_widget_set_name (w, "emacs-dialog");
1855 else if (menu_bar_p || pop_up_p)
1857 w = create_menus (val->contents,
1859 select_cb,
1860 deactivate_cb,
1861 highlight_cb,
1862 pop_up_p,
1863 menu_bar_p,
1864 menu_bar_p,
1867 name);
1869 /* Set the cursor to an arrow for popup menus when they are mapped.
1870 This is done by default for menu bar menus. */
1871 if (pop_up_p)
1873 /* Must realize so the GdkWindow inside the widget is created. */
1874 gtk_widget_realize (w);
1875 xg_set_cursor (w, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
1878 else
1880 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
1881 type);
1884 return w;
1887 /* Return the label for menu item WITEM. */
1888 static const char *
1889 xg_get_menu_item_label (witem)
1890 GtkMenuItem *witem;
1892 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
1893 return gtk_label_get_label (wlabel);
1896 /* Return non-zero if the menu item WITEM has the text LABEL. */
1897 static int
1898 xg_item_label_same_p (witem, label)
1899 GtkMenuItem *witem;
1900 char *label;
1902 int is_same = 0;
1903 char *utf8_label = get_utf8_string (label);
1904 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
1906 if (! old_label && ! utf8_label)
1907 is_same = 1;
1908 else if (old_label && utf8_label)
1909 is_same = strcmp (utf8_label, old_label) == 0;
1911 if (utf8_label && utf8_label != label) g_free (utf8_label);
1913 return is_same;
1916 /* Remove widgets in LIST from container WCONT. */
1917 static void
1918 remove_from_container (wcont, list)
1919 GtkWidget *wcont;
1920 GList *list;
1922 GList *iter;
1924 for (iter = list; iter; iter = g_list_next (iter))
1926 GtkWidget *w = GTK_WIDGET (iter->data);
1928 /* Add a ref to w so we can explicitly destroy it later. */
1929 gtk_widget_ref (w);
1930 gtk_container_remove (GTK_CONTAINER (wcont), w);
1932 /* If there is a menu under this widget that has been detached,
1933 there is a reference to it, and just removing w from the
1934 container does not destroy the submenu. By explicitly
1935 destroying w we make sure the submenu is destroyed, thus
1936 removing the detached window also if there was one. */
1937 gtk_widget_destroy (w);
1941 /* Update the top level names in MENUBAR (i.e. not submenus).
1942 F is the frame the menu bar belongs to.
1943 *LIST is a list with the current menu bar names (menu item widgets).
1944 ITER is the item within *LIST that shall be updated.
1945 POS is the numerical position, starting at 0, of ITER in *LIST.
1946 VAL describes what the menu bar shall look like after the update.
1947 SELECT_CB is the callback to use when a menu item is selected.
1948 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1949 CL_DATA points to the callback data to be used for this menu bar.
1951 This function calls itself to walk through the menu bar names. */
1952 static void
1953 xg_update_menubar (menubar, f, list, iter, pos, val,
1954 select_cb, highlight_cb, cl_data)
1955 GtkWidget *menubar;
1956 FRAME_PTR f;
1957 GList **list;
1958 GList *iter;
1959 int pos;
1960 widget_value *val;
1961 GCallback select_cb;
1962 GCallback highlight_cb;
1963 xg_menu_cb_data *cl_data;
1965 if (! iter && ! val)
1966 return;
1967 else if (iter && ! val)
1969 /* Item(s) have been removed. Remove all remaining items. */
1970 remove_from_container (menubar, iter);
1972 /* All updated. */
1973 val = 0;
1974 iter = 0;
1976 else if (! iter && val)
1978 /* Item(s) added. Add all new items in one call. */
1979 create_menus (val, f, select_cb, 0, highlight_cb,
1980 0, 1, 0, menubar, cl_data, 0);
1982 /* All updated. */
1983 val = 0;
1984 iter = 0;
1986 /* Below this neither iter or val is NULL */
1987 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
1989 /* This item is still the same, check next item. */
1990 val = val->next;
1991 iter = g_list_next (iter);
1992 ++pos;
1994 else /* This item is changed. */
1996 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
1997 GtkMenuItem *witem2 = 0;
1998 int val_in_menubar = 0;
1999 int iter_in_new_menubar = 0;
2000 GList *iter2;
2001 widget_value *cur;
2003 /* See if the changed entry (val) is present later in the menu bar */
2004 for (iter2 = iter;
2005 iter2 && ! val_in_menubar;
2006 iter2 = g_list_next (iter2))
2008 witem2 = GTK_MENU_ITEM (iter2->data);
2009 val_in_menubar = xg_item_label_same_p (witem2, val->name);
2012 /* See if the current entry (iter) is present later in the
2013 specification for the new menu bar. */
2014 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
2015 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
2017 if (val_in_menubar && ! iter_in_new_menubar)
2019 int nr = pos;
2021 /* This corresponds to:
2022 Current: A B C
2023 New: A C
2024 Remove B. */
2026 gtk_widget_ref (GTK_WIDGET (witem));
2027 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
2028 gtk_widget_destroy (GTK_WIDGET (witem));
2030 /* Must get new list since the old changed. */
2031 g_list_free (*list);
2032 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2033 while (nr-- > 0) iter = g_list_next (iter);
2035 else if (! val_in_menubar && ! iter_in_new_menubar)
2037 /* This corresponds to:
2038 Current: A B C
2039 New: A X C
2040 Rename B to X. This might seem to be a strange thing to do,
2041 since if there is a menu under B it will be totally wrong for X.
2042 But consider editing a C file. Then there is a C-mode menu
2043 (corresponds to B above).
2044 If then doing C-x C-f the minibuf menu (X above) replaces the
2045 C-mode menu. When returning from the minibuffer, we get
2046 back the C-mode menu. Thus we do:
2047 Rename B to X (C-mode to minibuf menu)
2048 Rename X to B (minibuf to C-mode menu).
2049 If the X menu hasn't been invoked, the menu under B
2050 is up to date when leaving the minibuffer. */
2051 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
2052 char *utf8_label = get_utf8_string (val->name);
2053 GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
2055 gtk_label_set_text (wlabel, utf8_label);
2057 /* If this item has a submenu that has been detached, change
2058 the title in the WM decorations also. */
2059 if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
2060 /* Set the title of the detached window. */
2061 gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
2063 iter = g_list_next (iter);
2064 val = val->next;
2065 ++pos;
2067 else if (! val_in_menubar && iter_in_new_menubar)
2069 /* This corresponds to:
2070 Current: A B C
2071 New: A X B C
2072 Insert X. */
2074 int nr = pos;
2075 GList *group = 0;
2076 GtkWidget *w = xg_create_one_menuitem (val,
2078 select_cb,
2079 highlight_cb,
2080 cl_data,
2081 &group);
2083 gtk_widget_set_name (w, MENU_ITEM_NAME);
2084 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
2086 g_list_free (*list);
2087 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2088 while (nr-- > 0) iter = g_list_next (iter);
2089 iter = g_list_next (iter);
2090 val = val->next;
2091 ++pos;
2093 else /* if (val_in_menubar && iter_in_new_menubar) */
2095 int nr = pos;
2096 /* This corresponds to:
2097 Current: A B C
2098 New: A C B
2099 Move C before B */
2101 gtk_widget_ref (GTK_WIDGET (witem2));
2102 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
2103 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
2104 GTK_WIDGET (witem2), pos);
2105 gtk_widget_unref (GTK_WIDGET (witem2));
2107 g_list_free (*list);
2108 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
2109 while (nr-- > 0) iter = g_list_next (iter);
2110 val = val->next;
2111 ++pos;
2115 /* Update the rest of the menu bar. */
2116 xg_update_menubar (menubar, f, list, iter, pos, val,
2117 select_cb, highlight_cb, cl_data);
2120 /* Update the menu item W so it corresponds to VAL.
2121 SELECT_CB is the callback to use when a menu item is selected.
2122 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2123 CL_DATA is the data to set in the widget for menu invokation. */
2124 static void
2125 xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
2126 widget_value *val;
2127 GtkWidget *w;
2128 GCallback select_cb;
2129 GCallback highlight_cb;
2130 xg_menu_cb_data *cl_data;
2132 GtkWidget *wchild;
2133 GtkLabel *wlbl = 0;
2134 GtkLabel *wkey = 0;
2135 char *utf8_label;
2136 char *utf8_key;
2137 const char *old_label = 0;
2138 const char *old_key = 0;
2139 xg_menu_item_cb_data *cb_data;
2141 wchild = gtk_bin_get_child (GTK_BIN (w));
2142 utf8_label = get_utf8_string (val->name);
2143 utf8_key = get_utf8_string (val->key);
2145 /* See if W is a menu item with a key. See make_menu_item above. */
2146 if (GTK_IS_HBOX (wchild))
2148 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
2150 wlbl = GTK_LABEL (list->data);
2151 wkey = GTK_LABEL (list->next->data);
2152 g_list_free (list);
2154 if (! utf8_key)
2156 /* Remove the key and keep just the label. */
2157 gtk_widget_ref (GTK_WIDGET (wlbl));
2158 gtk_container_remove (GTK_CONTAINER (w), wchild);
2159 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
2160 wkey = 0;
2164 else /* Just a label. */
2166 wlbl = GTK_LABEL (wchild);
2168 /* Check if there is now a key. */
2169 if (utf8_key)
2171 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
2172 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
2174 wlbl = GTK_LABEL (list->data);
2175 wkey = GTK_LABEL (list->next->data);
2176 g_list_free (list);
2178 gtk_container_remove (GTK_CONTAINER (w), wchild);
2179 gtk_container_add (GTK_CONTAINER (w), wtoadd);
2184 if (wkey) old_key = gtk_label_get_label (wkey);
2185 if (wlbl) old_label = gtk_label_get_label (wlbl);
2187 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
2188 gtk_label_set_text (wkey, utf8_key);
2190 if (! old_label || strcmp (utf8_label, old_label) != 0)
2191 gtk_label_set_text (wlbl, utf8_label);
2193 if (utf8_key && utf8_key != val->key) g_free (utf8_key);
2194 if (utf8_label && utf8_label != val->name) g_free (utf8_label);
2196 if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
2197 gtk_widget_set_sensitive (w, FALSE);
2198 else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
2199 gtk_widget_set_sensitive (w, TRUE);
2201 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
2202 XG_ITEM_DATA);
2203 if (cb_data)
2205 cb_data->call_data = val->call_data;
2206 cb_data->help = val->help;
2207 cb_data->cl_data = cl_data;
2209 /* We assume the callback functions don't change. */
2210 if (val->call_data && ! val->contents)
2212 /* This item shall have a select callback. */
2213 if (! cb_data->select_id)
2214 cb_data->select_id
2215 = g_signal_connect (G_OBJECT (w), "activate",
2216 select_cb, cb_data);
2218 else if (cb_data->select_id)
2220 g_signal_handler_disconnect (w, cb_data->select_id);
2221 cb_data->select_id = 0;
2224 if (NILP (cb_data->help))
2226 /* Shall not have help. Remove if any existed previously. */
2227 if (cb_data->highlight_id)
2229 g_signal_handler_disconnect (G_OBJECT (w),
2230 cb_data->highlight_id);
2231 cb_data->highlight_id = 0;
2233 if (cb_data->unhighlight_id)
2235 g_signal_handler_disconnect (G_OBJECT (w),
2236 cb_data->unhighlight_id);
2237 cb_data->unhighlight_id = 0;
2240 else if (! cb_data->highlight_id && highlight_cb)
2242 /* Have help now, but didn't previously. Add callback. */
2243 cb_data->highlight_id
2244 = g_signal_connect (G_OBJECT (w),
2245 "enter-notify-event",
2246 G_CALLBACK (menuitem_highlight_callback),
2247 cb_data);
2248 cb_data->unhighlight_id
2249 = g_signal_connect (G_OBJECT (w),
2250 "leave-notify-event",
2251 G_CALLBACK (menuitem_highlight_callback),
2252 cb_data);
2257 /* Update the toggle menu item W so it corresponds to VAL. */
2258 static void
2259 xg_update_toggle_item (val, w)
2260 widget_value *val;
2261 GtkWidget *w;
2263 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2266 /* Update the radio menu item W so it corresponds to VAL. */
2267 static void
2268 xg_update_radio_item (val, w)
2269 widget_value *val;
2270 GtkWidget *w;
2272 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2275 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
2276 SUBMENU may be NULL, in that case a new menu is created.
2277 F is the frame the menu bar belongs to.
2278 VAL describes the contents of the menu bar.
2279 SELECT_CB is the callback to use when a menu item is selected.
2280 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2281 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2282 CL_DATA is the call back data to use for any newly created items.
2284 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
2285 was NULL. */
2287 static GtkWidget *
2288 xg_update_submenu (submenu, f, val,
2289 select_cb, deactivate_cb, highlight_cb, cl_data)
2290 GtkWidget *submenu;
2291 FRAME_PTR f;
2292 widget_value *val;
2293 GCallback select_cb;
2294 GCallback deactivate_cb;
2295 GCallback highlight_cb;
2296 xg_menu_cb_data *cl_data;
2298 GtkWidget *newsub = submenu;
2299 GList *list = 0;
2300 GList *iter;
2301 widget_value *cur;
2302 int has_tearoff_p = 0;
2303 GList *first_radio = 0;
2305 if (submenu)
2306 list = gtk_container_get_children (GTK_CONTAINER (submenu));
2308 for (cur = val, iter = list;
2309 cur && iter;
2310 iter = g_list_next (iter), cur = cur->next)
2312 GtkWidget *w = GTK_WIDGET (iter->data);
2314 /* Skip tearoff items, they have no counterpart in val. */
2315 if (GTK_IS_TEAROFF_MENU_ITEM (w))
2317 has_tearoff_p = 1;
2318 iter = g_list_next (iter);
2319 if (iter) w = GTK_WIDGET (iter->data);
2320 else break;
2323 /* Remember first radio button in a group. If we get a mismatch in
2324 a radio group we must rebuild the whole group so that the connections
2325 in GTK becomes correct. */
2326 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
2327 first_radio = iter;
2328 else if (cur->button_type != BUTTON_TYPE_RADIO
2329 && ! GTK_IS_RADIO_MENU_ITEM (w))
2330 first_radio = 0;
2332 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
2334 if (! xg_separator_p (cur->name))
2335 break;
2337 else if (GTK_IS_CHECK_MENU_ITEM (w))
2339 if (cur->button_type != BUTTON_TYPE_TOGGLE)
2340 break;
2341 xg_update_toggle_item (cur, w);
2342 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2344 else if (GTK_IS_RADIO_MENU_ITEM (w))
2346 if (cur->button_type != BUTTON_TYPE_RADIO)
2347 break;
2348 xg_update_radio_item (cur, w);
2349 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2351 else if (GTK_IS_MENU_ITEM (w))
2353 GtkMenuItem *witem = GTK_MENU_ITEM (w);
2354 GtkWidget *sub;
2356 if (cur->button_type != BUTTON_TYPE_NONE ||
2357 xg_separator_p (cur->name))
2358 break;
2360 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2362 sub = gtk_menu_item_get_submenu (witem);
2363 if (sub && ! cur->contents)
2365 /* Not a submenu anymore. */
2366 gtk_widget_ref (sub);
2367 gtk_menu_item_remove_submenu (witem);
2368 gtk_widget_destroy (sub);
2370 else if (cur->contents)
2372 GtkWidget *nsub;
2374 nsub = xg_update_submenu (sub, f, cur->contents,
2375 select_cb, deactivate_cb,
2376 highlight_cb, cl_data);
2378 /* If this item just became a submenu, we must set it. */
2379 if (nsub != sub)
2380 gtk_menu_item_set_submenu (witem, nsub);
2383 else
2385 /* Structural difference. Remove everything from here and down
2386 in SUBMENU. */
2387 break;
2391 /* Remove widgets from first structual change. */
2392 if (iter)
2394 /* If we are adding new menu items below, we must remove from
2395 first radio button so that radio groups become correct. */
2396 if (cur && first_radio) remove_from_container (submenu, first_radio);
2397 else remove_from_container (submenu, iter);
2400 if (cur)
2402 /* More items added. Create them. */
2403 newsub = create_menus (cur,
2405 select_cb,
2406 deactivate_cb,
2407 highlight_cb,
2410 ! has_tearoff_p,
2411 submenu,
2412 cl_data,
2416 if (list) g_list_free (list);
2418 return newsub;
2421 /* Update the MENUBAR.
2422 F is the frame the menu bar belongs to.
2423 VAL describes the contents of the menu bar.
2424 If DEEP_P is non-zero, rebuild all but the top level menu names in
2425 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
2426 SELECT_CB is the callback to use when a menu item is selected.
2427 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2428 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
2429 void
2430 xg_modify_menubar_widgets (menubar, f, val, deep_p,
2431 select_cb, deactivate_cb, highlight_cb)
2432 GtkWidget *menubar;
2433 FRAME_PTR f;
2434 widget_value *val;
2435 int deep_p;
2436 GCallback select_cb;
2437 GCallback deactivate_cb;
2438 GCallback highlight_cb;
2440 xg_menu_cb_data *cl_data;
2441 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
2443 if (! list) return;
2445 cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
2446 XG_FRAME_DATA);
2448 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
2449 select_cb, highlight_cb, cl_data);
2451 if (deep_p);
2453 widget_value *cur;
2455 /* Update all sub menus.
2456 We must keep the submenus (GTK menu item widgets) since the
2457 X Window in the XEvent that activates the menu are those widgets. */
2459 /* Update cl_data, menu_item things in F may have changed. */
2460 update_cl_data (cl_data, f, highlight_cb);
2462 for (cur = val->contents; cur; cur = cur->next)
2464 GList *iter;
2465 GtkWidget *sub = 0;
2466 GtkWidget *newsub;
2467 GtkMenuItem *witem;
2469 /* Find sub menu that corresponds to val and update it. */
2470 for (iter = list ; iter; iter = g_list_next (iter))
2472 witem = GTK_MENU_ITEM (iter->data);
2473 if (xg_item_label_same_p (witem, cur->name))
2475 sub = gtk_menu_item_get_submenu (witem);
2476 break;
2480 newsub = xg_update_submenu (sub,
2482 cur->contents,
2483 select_cb,
2484 deactivate_cb,
2485 highlight_cb,
2486 cl_data);
2487 /* sub may still be NULL. If we just updated non deep and added
2488 a new menu bar item, it has no sub menu yet. So we set the
2489 newly created sub menu under witem. */
2490 if (newsub != sub)
2492 xg_set_screen (newsub, f);
2493 gtk_menu_item_set_submenu (witem, newsub);
2498 g_list_free (list);
2499 gtk_widget_show_all (menubar);
2502 /* Recompute all the widgets of frame F, when the menu bar has been
2503 changed. Value is non-zero if widgets were updated. */
2506 xg_update_frame_menubar (f)
2507 FRAME_PTR f;
2509 struct x_output *x = f->output_data.x;
2510 GtkRequisition req;
2512 if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
2513 return 0;
2515 BLOCK_INPUT;
2517 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
2518 FALSE, FALSE, 0);
2519 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
2521 gtk_widget_show_all (x->menubar_widget);
2522 gtk_widget_size_request (x->menubar_widget, &req);
2524 FRAME_MENUBAR_HEIGHT (f) = req.height;
2526 /* The height has changed, resize outer widget and set columns
2527 rows to what we had before adding the menu bar. */
2528 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2530 SET_FRAME_GARBAGED (f);
2531 UNBLOCK_INPUT;
2533 return 1;
2536 /* Get rid of the menu bar of frame F, and free its storage.
2537 This is used when deleting a frame, and when turning off the menu bar. */
2539 void
2540 free_frame_menubar (f)
2541 FRAME_PTR f;
2543 struct x_output *x = f->output_data.x;
2545 if (x->menubar_widget)
2547 BLOCK_INPUT;
2549 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
2550 /* The menubar and its children shall be deleted when removed from
2551 the container. */
2552 x->menubar_widget = 0;
2553 FRAME_MENUBAR_HEIGHT (f) = 0;
2555 /* The height has changed, resize outer widget and set columns
2556 rows to what we had before removing the menu bar. */
2557 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2559 SET_FRAME_GARBAGED (f);
2560 UNBLOCK_INPUT;
2566 /***********************************************************************
2567 Scroll bar functions
2568 ***********************************************************************/
2571 /* Setting scroll bar values invokes the callback. Use this variable
2572 to indicate that callback should do nothing. */
2573 int xg_ignore_gtk_scrollbar;
2575 /* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
2576 32 bits. But we want to store pointers, and they may be larger
2577 than 32 bits. Keep a mapping from integer index to widget pointers
2578 to get around the 32 bit limitation. */
2579 static struct
2581 GtkWidget **widgets;
2582 int max_size;
2583 int used;
2584 } id_to_widget;
2586 /* Grow this much every time we need to allocate more */
2587 #define ID_TO_WIDGET_INCR 32
2589 /* Store the widget pointer W in id_to_widget and return the integer index. */
2590 static int
2591 xg_store_widget_in_map (w)
2592 GtkWidget *w;
2594 int i;
2596 if (id_to_widget.max_size == id_to_widget.used)
2598 int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
2600 id_to_widget.widgets = xrealloc (id_to_widget.widgets,
2601 sizeof (GtkWidget *)*new_size);
2603 for (i = id_to_widget.max_size; i < new_size; ++i)
2604 id_to_widget.widgets[i] = 0;
2605 id_to_widget.max_size = new_size;
2608 /* Just loop over the array and find a free place. After all,
2609 how many scroll bars are we creating? Should be a small number.
2610 The check above guarantees we will find a free place. */
2611 for (i = 0; i < id_to_widget.max_size; ++i)
2613 if (! id_to_widget.widgets[i])
2615 id_to_widget.widgets[i] = w;
2616 ++id_to_widget.used;
2618 return i;
2622 /* Should never end up here */
2623 abort ();
2626 /* Remove pointer at IDX from id_to_widget.
2627 Called when scroll bar is destroyed. */
2628 static void
2629 xg_remove_widget_from_map (idx)
2630 int idx;
2632 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2634 id_to_widget.widgets[idx] = 0;
2635 --id_to_widget.used;
2639 /* Get the widget pointer at IDX from id_to_widget. */
2640 static GtkWidget *
2641 xg_get_widget_from_map (idx)
2642 int idx;
2644 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2645 return id_to_widget.widgets[idx];
2647 return 0;
2650 /* Return the scrollbar id for X Window WID on display DPY.
2651 Return -1 if WID not in id_to_widget. */
2653 xg_get_scroll_id_for_window (dpy, wid)
2654 Display *dpy;
2655 Window wid;
2657 int idx;
2658 GtkWidget *w;
2660 w = xg_win_to_widget (dpy, wid);
2662 if (w)
2664 for (idx = 0; idx < id_to_widget.max_size; ++idx)
2665 if (id_to_widget.widgets[idx] == w)
2666 return idx;
2669 return -1;
2672 /* Callback invoked when scroll bar WIDGET is destroyed.
2673 DATA is the index into id_to_widget for WIDGET.
2674 We free pointer to last scroll bar values here and remove the index. */
2675 static void
2676 xg_gtk_scroll_destroy (widget, data)
2677 GtkWidget *widget;
2678 gpointer data;
2680 gpointer p;
2681 int id = (int)data;
2683 p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
2684 if (p) xfree (p);
2685 xg_remove_widget_from_map (id);
2688 /* Callback for button press/release events. Used to start timer so that
2689 the scroll bar repetition timer in GTK gets handeled.
2690 Also, sets bar->dragging to Qnil when dragging (button release) is done.
2691 WIDGET is the scroll bar widget the event is for (not used).
2692 EVENT contains the event.
2693 USER_DATA points to the struct scrollbar structure.
2695 Returns FALSE to tell GTK that it shall continue propagate the event
2696 to widgets. */
2697 static gboolean
2698 scroll_bar_button_cb (widget, event, user_data)
2699 GtkWidget *widget;
2700 GdkEventButton *event;
2701 gpointer user_data;
2703 if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
2704 xg_start_timer ();
2705 else if (event->type == GDK_BUTTON_RELEASE)
2707 struct scroll_bar *bar = (struct scroll_bar *) user_data;
2708 if (xg_timer) xg_stop_timer ();
2709 bar->dragging = Qnil;
2712 return FALSE;
2715 /* Create a scroll bar widget for frame F. Store the scroll bar
2716 in BAR.
2717 SCROLL_CALLBACK is the callback to invoke when the value of the
2718 bar changes.
2719 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
2720 to set resources for the widget. */
2721 void
2722 xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
2723 FRAME_PTR f;
2724 struct scroll_bar *bar;
2725 GCallback scroll_callback;
2726 char *scroll_bar_name;
2728 GtkWidget *wscroll;
2729 GtkObject *vadj;
2730 int scroll_id;
2732 /* Page, step increment values are not so important here, they
2733 will be corrected in x_set_toolkit_scroll_bar_thumb. */
2734 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
2735 0.1, 0.1, 0.1);
2737 wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
2738 gtk_widget_set_name (wscroll, scroll_bar_name);
2739 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
2741 scroll_id = xg_store_widget_in_map (wscroll);
2743 g_signal_connect (G_OBJECT (wscroll),
2744 "value-changed",
2745 scroll_callback,
2746 (gpointer) bar);
2747 g_signal_connect (G_OBJECT (wscroll),
2748 "destroy",
2749 G_CALLBACK (xg_gtk_scroll_destroy),
2750 (gpointer) scroll_id);
2752 /* Connect to button press and button release to detect if any scroll bar
2753 has the pointer. */
2754 g_signal_connect (G_OBJECT (wscroll),
2755 "button-press-event",
2756 G_CALLBACK (scroll_bar_button_cb),
2757 (gpointer) bar);
2758 g_signal_connect (G_OBJECT (wscroll),
2759 "button-release-event",
2760 G_CALLBACK (scroll_bar_button_cb),
2761 (gpointer) bar);
2763 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
2764 wscroll, -1, -1);
2766 /* Set the cursor to an arrow. */
2767 xg_set_cursor (wscroll, FRAME_X_DISPLAY_INFO (f)->xg_cursor);
2769 SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
2772 /* Make the scroll bar represented by SCROLLBAR_ID visible. */
2773 void
2774 xg_show_scroll_bar (scrollbar_id)
2775 int scrollbar_id;
2777 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2778 if (w)
2779 gtk_widget_show (w);
2782 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
2783 void
2784 xg_remove_scroll_bar (f, scrollbar_id)
2785 FRAME_PTR f;
2786 int scrollbar_id;
2788 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2789 if (w)
2791 gtk_widget_destroy (w);
2792 SET_FRAME_GARBAGED (f);
2796 /* Find left/top for widget W in GtkFixed widget WFIXED. */
2797 static void
2798 xg_find_top_left_in_fixed (w, wfixed, left, top)
2799 GtkWidget *w, *wfixed;
2800 int *left, *top;
2802 GList *iter;
2804 for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
2806 GtkFixedChild *child = (GtkFixedChild *) iter->data;
2808 if (child->widget == w)
2810 *left = child->x;
2811 *top = child->y;
2812 return;
2816 /* Shall never end up here. */
2817 abort ();
2820 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
2821 in frame F.
2822 TOP/LEFT are the new pixel positions where the bar shall appear.
2823 WIDTH, HEIGHT is the size in pixels the bar shall have. */
2824 void
2825 xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height,
2826 real_left, canon_width)
2827 FRAME_PTR f;
2828 int scrollbar_id;
2829 int top;
2830 int left;
2831 int width;
2832 int height;
2835 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
2837 if (wscroll)
2839 GtkWidget *wfixed = f->output_data.x->edit_widget;
2841 gtk_container_set_reallocate_redraws (GTK_CONTAINER (wfixed), TRUE);
2843 /* Move and resize to new values. */
2844 gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
2845 gtk_widget_set_size_request (wscroll, width, height);
2847 /* Must force out update so changed scroll bars gets redrawn. */
2848 gdk_window_process_all_updates ();
2850 /* Scroll bars in GTK has a fixed width, so if we say width 16, it
2851 will only be its fixed width (14 is default) anyway, the rest is
2852 blank. We are drawing the mode line across scroll bars when
2853 the frame is split:
2854 |bar| |fringe|
2855 ----------------
2856 mode line
2857 ----------------
2858 |bar| |fringe|
2860 When we "unsplit" the frame:
2862 |bar| |fringe|
2863 -| |-| |
2864 m¦ |i| |
2865 -| |-| |
2866 | | | |
2869 the remains of the mode line can be seen in these blank spaces.
2870 So we must clear them explicitly.
2871 GTK scroll bars should do that, but they don't.
2872 Also, the canonical width may be wider than the width for the
2873 scroll bar so that there is some space (typically 1 pixel) between
2874 the scroll bar and the edge of the window and between the scroll
2875 bar and the fringe. */
2877 XClearWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (wscroll));
2879 SET_FRAME_GARBAGED (f);
2880 cancel_mouse_face (f);
2884 /* Set the thumb size and position of scroll bar BAR. We are currently
2885 displaying PORTION out of a whole WHOLE, and our position POSITION. */
2886 void
2887 xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
2888 struct scroll_bar *bar;
2889 int portion, position, whole;
2891 GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
2893 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2895 if (wscroll && NILP (bar->dragging))
2897 GtkAdjustment *adj;
2898 gdouble shown;
2899 gdouble top;
2900 int size, value;
2901 int new_step;
2902 int changed = 0;
2904 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
2906 /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
2907 rather than the real portion value. This makes the thumb less likely
2908 to resize and that looks better. */
2909 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
2910 /* When the thumb is at the bottom, position == whole.
2911 So we need to increase `whole' to make space for the thumb. */
2912 whole += portion;
2914 if (whole <= 0)
2915 top = 0, shown = 1;
2916 else
2918 top = (gdouble) position / whole;
2919 shown = (gdouble) portion / whole;
2922 size = shown * XG_SB_RANGE;
2923 size = min (size, XG_SB_RANGE);
2924 size = max (size, 1);
2926 value = top * XG_SB_RANGE;
2927 value = min (value, XG_SB_MAX - size);
2928 value = max (value, XG_SB_MIN);
2930 /* Assume all lines are of equal size. */
2931 new_step = size / max (1, FRAME_LINES (f));
2933 if ((int) adj->page_size != size
2934 || (int) adj->step_increment != new_step)
2936 adj->page_size = size;
2937 adj->step_increment = new_step;
2938 /* Assume a page increment is about 95% of the page size */
2939 adj->page_increment = (int) (0.95*adj->page_size);
2940 changed = 1;
2943 if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
2945 GtkWidget *wfixed = f->output_data.x->edit_widget;
2947 BLOCK_INPUT;
2949 /* gtk_range_set_value invokes the callback. Set
2950 ignore_gtk_scrollbar to make the callback do nothing */
2951 xg_ignore_gtk_scrollbar = 1;
2953 if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
2954 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
2955 else if (changed)
2956 gtk_adjustment_changed (adj);
2958 xg_ignore_gtk_scrollbar = 0;
2960 UNBLOCK_INPUT;
2966 /***********************************************************************
2967 Tool bar functions
2968 ***********************************************************************/
2969 /* The key for the data we put in the GtkImage widgets. The data is
2970 the image used by Emacs. We use this to see if we need to update
2971 the GtkImage with a new image. */
2972 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
2974 /* Callback function invoked when a tool bar item is pressed.
2975 W is the button widget in the tool bar that got pressed,
2976 CLIENT_DATA is an integer that is the index of the button in the
2977 tool bar. 0 is the first button. */
2978 static void
2979 xg_tool_bar_callback (w, client_data)
2980 GtkWidget *w;
2981 gpointer client_data;
2983 int idx = (int)client_data;
2984 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
2985 Lisp_Object key, frame;
2986 struct input_event event;
2987 EVENT_INIT (event);
2989 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
2990 return;
2992 idx *= TOOL_BAR_ITEM_NSLOTS;
2994 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
2995 XSETFRAME (frame, f);
2996 event.kind = TOOL_BAR_EVENT;
2997 event.frame_or_window = frame;
2998 event.arg = frame;
2999 kbd_buffer_store_event (&event);
3001 event.kind = TOOL_BAR_EVENT;
3002 event.frame_or_window = frame;
3003 event.arg = key;
3004 event.modifiers = 0; /* These are not available. */
3005 kbd_buffer_store_event (&event);
3008 /* This callback is called when a tool bar is detached. We must set
3009 the height of the tool bar to zero when this happens so frame sizes
3010 are correctly calculated.
3011 WBOX is the handle box widget that enables detach/attach of the tool bar.
3012 W is the tool bar widget.
3013 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
3014 static void
3015 xg_tool_bar_detach_callback (wbox, w, client_data)
3016 GtkHandleBox *wbox;
3017 GtkWidget *w;
3018 gpointer client_data;
3020 FRAME_PTR f = (FRAME_PTR) client_data;
3022 if (f)
3024 /* When detaching a tool bar, not everything dissapear. There are
3025 a few pixels left that are used to drop the tool bar back into
3026 place. */
3027 int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox));
3028 FRAME_TOOLBAR_HEIGHT (f) = 2;
3030 /* The height has changed, resize outer widget and set columns
3031 rows to what we had before detaching the tool bar. */
3032 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3036 /* This callback is called when a tool bar is reattached. We must set
3037 the height of the tool bar when this happens so frame sizes
3038 are correctly calculated.
3039 WBOX is the handle box widget that enables detach/attach of the tool bar.
3040 W is the tool bar widget.
3041 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
3042 static void
3043 xg_tool_bar_attach_callback (wbox, w, client_data)
3044 GtkHandleBox *wbox;
3045 GtkWidget *w;
3046 gpointer client_data;
3048 FRAME_PTR f = (FRAME_PTR) client_data;
3050 if (f)
3052 GtkRequisition req;
3054 gtk_widget_size_request (w, &req);
3055 FRAME_TOOLBAR_HEIGHT (f) = req.height;
3057 /* The height has changed, resize outer widget and set columns
3058 rows to what we had before detaching the tool bar. */
3059 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3063 /* This callback is called when the mouse enters or leaves a tool bar item.
3064 It is used for displaying and hiding the help text.
3065 W is the tool bar item, a button.
3066 EVENT is either an enter event or leave event.
3067 CLIENT_DATA is an integer that is the index of the button in the
3068 tool bar. 0 is the first button.
3070 Returns FALSE to tell GTK to keep processing this event. */
3071 static gboolean
3072 xg_tool_bar_help_callback (w, event, client_data)
3073 GtkWidget *w;
3074 GdkEventCrossing *event;
3075 gpointer client_data;
3077 int idx = (int)client_data;
3078 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
3079 Lisp_Object help, frame;
3081 if (! GTK_IS_BUTTON (w))
3083 return FALSE;
3086 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
3087 return FALSE;
3089 if (event->type == GDK_ENTER_NOTIFY)
3091 idx *= TOOL_BAR_ITEM_NSLOTS;
3092 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
3094 if (NILP (help))
3095 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
3097 else
3098 help = Qnil;
3100 XSETFRAME (frame, f);
3101 kbd_buffer_store_help_event (frame, help);
3103 return FALSE;
3107 /* This callback is called when a tool bar item shall be redrawn.
3108 It modifies the expose event so that the GtkImage widget redraws the
3109 whole image. This to overcome a bug that makes GtkImage draw the image
3110 in the wrong place when it tries to redraw just a part of the image.
3111 W is the GtkImage to be redrawn.
3112 EVENT is the expose event for W.
3113 CLIENT_DATA is unused.
3115 Returns FALSE to tell GTK to keep processing this event. */
3116 static gboolean
3117 xg_tool_bar_item_expose_callback (w, event, client_data)
3118 GtkWidget *w;
3119 GdkEventExpose *event;
3120 gpointer client_data;
3122 gint width, height;
3124 gdk_drawable_get_size (event->window, &width, &height);
3126 event->area.x -= width > event->area.width ? width-event->area.width : 0;
3127 event->area.y -= height > event->area.height ? height-event->area.height : 0;
3129 event->area.x = max (0, event->area.x);
3130 event->area.y = max (0, event->area.y);
3132 event->area.width = max (width, event->area.width);
3133 event->area.height = max (height, event->area.height);
3135 return FALSE;
3138 /* This callback is called when a tool bar shall be redrawn.
3139 We need to update the tool bar from here in case the image cache
3140 has deleted the pixmaps used in the tool bar.
3141 W is the GtkToolbar to be redrawn.
3142 EVENT is the expose event for W.
3143 CLIENT_DATA is pointing to the frame for this tool bar.
3145 Returns FALSE to tell GTK to keep processing this event. */
3146 static gboolean
3147 xg_tool_bar_expose_callback (w, event, client_data)
3148 GtkWidget *w;
3149 GdkEventExpose *event;
3150 gpointer client_data;
3152 update_frame_tool_bar ((FRAME_PTR) client_data);
3153 return FALSE;
3156 static void
3157 xg_create_tool_bar (f)
3158 FRAME_PTR f;
3160 struct x_output *x = f->output_data.x;
3161 GtkRequisition req;
3162 int vbox_pos = x->menubar_widget ? 1 : 0;
3164 x->toolbar_widget = gtk_toolbar_new ();
3165 x->handlebox_widget = gtk_handle_box_new ();
3166 gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
3167 x->toolbar_widget);
3169 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
3170 FALSE, FALSE, 0);
3172 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
3173 vbox_pos);
3175 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
3177 /* We only have icons, so override any user setting. We could
3178 use the caption property of the toolbar item (see update_frame_tool_bar
3179 below), but some of those strings are long, making the toolbar so
3180 long it does not fit on the screen. The GtkToolbar widget makes every
3181 item equal size, so the longest caption determine the size of every
3182 tool bar item. I think the creators of the GtkToolbar widget
3183 counted on 4 or 5 character long strings. */
3184 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
3185 gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
3186 GTK_ORIENTATION_HORIZONTAL);
3188 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
3189 G_CALLBACK (xg_tool_bar_detach_callback), f);
3190 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
3191 G_CALLBACK (xg_tool_bar_attach_callback), f);
3192 g_signal_connect (G_OBJECT (x->toolbar_widget),
3193 "expose-event",
3194 G_CALLBACK (xg_tool_bar_expose_callback),
3197 gtk_widget_show_all (x->handlebox_widget);
3199 gtk_widget_size_request (x->toolbar_widget, &req);
3200 FRAME_TOOLBAR_HEIGHT (f) = req.height;
3202 /* The height has changed, resize outer widget and set columns
3203 rows to what we had before adding the tool bar. */
3204 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3206 SET_FRAME_GARBAGED (f);
3209 void
3210 update_frame_tool_bar (f)
3211 FRAME_PTR f;
3213 int i;
3214 GtkRequisition old_req, new_req;
3215 GList *icon_list;
3216 GList *iter;
3217 struct x_output *x = f->output_data.x;
3219 if (! FRAME_GTK_WIDGET (f))
3220 return;
3222 BLOCK_INPUT;
3224 if (! x->toolbar_widget)
3225 xg_create_tool_bar (f);
3227 gtk_widget_size_request (x->toolbar_widget, &old_req);
3229 icon_list = gtk_container_get_children (GTK_CONTAINER (x->toolbar_widget));
3230 iter = icon_list;
3232 for (i = 0; i < f->n_tool_bar_items; ++i)
3234 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
3236 int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
3237 int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
3238 int idx;
3239 int img_id;
3240 struct image *img;
3241 Lisp_Object image;
3242 GtkWidget *wicon = iter ? GTK_WIDGET (iter->data) : 0;
3244 if (iter) iter = g_list_next (iter);
3246 /* If image is a vector, choose the image according to the
3247 button state. */
3248 image = PROP (TOOL_BAR_ITEM_IMAGES);
3249 if (VECTORP (image))
3251 if (enabled_p)
3252 idx = (selected_p
3253 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
3254 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
3255 else
3256 idx = (selected_p
3257 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
3258 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
3260 xassert (ASIZE (image) >= idx);
3261 image = AREF (image, idx);
3263 else
3264 idx = -1;
3266 /* Ignore invalid image specifications. */
3267 if (!valid_image_p (image))
3269 if (wicon) gtk_widget_hide (wicon);
3270 continue;
3273 img_id = lookup_image (f, image);
3274 img = IMAGE_FROM_ID (f, img_id);
3275 prepare_image_for_display (f, img);
3277 if (img->load_failed_p || img->pixmap == None)
3279 if (wicon) gtk_widget_hide (wicon);
3280 continue;
3283 if (! wicon)
3285 GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
3287 gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
3288 0, 0, 0,
3290 GTK_SIGNAL_FUNC (xg_tool_bar_callback),
3291 (gpointer)i);
3293 /* Save the image so we can see if an update is needed when
3294 this function is called again. */
3295 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
3296 (gpointer)img->pixmap);
3298 /* Catch expose events to overcome an annoying redraw bug, see
3299 comment for xg_tool_bar_item_expose_callback. */
3300 g_signal_connect (G_OBJECT (w),
3301 "expose-event",
3302 G_CALLBACK (xg_tool_bar_item_expose_callback),
3305 /* We must set sensitive on the button that is the parent
3306 of the GtkImage parent. Go upwards until we find the button. */
3307 while (! GTK_IS_BUTTON (w))
3308 w = gtk_widget_get_parent (w);
3310 if (w)
3312 /* Save the frame in the button so the xg_tool_bar_callback
3313 can get at it. */
3314 g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
3315 gtk_widget_set_sensitive (w, enabled_p);
3317 /* Use enter/leave notify to show help. We use the events
3318 rather than the GtkButton specific signals "enter" and
3319 "leave", so we can have only one callback. The event
3320 will tell us what kind of event it is. */
3321 g_signal_connect (G_OBJECT (w),
3322 "enter-notify-event",
3323 G_CALLBACK (xg_tool_bar_help_callback),
3324 (gpointer)i);
3325 g_signal_connect (G_OBJECT (w),
3326 "leave-notify-event",
3327 G_CALLBACK (xg_tool_bar_help_callback),
3328 (gpointer)i);
3331 else
3333 /* The child of the tool bar is a button. Inside that button
3334 is a vbox. Inside that vbox is the GtkImage. */
3335 GtkWidget *wvbox = gtk_bin_get_child (GTK_BIN (wicon));
3336 GList *chlist = gtk_container_get_children (GTK_CONTAINER (wvbox));
3337 GtkImage *wimage = GTK_IMAGE (chlist->data);
3338 Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
3339 XG_TOOL_BAR_IMAGE_DATA);
3340 g_list_free (chlist);
3342 if (old_img != img->pixmap)
3343 (void) xg_get_image_for_pixmap (f, img, x->widget, wimage);
3345 g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
3346 (gpointer)img->pixmap);
3348 gtk_widget_set_sensitive (wicon, enabled_p);
3349 gtk_widget_show (wicon);
3352 #undef PROP
3355 /* Remove buttons not longer needed. We just hide them so they
3356 can be reused later on. */
3357 while (iter)
3359 GtkWidget *w = GTK_WIDGET (iter->data);
3360 gtk_widget_hide (w);
3361 iter = g_list_next (iter);
3364 gtk_widget_size_request (x->toolbar_widget, &new_req);
3365 if (old_req.height != new_req.height)
3367 FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
3368 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3371 if (icon_list) g_list_free (icon_list);
3373 UNBLOCK_INPUT;
3376 void
3377 free_frame_tool_bar (f)
3378 FRAME_PTR f;
3380 struct x_output *x = f->output_data.x;
3382 if (x->toolbar_widget)
3384 BLOCK_INPUT;
3385 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
3386 x->handlebox_widget);
3387 x->toolbar_widget = 0;
3388 x->handlebox_widget = 0;
3389 FRAME_TOOLBAR_HEIGHT (f) = 0;
3391 /* The height has changed, resize outer widget and set columns
3392 rows to what we had before removing the tool bar. */
3393 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3395 SET_FRAME_GARBAGED (f);
3396 UNBLOCK_INPUT;
3402 /***********************************************************************
3403 Initializing
3404 ***********************************************************************/
3405 void
3406 xg_initialize ()
3408 xg_ignore_gtk_scrollbar = 0;
3409 xg_detached_menus = 0;
3410 xg_menu_cb_list.prev = xg_menu_cb_list.next =
3411 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
3413 id_to_widget.max_size = id_to_widget.used = 0;
3414 id_to_widget.widgets = 0;
3416 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
3417 bindings. It doesn't seem to be any way to remove properties,
3418 so we set it to VoidSymbol which in X means "no key". */
3419 gtk_settings_set_string_property (gtk_settings_get_default (),
3420 "gtk-menu-bar-accel",
3421 "VoidSymbol",
3422 EMACS_CLASS);
3424 /* Make GTK text input widgets use Emacs style keybindings. This is
3425 Emacs after all. */
3426 gtk_settings_set_string_property (gtk_settings_get_default (),
3427 "gtk-key-theme-name",
3428 "Emacs",
3429 EMACS_CLASS);
3432 #endif /* USE_GTK */
3434 /* arch-tag: fe7104da-bc1e-4aba-9bd1-f349c528f7e3
3435 (do not change this comment) */