(realize_default_face): Do not abort if lface is non-existent -
[emacs.git] / src / gtkutil.c
blobf2690635f533ae64a239fe53b15037e398cde58a
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 <gdk/gdkkeysyms.h>
36 #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
37 (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
41 /***********************************************************************
42 Utility functions
43 ***********************************************************************/
44 /* The timer for scroll bar repetition and menu bar timeouts.
45 NULL if no timer is started. */
46 static struct atimer *xg_timer;
48 /* The cursor used for scroll bars and popup menus.
49 We only have one cursor for all scroll bars and all popup menus. */
50 static GdkCursor *xg_left_ptr_cursor;
53 /* The next two variables and functions are taken from lwlib. */
54 static widget_value *widget_value_free_list;
55 static int malloc_cpt;
57 /* Allocate a widget_value structure, either by taking one from the
58 widget_value_free_list or by malloc:ing a new one.
60 Return a pointer to the allocated structure. */
61 widget_value *
62 malloc_widget_value ()
64 widget_value *wv;
65 if (widget_value_free_list)
67 wv = widget_value_free_list;
68 widget_value_free_list = wv->free_list;
69 wv->free_list = 0;
71 else
73 wv = (widget_value *) malloc (sizeof (widget_value));
74 malloc_cpt++;
76 memset (wv, 0, sizeof (widget_value));
77 return wv;
80 /* This is analogous to free. It frees only what was allocated
81 by malloc_widget_value, and no substructures. */
82 void
83 free_widget_value (wv)
84 widget_value *wv;
86 if (wv->free_list)
87 abort ();
89 if (malloc_cpt > 25)
91 /* When the number of already allocated cells is too big,
92 We free it. */
93 free (wv);
94 malloc_cpt--;
96 else
98 wv->free_list = widget_value_free_list;
99 widget_value_free_list = wv;
103 /* Set *CURSOR on W and all widgets W contain. We must do like this
104 for scroll bars and menu because they create widgets internally,
105 and it is those widgets that are visible.
107 If *CURSOR is NULL, create a GDK_LEFT_PTR cursor and set *CURSOR to
108 the created cursor. */
109 void
110 xg_set_cursor (w, cursor)
111 GtkWidget *w;
112 GdkCursor **cursor;
114 GList *children = gdk_window_peek_children (w->window);
116 /* Create the cursor unless already created. */
117 if (! *cursor)
118 *cursor = gdk_cursor_new (GDK_LEFT_PTR);
120 gdk_window_set_cursor (w->window, *cursor);
122 /* The scroll bar widget has more than one GDK window (had to look at
123 the source to figure this out), and there is no way to set cursor
124 on widgets in GTK. So we must set the cursor for all GDK windows.
125 Ditto for menus. */
127 for ( ; children; children = g_list_next (children))
128 gdk_window_set_cursor (GDK_WINDOW (children->data), *cursor);
131 /* Timer function called when a timeout occurs for xg_timer.
132 This function processes all GTK events in a recursive event loop.
133 This is done because GTK timer events are not seen by Emacs event
134 detection, Emacs only looks for X events. When a scroll bar has the
135 pointer (detected by button press/release events below) an Emacs
136 timer is started, and this function can then check if the GTK timer
137 has expired by calling the GTK event loop.
138 Also, when a menu is active, it has a small timeout before it
139 pops down the sub menu under it. */
140 static void
141 xg_process_timeouts (timer)
142 struct atimer *timer;
144 BLOCK_INPUT;
145 /* Ideally we would like to just handle timer events, like the Xt version
146 of this does in xterm.c, but there is no such feature in GTK. */
147 while (gtk_events_pending ())
148 gtk_main_iteration ();
149 UNBLOCK_INPUT;
152 /* Start the xg_timer with an interval of 0.1 seconds, if not already started.
153 xg_process_timeouts is called when the timer expires. The timer
154 started is continuous, i.e. runs until xg_stop_timer is called. */
155 static void
156 xg_start_timer ()
158 if (! xg_timer)
160 EMACS_TIME interval;
161 EMACS_SET_SECS_USECS (interval, 0, 100000);
162 xg_timer = start_atimer (ATIMER_CONTINUOUS,
163 interval,
164 xg_process_timeouts,
169 /* Stop the xg_timer if started. */
170 static void
171 xg_stop_timer ()
173 if (xg_timer)
175 cancel_atimer (xg_timer);
176 xg_timer = 0;
180 /* Insert NODE into linked LIST. */
181 static void
182 xg_list_insert (xg_list_node *list, xg_list_node *node)
184 xg_list_node *list_start = list->next;
186 if (list_start) list_start->prev = node;
187 node->next = list_start;
188 node->prev = 0;
189 list->next = node;
192 /* Remove NODE from linked LIST. */
193 static void
194 xg_list_remove (xg_list_node *list, xg_list_node *node)
196 xg_list_node *list_start = list->next;
197 if (node == list_start)
199 list->next = node->next;
200 if (list->next) list->next->prev = 0;
202 else
204 node->prev->next = node->next;
205 if (node->next) node->next->prev = node->prev;
209 /* Allocate and return a utf8 version of STR. If STR is already
210 utf8 or NULL, just return STR.
211 If not, a new string is allocated and the caller must free the result
212 with g_free. */
213 static char *
214 get_utf8_string (str)
215 char *str;
217 char *utf8_str = str;
219 /* If not UTF-8, try current locale. */
220 if (str && !g_utf8_validate (str, -1, NULL))
221 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
223 return utf8_str;
228 /***********************************************************************
229 General functions for creating widgets, resizing, events, e.t.c.
230 ***********************************************************************/
232 /* Make a geometry string and pass that to GTK. It seems this is the
233 only way to get geometry position right if the user explicitly
234 asked for a position when starting Emacs.
235 F is the frame we shall set geometry for. */
236 static void
237 xg_set_geometry (f)
238 FRAME_PTR f;
240 if (f->size_hint_flags & USPosition)
242 int left = f->left_pos;
243 int xneg = f->size_hint_flags & XNegative;
244 int top = f->top_pos;
245 int yneg = f->size_hint_flags & YNegative;
246 char geom_str[32];
248 if (xneg)
249 left = -left;
250 if (yneg)
251 top = -top;
253 sprintf (geom_str, "=%dx%d%c%d%c%d",
254 FRAME_PIXEL_WIDTH (f),
255 FRAME_TOTAL_PIXEL_HEIGHT (f),
256 (xneg ? '-' : '+'), left,
257 (yneg ? '-' : '+'), top);
259 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
260 geom_str))
261 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
266 /* Resize the outer window of frame F after chainging the height.
267 This happend when the menu bar or the tool bar is added or removed.
268 COLUMNS/ROWS is the size the edit area shall have after the resize. */
269 static void
270 xg_resize_outer_widget (f, columns, rows)
271 FRAME_PTR f;
272 int columns;
273 int rows;
275 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
276 FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
278 /* base_height is now changed. */
279 x_wm_set_size_hint (f, 0, 0);
281 /* If we are not mapped yet, set geometry once again, as window
282 height now have changed. */
283 if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f)))
284 xg_set_geometry (f);
286 xg_frame_set_char_size (f, columns, rows);
287 gdk_window_process_all_updates ();
290 /* This gets called after the frame F has been cleared. Since that is
291 done with X calls, we need to redraw GTK widget (scroll bars). */
292 void
293 xg_frame_cleared (f)
294 FRAME_PTR f;
296 GtkWidget *w = f->output_data.x->widget;
298 if (w)
300 gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE);
301 gtk_container_foreach (GTK_CONTAINER (w),
302 (GtkCallback) gtk_widget_queue_draw,
304 gdk_window_process_all_updates ();
308 /* Function to handle resize of our widgets. Since Emacs has some layouts
309 that does not fit well with GTK standard containers, we do most layout
310 manually.
311 F is the frame to resize.
312 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
313 void
314 xg_resize_widgets (f, pixelwidth, pixelheight)
315 FRAME_PTR f;
316 int pixelwidth, pixelheight;
318 int mbheight = FRAME_MENUBAR_HEIGHT (f);
319 int tbheight = FRAME_TOOLBAR_HEIGHT (f);
320 int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
321 - mbheight - tbheight));
322 int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
324 if (FRAME_GTK_WIDGET (f)
325 && (columns != FRAME_COLS (f) || rows != FRAME_LINES (f)
326 || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f)))
328 struct x_output *x = f->output_data.x;
329 GtkAllocation all;
331 all.y = mbheight + tbheight;
332 all.x = 0;
334 all.width = pixelwidth;
335 all.height = pixelheight - mbheight - tbheight;
337 gtk_widget_size_allocate (x->edit_widget, &all);
339 change_frame_size (f, rows, columns, 0, 1, 0);
340 SET_FRAME_GARBAGED (f);
341 cancel_mouse_face (f);
346 /* Update our widget size to be COLS/ROWS characters for frame F. */
347 void
348 xg_frame_set_char_size (f, cols, rows)
349 FRAME_PTR f;
350 int cols;
351 int rows;
353 int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
354 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
355 int pixelwidth;
357 /* Take into account the size of the scroll bar. Always use the
358 number of columns occupied by the scroll bar here otherwise we
359 might end up with a frame width that is not a multiple of the
360 frame's character width which is bad for vertically split
361 windows. */
362 f->scroll_bar_actual_width
363 = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
365 compute_fringe_widths (f, 0);
367 /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it
368 after calculating that value. */
369 pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
371 /* Must resize our top level widget. Font size may have changed,
372 but not rows/cols. */
373 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
374 pixelwidth, pixelheight);
375 xg_resize_widgets (f, pixelwidth, pixelheight);
377 SET_FRAME_GARBAGED (f);
378 cancel_mouse_face (f);
381 /* Convert an X Window WSESC to its corresponding GtkWidget.
382 Must be done like this, because GtkWidget:s can have "hidden"
383 X Window that aren't accessible.
385 Return 0 if no widget match WDESC. */
386 GtkWidget *
387 xg_win_to_widget (wdesc)
388 Window wdesc;
390 gpointer gdkwin;
391 GtkWidget *gwdesc = 0;
393 BLOCK_INPUT;
394 gdkwin = gdk_xid_table_lookup (wdesc);
395 if (gdkwin)
397 GdkEvent event;
398 event.any.window = gdkwin;
399 gwdesc = gtk_get_event_widget (&event);
402 UNBLOCK_INPUT;
403 return gwdesc;
406 /* Fill in the GdkColor C so that it represents PIXEL.
407 W is the widget that color will be used for. Used to find colormap. */
408 static void
409 xg_pix_to_gcolor (w, pixel, c)
410 GtkWidget *w;
411 unsigned long pixel;
412 GdkColor *c;
414 GdkColormap *map = gtk_widget_get_colormap (w);
415 gdk_colormap_query_color (map, pixel, c);
418 /* Turning off double buffering for our GtkFixed widget has the side
419 effect of turning it off also for its children (scroll bars).
420 But we want those to be double buffered to not flicker so handle
421 expose manually here.
422 WIDGET is the GtkFixed widget that gets exposed.
423 EVENT is the expose event.
424 USER_DATA is unused.
426 Return TRUE to tell GTK that this expose event has been fully handeled
427 and that GTK shall do nothing more with it. */
428 static gboolean
429 xg_fixed_handle_expose(GtkWidget *widget,
430 GdkEventExpose *event,
431 gpointer user_data)
433 GList *iter;
435 for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter))
437 GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
438 GtkWidget *child = child_data->widget;
439 GdkWindow *window = child->window;
440 GdkRegion *region = gtk_widget_region_intersect (child, event->region);
442 if (! gdk_region_empty (region))
444 GdkEvent child_event;
445 child_event.expose = *event;
446 child_event.expose.region = region;
448 /* Turn on double buffering, i.e. draw to an off screen area. */
449 gdk_window_begin_paint_region (window, region);
451 /* Tell child to redraw itself. */
452 gdk_region_get_clipbox (region, &child_event.expose.area);
453 gtk_widget_send_expose (child, &child_event);
454 gdk_window_process_updates (window, TRUE);
456 /* Copy off screen area to the window. */
457 gdk_window_end_paint (window);
460 gdk_region_destroy (region);
463 return TRUE;
466 /* Create and set up the GTK widgets for frame F.
467 Return 0 if creation failed, non-zero otherwise. */
469 xg_create_frame_widgets (f)
470 FRAME_PTR f;
472 GtkWidget *wtop;
473 GtkWidget *wvbox;
474 GtkWidget *wfixed;
475 GdkColor bg;
476 GtkRcStyle *style;
477 int i;
478 char *title = 0;
480 BLOCK_INPUT;
482 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
483 wvbox = gtk_vbox_new (FALSE, 0);
484 wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */
486 if (! wtop || ! wvbox || ! wfixed)
488 if (wtop) gtk_widget_destroy (wtop);
489 if (wvbox) gtk_widget_destroy (wvbox);
490 if (wfixed) gtk_widget_destroy (wfixed);
492 return 0;
495 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
496 gtk_widget_set_name (wtop, EMACS_CLASS);
497 gtk_widget_set_name (wvbox, "pane");
498 gtk_widget_set_name (wfixed, SDATA (Vx_resource_name));
500 /* If this frame has a title or name, set it in the title bar. */
501 if (! NILP (f->title)) title = SDATA (f->title);
502 else if (! NILP (f->name)) title = SDATA (f->name);
504 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
506 FRAME_GTK_OUTER_WIDGET (f) = wtop;
507 FRAME_GTK_WIDGET (f) = wfixed;
508 f->output_data.x->vbox_widget = wvbox;
510 gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
512 gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
514 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
515 gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
517 if (FRAME_EXTERNAL_TOOL_BAR (f))
518 update_frame_tool_bar (f);
520 /* The tool bar is created but first there are no items in it.
521 This causes it to be zero height. Later items are added, but then
522 the frame is already mapped, so there is a "jumping" resize.
523 This makes geometry handling difficult, for example -0-0 will end
524 up in the wrong place as tool bar height has not been taken into account.
525 So we cheat a bit by setting a height that is what it will have
526 later on when tool bar items are added. */
527 if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0)
528 FRAME_TOOLBAR_HEIGHT (f) = 34;
531 /* We don't want this widget double buffered, because we draw on it
532 with regular X drawing primitives, so from a GTK/GDK point of
533 view, the widget is totally blank. When an expose comes, this
534 will make the widget blank, and then Emacs redraws it. This flickers
535 a lot, so we turn off double buffering. */
536 gtk_widget_set_double_buffered (wfixed, FALSE);
538 /* Turning off double buffering above has the side effect of turning
539 it off also for its children (scroll bars). But we want those
540 to be double buffered to not flicker so handle expose manually. */
541 g_signal_connect (G_OBJECT (wfixed), "expose-event",
542 G_CALLBACK (xg_fixed_handle_expose), 0);
544 /* GTK documents says use gtk_window_set_resizable. But then a user
545 can't shrink the window from its starting size. */
546 gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
547 gtk_window_set_wmclass (GTK_WINDOW (wtop),
548 SDATA (Vx_resource_name),
549 SDATA (Vx_resource_class));
551 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
552 GTK is to destroy the widget. We want Emacs to do that instead. */
553 g_signal_connect (G_OBJECT (wtop), "delete-event",
554 G_CALLBACK (gtk_true), 0);
556 /* Convert our geometry parameters into a geometry string
557 and specify it.
558 GTK will itself handle calculating the real position this way. */
559 xg_set_geometry (f);
561 gtk_widget_add_events (wfixed,
562 GDK_POINTER_MOTION_MASK
563 | GDK_EXPOSURE_MASK
564 | GDK_BUTTON_PRESS_MASK
565 | GDK_BUTTON_RELEASE_MASK
566 | GDK_KEY_PRESS_MASK
567 | GDK_ENTER_NOTIFY_MASK
568 | GDK_LEAVE_NOTIFY_MASK
569 | GDK_FOCUS_CHANGE_MASK
570 | GDK_STRUCTURE_MASK
571 | GDK_VISIBILITY_NOTIFY_MASK);
573 /* Must realize the windows so the X window gets created. It is used
574 by callers of this function. */
575 gtk_widget_realize (wfixed);
576 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
578 /* Since GTK clears its window by filling with the background color,
579 we must keep X and GTK background in sync. */
580 xg_pix_to_gcolor (wfixed, f->output_data.x->background_pixel, &bg);
581 gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
583 /* Also, do not let any background pixmap to be set, this looks very
584 bad as Emacs overwrites the background pixmap with its own idea
585 of background color. */
586 style = gtk_widget_get_modifier_style (wfixed);
588 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
589 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
590 gtk_widget_modify_style (wfixed, style);
592 /* GTK does not set any border, and they look bad with GTK. */
593 f->border_width = 0;
594 f->internal_border_width = 0;
596 UNBLOCK_INPUT;
598 return 1;
601 /* Set the normal size hints for the window manager, for frame F.
602 FLAGS is the flags word to use--or 0 meaning preserve the flags
603 that the window now has.
604 If USER_POSITION is nonzero, we set the User Position
605 flag (this is useful when FLAGS is 0). */
606 void
607 x_wm_set_size_hint (f, flags, user_position)
608 FRAME_PTR f;
609 long flags;
610 int user_position;
612 if (FRAME_GTK_OUTER_WIDGET (f))
614 /* Must use GTK routines here, otherwise GTK resets the size hints
615 to its own defaults. */
616 GdkGeometry size_hints;
617 gint hint_flags = 0;
618 int base_width, base_height;
619 int min_rows = 0, min_cols = 0;
620 int win_gravity = f->win_gravity;
622 if (flags)
624 memset (&size_hints, 0, sizeof (size_hints));
625 f->output_data.x->size_hints = size_hints;
626 f->output_data.x->hint_flags = hint_flags;
628 else
629 flags = f->size_hint_flags;
631 size_hints = f->output_data.x->size_hints;
632 hint_flags = f->output_data.x->hint_flags;
634 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
635 size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
636 size_hints.height_inc = FRAME_LINE_HEIGHT (f);
638 hint_flags |= GDK_HINT_BASE_SIZE;
639 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
640 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
641 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
643 check_frame_size (f, &min_rows, &min_cols);
645 size_hints.base_width = base_width;
646 size_hints.base_height = base_height;
647 size_hints.min_width = base_width + min_cols * size_hints.width_inc;
648 size_hints.min_height = base_height + min_rows * size_hints.height_inc;
651 /* These currently have a one to one mapping with the X values, but I
652 don't think we should rely on that. */
653 hint_flags |= GDK_HINT_WIN_GRAVITY;
654 size_hints.win_gravity = 0;
655 if (win_gravity == NorthWestGravity)
656 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
657 else if (win_gravity == NorthGravity)
658 size_hints.win_gravity = GDK_GRAVITY_NORTH;
659 else if (win_gravity == NorthEastGravity)
660 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
661 else if (win_gravity == WestGravity)
662 size_hints.win_gravity = GDK_GRAVITY_WEST;
663 else if (win_gravity == CenterGravity)
664 size_hints.win_gravity = GDK_GRAVITY_CENTER;
665 else if (win_gravity == EastGravity)
666 size_hints.win_gravity = GDK_GRAVITY_EAST;
667 else if (win_gravity == SouthWestGravity)
668 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
669 else if (win_gravity == SouthGravity)
670 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
671 else if (win_gravity == SouthEastGravity)
672 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
673 else if (win_gravity == StaticGravity)
674 size_hints.win_gravity = GDK_GRAVITY_STATIC;
676 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
677 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
678 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
680 if (user_position)
682 hint_flags &= ~GDK_HINT_POS;
683 hint_flags |= GDK_HINT_USER_POS;
686 BLOCK_INPUT;
688 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
689 FRAME_GTK_OUTER_WIDGET (f),
690 &size_hints,
691 hint_flags);
693 f->output_data.x->size_hints = size_hints;
694 f->output_data.x->hint_flags = hint_flags;
695 UNBLOCK_INPUT;
699 /* Change background color of a frame.
700 Since GTK uses the background colour to clear the window, we must
701 keep the GTK and X colors in sync.
702 F is the frame to change,
703 BG is the pixel value to change to. */
704 void
705 xg_set_background_color (f, bg)
706 FRAME_PTR f;
707 unsigned long bg;
709 if (FRAME_GTK_WIDGET (f))
711 GdkColor gdk_bg;
713 BLOCK_INPUT;
714 xg_pix_to_gcolor (FRAME_GTK_WIDGET (f), bg, &gdk_bg);
715 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &gdk_bg);
716 UNBLOCK_INPUT;
722 /***********************************************************************
723 Dialog functions
724 ***********************************************************************/
725 /* Return the dialog title to use for a dialog of type KEY.
726 This is the encoding used by lwlib. We use the same for GTK. */
727 static char *
728 get_dialog_title (char key)
730 char *title = "";
732 switch (key) {
733 case 'E': case 'e':
734 title = "Error";
735 break;
737 case 'I': case 'i':
738 title = "Information";
739 break;
741 case 'L': case 'l':
742 title = "Prompt";
743 break;
745 case 'P': case 'p':
746 title = "Prompt";
747 break;
749 case 'Q': case 'q':
750 title = "Question";
751 break;
754 return title;
757 /* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
758 the dialog, but return TRUE so the event does not propagate further
759 in GTK. This prevents GTK from destroying the dialog widget automatically
760 and we can always destrou the widget manually, regardles of how
761 it was popped down (button press or WM_DELETE_WINDOW).
762 W is the dialog widget.
763 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
764 user_data is NULL (not used).
766 Returns TRUE to end propagation of event. */
767 static gboolean
768 dialog_delete_callback (w, event, user_data)
769 GtkWidget *w;
770 GdkEvent *event;
771 gpointer user_data;
773 gtk_widget_unmap (w);
774 return TRUE;
777 /* Create a popup dialog window. See also xg_create_widget below.
778 WV is a widget_value describing the dialog.
779 SELECT_CB is the callback to use when a button has been pressed.
780 DEACTIVATE_CB is the callback to use when the dialog pops down.
782 Returns the GTK dialog widget. */
783 static GtkWidget *
784 create_dialog (wv, select_cb, deactivate_cb)
785 widget_value *wv;
786 GCallback select_cb;
787 GCallback deactivate_cb;
789 char *title = get_dialog_title (wv->name[0]);
790 int total_buttons = wv->name[1] - '0';
791 int right_buttons = wv->name[4] - '0';
792 int left_buttons;
793 int button_nr = 0;
794 int button_spacing = 10;
795 GtkWidget *wdialog = gtk_dialog_new ();
796 widget_value *item;
797 GtkBox *cur_box;
798 GtkWidget *wvbox;
799 GtkWidget *whbox_up;
800 GtkWidget *whbox_down;
802 /* If the number of buttons is greater than 4, make two rows of buttons
803 instead. This looks better. */
804 int make_two_rows = total_buttons > 4;
806 if (right_buttons == 0) right_buttons = total_buttons/2;
807 left_buttons = total_buttons - right_buttons;
809 gtk_window_set_title (GTK_WINDOW (wdialog), title);
810 gtk_widget_set_name (wdialog, "emacs-dialog");
812 cur_box = GTK_BOX (GTK_DIALOG (wdialog)->action_area);
814 if (make_two_rows)
816 wvbox = gtk_vbox_new (TRUE, button_spacing);
817 whbox_up = gtk_hbox_new (FALSE, 0);
818 whbox_down = gtk_hbox_new (FALSE, 0);
820 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
821 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
822 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
824 cur_box = GTK_BOX (whbox_up);
827 g_signal_connect (G_OBJECT (wdialog), "delete-event",
828 G_CALLBACK (dialog_delete_callback), 0);
830 if (deactivate_cb)
832 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
833 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
836 for (item = wv->contents; item; item = item->next)
838 char *utf8_label = get_utf8_string (item->value);
839 GtkWidget *w;
840 GtkRequisition req;
842 if (item->name && strcmp (item->name, "message") == 0)
844 /* This is the text part of the dialog. */
845 w = gtk_label_new (utf8_label);
846 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
847 gtk_label_new (""),
848 FALSE, FALSE, 0);
849 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w,
850 TRUE, TRUE, 0);
851 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
853 /* Try to make dialog look better. Must realize first so
854 the widget can calculate the size it needs. */
855 gtk_widget_realize (w);
856 gtk_widget_size_request (w, &req);
857 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
858 req.height);
859 if (item->value && strlen (item->value) > 0)
860 button_spacing = 2*req.width/strlen (item->value);
862 else
864 /* This is one button to add to the dialog. */
865 w = gtk_button_new_with_label (utf8_label);
866 if (! item->enabled)
867 gtk_widget_set_sensitive (w, FALSE);
868 if (select_cb)
869 g_signal_connect (G_OBJECT (w), "clicked",
870 select_cb, item->call_data);
872 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
873 if (++button_nr == left_buttons)
875 if (make_two_rows)
876 cur_box = GTK_BOX (whbox_down);
877 else
878 gtk_box_pack_start (cur_box,
879 gtk_label_new (""),
880 TRUE, TRUE,
881 button_spacing);
885 if (utf8_label && utf8_label != item->value)
886 g_free (utf8_label);
889 return wdialog;
893 enum
895 XG_FILE_NOT_DONE,
896 XG_FILE_OK,
897 XG_FILE_CANCEL,
898 XG_FILE_DESTROYED,
901 /* Callback function invoked when the Ok button is pressed in
902 a file dialog.
903 W is the file dialog widget,
904 ARG points to an integer where we record what has happend. */
905 static void
906 xg_file_sel_ok (w, arg)
907 GtkWidget *w;
908 gpointer arg;
910 *(int*)arg = XG_FILE_OK;
913 /* Callback function invoked when the Cancel button is pressed in
914 a file dialog.
915 W is the file dialog widget,
916 ARG points to an integer where we record what has happend. */
917 static void
918 xg_file_sel_cancel (w, arg)
919 GtkWidget *w;
920 gpointer arg;
922 *(int*)arg = XG_FILE_CANCEL;
925 /* Callback function invoked when the file dialog is destroyed (i.e.
926 popped down). We must keep track of this, because if this
927 happens, GTK destroys the widget. But if for example, Ok is pressed,
928 the dialog is popped down, but the dialog widget is not destroyed.
929 W is the file dialog widget,
930 ARG points to an integer where we record what has happend. */
931 static void
932 xg_file_sel_destroy (w, arg)
933 GtkWidget *w;
934 gpointer arg;
936 *(int*)arg = XG_FILE_DESTROYED;
939 /* Read a file name from the user using a file dialog.
940 F is the current frame.
941 PROMPT is a prompt to show to the user. May not be NULL.
942 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
943 If MUSTMATCH_P is non-zero, the returned file name must be an existing
944 file.
946 Returns a file name or NULL if no file was selected.
947 The returned string must be freed by the caller. */
948 char *
949 xg_get_file_name (f, prompt, default_filename, mustmatch_p)
950 FRAME_PTR f;
951 char *prompt;
952 char *default_filename;
953 int mustmatch_p;
955 GtkWidget *filewin;
956 GtkFileSelection *filesel;
957 int filesel_done = XG_FILE_NOT_DONE;
958 char *fn = 0;
960 filewin = gtk_file_selection_new (prompt);
961 filesel = GTK_FILE_SELECTION (filewin);
963 gtk_widget_set_name (filewin, "emacs-filedialog");
965 gtk_window_set_transient_for (GTK_WINDOW (filewin),
966 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
967 gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
969 g_signal_connect (G_OBJECT (filesel->ok_button),
970 "clicked",
971 G_CALLBACK (xg_file_sel_ok),
972 &filesel_done);
973 g_signal_connect (G_OBJECT (filesel->cancel_button),
974 "clicked",
975 G_CALLBACK (xg_file_sel_cancel),
976 &filesel_done);
977 g_signal_connect (G_OBJECT (filesel),
978 "destroy",
979 G_CALLBACK (xg_file_sel_destroy),
980 &filesel_done);
982 if (default_filename)
983 gtk_file_selection_set_filename (filesel, default_filename);
985 if (mustmatch_p)
987 /* The selection_entry part of filesel is not documented. */
988 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
989 gtk_file_selection_hide_fileop_buttons (filesel);
993 gtk_widget_show_all (filewin);
995 while (filesel_done == XG_FILE_NOT_DONE)
996 gtk_main_iteration ();
998 if (filesel_done == XG_FILE_OK)
999 fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
1001 if (filesel_done != XG_FILE_DESTROYED)
1002 gtk_widget_destroy (filewin);
1004 return fn;
1008 /***********************************************************************
1009 Menu functions.
1010 ***********************************************************************/
1012 /* The name of menu items that can be used for citomization. Since GTK
1013 RC files are very crude and primitive, we have to set this on all
1014 menu item names so a user can easily cutomize menu items. */
1016 #define MENU_ITEM_NAME "emacs-menuitem"
1019 /* Linked list of all allocated struct xg_menu_cb_data. Used for marking
1020 during GC. The next member points to the items. */
1021 static xg_list_node xg_menu_cb_list;
1023 /* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
1024 during GC. The next member points to the items. */
1025 static xg_list_node xg_menu_item_cb_list;
1027 /* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
1028 F is the frame CL_DATA will be initialized for.
1029 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1031 The menu bar and all sub menus under the menu bar in a frame
1032 share the same structure, hence the reference count.
1034 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
1035 allocated xg_menu_cb_data if CL_DATA is NULL. */
1036 static xg_menu_cb_data *
1037 make_cl_data (cl_data, f, highlight_cb)
1038 xg_menu_cb_data *cl_data;
1039 FRAME_PTR f;
1040 GCallback highlight_cb;
1042 if (! cl_data)
1044 cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
1045 cl_data->f = f;
1046 cl_data->menu_bar_vector = f->menu_bar_vector;
1047 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1048 cl_data->highlight_cb = highlight_cb;
1049 cl_data->ref_count = 0;
1051 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
1054 cl_data->ref_count++;
1056 return cl_data;
1059 /* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
1060 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1062 When the menu bar is updated, menu items may have been added and/or
1063 removed, so menu_bar_vector and menu_bar_items_used change. We must
1064 then update CL_DATA since it is used to determine which menu
1065 item that is invoked in the menu.
1066 HIGHLIGHT_CB could change, there is no check that the same
1067 function is given when modifying a menu bar as was given when
1068 creating the menu bar. */
1069 static void
1070 update_cl_data (cl_data, f, highlight_cb)
1071 xg_menu_cb_data *cl_data;
1072 FRAME_PTR f;
1073 GCallback highlight_cb;
1075 if (cl_data)
1077 cl_data->f = f;
1078 cl_data->menu_bar_vector = f->menu_bar_vector;
1079 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1080 cl_data->highlight_cb = highlight_cb;
1084 /* Decrease reference count for CL_DATA.
1085 If reference count is zero, free CL_DATA. */
1086 static void
1087 unref_cl_data (cl_data)
1088 xg_menu_cb_data *cl_data;
1090 if (cl_data && cl_data->ref_count > 0)
1092 cl_data->ref_count--;
1093 if (cl_data->ref_count == 0)
1095 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
1096 xfree (cl_data);
1101 /* Function that marks all lisp data during GC. */
1102 void
1103 xg_mark_data ()
1105 xg_list_node *iter;
1107 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
1108 mark_object (&((xg_menu_cb_data *) iter)->menu_bar_vector);
1110 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
1112 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
1114 if (! NILP (cb_data->help))
1115 mark_object (&cb_data->help);
1120 /* Callback called when a menu item is destroyed. Used to free data.
1121 W is the widget that is being destroyed (not used).
1122 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
1123 static void
1124 menuitem_destroy_callback (w, client_data)
1125 GtkWidget *w;
1126 gpointer client_data;
1128 if (client_data)
1130 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1131 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
1132 xfree (data);
1136 /* Callback called when the pointer enters/leaves a menu item.
1137 W is the menu item.
1138 EVENT is either an enter event or leave event.
1139 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
1141 Returns FALSE to tell GTK to keep processing this event. */
1142 static gboolean
1143 menuitem_highlight_callback (w, event, client_data)
1144 GtkWidget *w;
1145 GdkEventCrossing *event;
1146 gpointer client_data;
1148 if (client_data)
1150 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1151 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
1153 if (! NILP (data->help) && data->cl_data->highlight_cb)
1155 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
1156 (*func) (w, call_data);
1160 return FALSE;
1163 /* Callback called when a menu is destroyed. Used to free data.
1164 W is the widget that is being destroyed (not used).
1165 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
1166 static void
1167 menu_destroy_callback (w, client_data)
1168 GtkWidget *w;
1169 gpointer client_data;
1171 unref_cl_data ((xg_menu_cb_data*) client_data);
1174 /* Callback called when a menu does a grab or ungrab. That means the
1175 menu has been activated or deactivated.
1176 Used to start a timer so the small timeout the menus in GTK uses before
1177 popping down a menu is seen by Emacs (see xg_process_timeouts above).
1178 W is the widget that does the grab (not used).
1179 UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
1180 CLIENT_DATA is NULL (not used). */
1181 static void
1182 menu_grab_callback (GtkWidget *widget,
1183 gboolean ungrab_p,
1184 gpointer client_data)
1186 /* Keep track of total number of grabs. */
1187 static int cnt;
1189 if (ungrab_p) cnt--;
1190 else cnt++;
1192 if (cnt > 0 && ! xg_timer) xg_start_timer ();
1193 else if (cnt == 0 && xg_timer) xg_stop_timer ();
1196 /* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
1197 must be non-NULL) and can be inserted into a menu item.
1199 Returns the GtkHBox. */
1200 static GtkWidget *
1201 make_widget_for_menu_item (utf8_label, utf8_key)
1202 char *utf8_label;
1203 char *utf8_key;
1205 GtkWidget *wlbl;
1206 GtkWidget *wkey;
1207 GtkWidget *wbox;
1209 wbox = gtk_hbox_new (FALSE, 0);
1210 wlbl = gtk_label_new (utf8_label);
1211 wkey = gtk_label_new (utf8_key);
1213 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
1214 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
1216 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
1217 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
1219 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
1220 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
1221 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
1223 return wbox;
1226 /* Make and return a menu item widget with the key to the right.
1227 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
1228 UTF8_KEY is the text representing the key binding.
1229 ITEM is the widget_value describing the menu item.
1231 GROUP is an in/out parameter. If the menu item to be created is not
1232 part of any radio menu group, *GROUP contains NULL on entry and exit.
1233 If the menu item to be created is part of a radio menu group, on entry
1234 *GROUP contains the group to use, or NULL if this is the first item
1235 in the group. On exit, *GROUP contains the radio item group.
1237 Unfortunately, keys don't line up as nicely as in Motif,
1238 but the MacOS X version doesn't either, so I guess that is OK. */
1239 static GtkWidget *
1240 make_menu_item (utf8_label, utf8_key, item, group)
1241 char *utf8_label;
1242 char *utf8_key;
1243 widget_value *item;
1244 GSList **group;
1246 GtkWidget *w;
1247 GtkWidget *wtoadd = 0;
1249 if (utf8_key)
1250 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
1252 if (item->button_type == BUTTON_TYPE_TOGGLE)
1254 *group = NULL;
1255 if (utf8_key) w = gtk_check_menu_item_new ();
1256 else w = gtk_check_menu_item_new_with_label (utf8_label);
1257 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
1259 else if (item->button_type == BUTTON_TYPE_RADIO)
1261 if (utf8_key) w = gtk_radio_menu_item_new (*group);
1262 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
1263 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
1264 if (item->selected)
1265 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1267 else
1269 *group = NULL;
1270 if (utf8_key) w = gtk_menu_item_new ();
1271 else w = gtk_menu_item_new_with_label (utf8_label);
1274 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
1275 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
1277 return w;
1280 /* Return non-zero if LABEL specifies a separator (GTK only has one
1281 separator type) */
1282 static int
1283 xg_separator_p (char *label)
1285 if (! label) return 0;
1286 else if (strlen (label) > 3
1287 && strncmp (label, "--", 2) == 0
1288 && label[2] != '-')
1290 static char* separator_names[] = {
1291 "space",
1292 "no-line",
1293 "single-line",
1294 "double-line",
1295 "single-dashed-line",
1296 "double-dashed-line",
1297 "shadow-etched-in",
1298 "shadow-etched-out",
1299 "shadow-etched-in-dash",
1300 "shadow-etched-out-dash",
1301 "shadow-double-etched-in",
1302 "shadow-double-etched-out",
1303 "shadow-double-etched-in-dash",
1304 "shadow-double-etched-out-dash",
1308 int i;
1310 label += 2;
1311 for (i = 0; separator_names[i]; ++i)
1312 if (strcmp (label, separator_names[i]) == 0)
1313 return 1;
1315 else
1317 /* Old-style separator, maybe. It's a separator if it contains
1318 only dashes. */
1319 while (*label == '-')
1320 ++label;
1321 if (*label == 0) return 1;
1324 return 0;
1327 GtkWidget *xg_did_tearoff;
1329 /* Callback invoked when a detached menu window is removed. Here we
1330 delete the popup menu.
1331 WIDGET is the top level window that is removed (the parent of the menu).
1332 EVENT is the event that triggers the window removal.
1333 CLIENT_DATA points to the menu that is detached.
1335 Returns TRUE to tell GTK to stop processing this event. */
1336 static gboolean
1337 tearoff_remove (widget, event, client_data)
1338 GtkWidget *widget;
1339 GdkEvent *event;
1340 gpointer client_data;
1342 gtk_widget_destroy (GTK_WIDGET (client_data));
1343 return TRUE;
1346 /* Callback invoked when a menu is detached. It sets the xg_did_tearoff
1347 variable.
1348 WIDGET is the GtkTearoffMenuItem.
1349 CLIENT_DATA is not used. */
1350 static void
1351 tearoff_activate (widget, client_data)
1352 GtkWidget *widget;
1353 gpointer client_data;
1355 GtkWidget *menu = gtk_widget_get_parent (widget);
1356 if (! gtk_menu_get_tearoff_state (GTK_MENU (menu)))
1357 return;
1359 xg_did_tearoff = menu;
1362 /* If a detach of a popup menu is done, this function should be called
1363 to keep the menu around until the detached window is removed.
1364 MENU is the top level menu for the popup,
1365 SUBMENU is the menu that got detached (that is MENU or a
1366 submenu of MENU), see the xg_did_tearoff variable. */
1367 void
1368 xg_keep_popup (menu, submenu)
1369 GtkWidget *menu;
1370 GtkWidget *submenu;
1372 GtkWidget *p;
1374 /* Find the top widget for the detached menu. */
1375 p = gtk_widget_get_toplevel (submenu);
1377 /* Delay destroying the menu until the detached menu is removed. */
1378 g_signal_connect (G_OBJECT (p), "unmap_event",
1379 G_CALLBACK (tearoff_remove), menu);
1382 /* Create a menu item widget, and connect the callbacks.
1383 ITEM decribes the menu item.
1384 F is the frame the created menu belongs to.
1385 SELECT_CB is the callback to use when a menu item is selected.
1386 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1387 CL_DATA points to the callback data to be used for this menu.
1388 GROUP is an in/out parameter. If the menu item to be created is not
1389 part of any radio menu group, *GROUP contains NULL on entry and exit.
1390 If the menu item to be created is part of a radio menu group, on entry
1391 *GROUP contains the group to use, or NULL if this is the first item
1392 in the group. On exit, *GROUP contains the radio item group.
1394 Returns the created GtkWidget. */
1395 static GtkWidget *
1396 xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
1397 widget_value *item;
1398 FRAME_PTR f;
1399 GCallback select_cb;
1400 GCallback highlight_cb;
1401 xg_menu_cb_data *cl_data;
1402 GSList **group;
1404 char *utf8_label;
1405 char *utf8_key;
1406 GtkWidget *w;
1407 xg_menu_item_cb_data *cb_data;
1409 utf8_label = get_utf8_string (item->name);
1410 utf8_key = get_utf8_string (item->key);
1412 w = make_menu_item (utf8_label, utf8_key, item, group);
1414 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1415 if (utf8_key && utf8_key != item->key) g_free (utf8_key);
1417 cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
1419 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
1421 cb_data->unhighlight_id = cb_data->highlight_id = cb_data->select_id = 0;
1422 cb_data->help = item->help;
1423 cb_data->cl_data = cl_data;
1424 cb_data->call_data = item->call_data;
1426 g_signal_connect (G_OBJECT (w),
1427 "destroy",
1428 G_CALLBACK (menuitem_destroy_callback),
1429 cb_data);
1431 /* Put cb_data in widget, so we can get at it when modifying menubar */
1432 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
1434 /* final item, not a submenu */
1435 if (item->call_data && ! item->contents)
1437 if (select_cb)
1438 cb_data->select_id
1439 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
1442 if (! NILP (item->help) && highlight_cb)
1444 /* We use enter/leave notify instead of select/deselect because
1445 select/deselect doesn't go well with detached menus. */
1446 cb_data->highlight_id
1447 = g_signal_connect (G_OBJECT (w),
1448 "enter-notify-event",
1449 G_CALLBACK (menuitem_highlight_callback),
1450 cb_data);
1451 cb_data->unhighlight_id
1452 = g_signal_connect (G_OBJECT (w),
1453 "leave-notify-event",
1454 G_CALLBACK (menuitem_highlight_callback),
1455 cb_data);
1458 return w;
1461 static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
1462 GCallback, GCallback, int, int, int,
1463 GtkWidget *, xg_menu_cb_data *, char *));
1465 /* Create a full menu tree specified by DATA.
1466 F is the frame the created menu belongs to.
1467 SELECT_CB is the callback to use when a menu item is selected.
1468 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
1469 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1470 POP_UP_P is non-zero if we shall create a popup menu.
1471 MENU_BAR_P is non-zero if we shall create a menu bar.
1472 ADD_TEAROFF_P is non-zero if we shall add a teroff menu item. Ignored
1473 if MENU_BAR_P is non-zero.
1474 TOPMENU is the topmost GtkWidget that others shall be placed under.
1475 It may be NULL, in that case we create the appropriate widget
1476 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
1477 CL_DATA is the callback data we shall use for this menu, or NULL
1478 if we haven't set the first callback yet.
1479 NAME is the name to give to the top level menu if this function
1480 creates it. May be NULL to not set any name.
1482 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
1483 not NULL.
1485 This function calls itself to create submenus. */
1487 static GtkWidget *
1488 create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
1489 pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name)
1490 widget_value *data;
1491 FRAME_PTR f;
1492 GCallback select_cb;
1493 GCallback deactivate_cb;
1494 GCallback highlight_cb;
1495 int pop_up_p;
1496 int menu_bar_p;
1497 int add_tearoff_p;
1498 GtkWidget *topmenu;
1499 xg_menu_cb_data *cl_data;
1500 char *name;
1502 widget_value *item;
1503 GtkWidget *wmenu = topmenu;
1504 GSList *group = NULL;
1506 if (! topmenu)
1508 if (! menu_bar_p) wmenu = gtk_menu_new ();
1509 else wmenu = gtk_menu_bar_new ();
1511 /* Put cl_data on the top menu for easier access. */
1512 cl_data = make_cl_data (cl_data, f, highlight_cb);
1513 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
1514 g_signal_connect (G_OBJECT (wmenu), "destroy",
1515 G_CALLBACK (menu_destroy_callback), cl_data);
1517 if (name)
1518 gtk_widget_set_name (wmenu, name);
1520 if (deactivate_cb)
1521 g_signal_connect (G_OBJECT (wmenu),
1522 "deactivate", deactivate_cb, 0);
1524 g_signal_connect (G_OBJECT (wmenu),
1525 "grab-notify", G_CALLBACK (menu_grab_callback), 0);
1528 if (! menu_bar_p && add_tearoff_p)
1530 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
1531 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
1533 g_signal_connect (G_OBJECT (tearoff), "activate",
1534 G_CALLBACK (tearoff_activate), 0);
1537 for (item = data; item; item = item->next)
1539 GtkWidget *w;
1541 if (pop_up_p && !item->contents && !item->call_data
1542 && !xg_separator_p (item->name))
1544 char *utf8_label;
1545 /* A title for a popup. We do the same as GTK does when
1546 creating titles, but it does not look good. */
1547 group = NULL;
1548 utf8_label = get_utf8_string (item->name);
1550 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
1551 w = gtk_menu_item_new_with_label (utf8_label);
1552 gtk_widget_set_sensitive (w, FALSE);
1553 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1555 else if (xg_separator_p (item->name))
1557 group = NULL;
1558 /* GTK only have one separator type. */
1559 w = gtk_separator_menu_item_new ();
1561 else
1563 w = xg_create_one_menuitem (item,
1565 item->contents ? 0 : select_cb,
1566 highlight_cb,
1567 cl_data,
1568 &group);
1570 if (item->contents)
1572 GtkWidget *submenu = create_menus (item->contents,
1574 select_cb,
1575 deactivate_cb,
1576 highlight_cb,
1581 cl_data,
1583 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
1587 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
1588 gtk_widget_set_name (w, MENU_ITEM_NAME);
1591 return wmenu;
1594 /* Create a menubar, popup menu or dialog, depending on the TYPE argument.
1595 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
1596 with some text and buttons.
1597 F is the frame the created item belongs to.
1598 NAME is the name to use for the top widget.
1599 VAL is a widget_value structure describing items to be created.
1600 SELECT_CB is the callback to use when a menu item is selected or
1601 a dialog button is pressed.
1602 DEACTIVATE_CB is the callback to use when an item is deactivated.
1603 For a menu, when a sub menu is not shown anymore, for a dialog it is
1604 called when the dialog is popped down.
1605 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1607 Returns the widget created. */
1608 GtkWidget *
1609 xg_create_widget (type, name, f, val,
1610 select_cb, deactivate_cb, highlight_cb)
1611 char *type;
1612 char *name;
1613 FRAME_PTR f;
1614 widget_value *val;
1615 GCallback select_cb;
1616 GCallback deactivate_cb;
1617 GCallback highlight_cb;
1619 GtkWidget *w = 0;
1620 if (strcmp (type, "dialog") == 0)
1622 w = create_dialog (val, select_cb, deactivate_cb);
1623 gtk_window_set_transient_for (GTK_WINDOW (w),
1624 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1625 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1627 if (w)
1628 gtk_widget_set_name (w, "emacs-dialog");
1630 else if (strcmp (type, "menubar") == 0 || strcmp (type, "popup") == 0)
1632 w = create_menus (val->contents,
1634 select_cb,
1635 deactivate_cb,
1636 highlight_cb,
1637 strcmp (type, "popup") == 0,
1638 strcmp (type, "menubar") == 0,
1642 name);
1644 /* Set the cursor to an arrow for popup menus when they are mapped.
1645 This is done by default for menu bar menus. */
1646 if (strcmp (type, "popup") == 0)
1648 /* Must realize so the GdkWindow inside the widget is created. */
1649 gtk_widget_realize (w);
1650 xg_set_cursor (w, &xg_left_ptr_cursor);
1653 else
1655 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
1656 type);
1659 return w;
1662 /* Return the label for menu item WITEM. */
1663 static const char *
1664 xg_get_menu_item_label (witem)
1665 GtkMenuItem *witem;
1667 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
1668 return gtk_label_get_label (wlabel);
1671 /* Return non-zero if the menu item WITEM has the text LABEL. */
1672 static int
1673 xg_item_label_same_p (witem, label)
1674 GtkMenuItem *witem;
1675 char *label;
1677 int is_same = 0;
1678 char *utf8_label = get_utf8_string (label);
1679 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
1681 if (! old_label && ! utf8_label)
1682 is_same = 1;
1683 else if (old_label && utf8_label)
1684 is_same = strcmp (utf8_label, old_label) == 0;
1686 if (utf8_label && utf8_label != label) g_free (utf8_label);
1688 return is_same;
1691 /* Remove widgets in LIST from container WCONT. */
1692 static void
1693 remove_from_container (wcont, list)
1694 GtkWidget *wcont;
1695 GList *list;
1697 GList *iter;
1699 for (iter = list; iter; iter = g_list_next (iter))
1701 GtkWidget *w = GTK_WIDGET (iter->data);
1703 /* Add a ref to w so we can explicitly destroy it later. */
1704 gtk_widget_ref (w);
1705 gtk_container_remove (GTK_CONTAINER (wcont), w);
1707 /* If there is a menu under this widget that has been detached,
1708 there is a reference to it, and just removing w from the
1709 container does not destroy the submenu. By explicitly
1710 destroying w we make sure the submenu is destroyed, thus
1711 removing the detached window also if there was one. */
1712 gtk_widget_destroy (w);
1716 /* Update the top level names in MENUBAR (i.e. not submenus).
1717 F is the frame the menu bar belongs to.
1718 *LIST is a list with the current menu bar names (menu item widgets).
1719 ITER is the item within *LIST that shall be updated.
1720 POS is the numerical position, starting at 0, of ITER in *LIST.
1721 VAL describes what the menu bar shall look like after the update.
1722 SELECT_CB is the callback to use when a menu item is selected.
1723 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1724 CL_DATA points to the callback data to be used for this menu bar.
1726 This function calls itself to walk through the menu bar names. */
1727 static void
1728 xg_update_menubar (menubar, f, list, iter, pos, val,
1729 select_cb, highlight_cb, cl_data)
1730 GtkWidget *menubar;
1731 FRAME_PTR f;
1732 GList **list;
1733 GList *iter;
1734 int pos;
1735 widget_value *val;
1736 GCallback select_cb;
1737 GCallback highlight_cb;
1738 xg_menu_cb_data *cl_data;
1740 if (! iter && ! val)
1741 return;
1742 else if (iter && ! val)
1744 /* Item(s) have been removed. Remove all remaining items. */
1745 remove_from_container (menubar, iter);
1747 /* All updated. */
1748 val = 0;
1749 iter = 0;
1751 else if (! iter && val)
1753 /* Item(s) added. Add all new items in one call. */
1754 create_menus (val, f, select_cb, 0, highlight_cb,
1755 0, 1, 0, menubar, cl_data, 0);
1757 /* All updated. */
1758 val = 0;
1759 iter = 0;
1761 /* Below this neither iter or val is NULL */
1762 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
1764 /* This item is still the same, check next item. */
1765 val = val->next;
1766 iter = g_list_next (iter);
1767 ++pos;
1769 else /* This item is changed. */
1771 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
1772 GtkMenuItem *witem2 = 0;
1773 int val_in_menubar = 0;
1774 int iter_in_new_menubar = 0;
1775 GList *iter2;
1776 widget_value *cur;
1778 /* See if the changed entry (val) is present later in the menu bar */
1779 for (iter2 = iter;
1780 iter2 && ! val_in_menubar;
1781 iter2 = g_list_next (iter2))
1783 witem2 = GTK_MENU_ITEM (iter2->data);
1784 val_in_menubar = xg_item_label_same_p (witem2, val->name);
1787 /* See if the current entry (iter) is present later in the
1788 specification for the new menu bar. */
1789 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
1790 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
1792 if (val_in_menubar && ! iter_in_new_menubar)
1794 int nr = pos;
1796 /* This corresponds to:
1797 Current: A B C
1798 New: A C
1799 Remove B. */
1801 gtk_widget_ref (GTK_WIDGET (witem));
1802 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
1803 gtk_widget_destroy (GTK_WIDGET (witem));
1805 /* Must get new list since the old changed. */
1806 g_list_free (*list);
1807 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1808 while (nr-- > 0) iter = g_list_next (iter);
1810 else if (! val_in_menubar && ! iter_in_new_menubar)
1812 /* This corresponds to:
1813 Current: A B C
1814 New: A X C
1815 Rename B to X. This might seem to be a strange thing to do,
1816 since if there is a menu under B it will be totally wrong for X.
1817 But consider editing a C file. Then there is a C-mode menu
1818 (corresponds to B above).
1819 If then doing C-x C-f the minibuf menu (X above) replaces the
1820 C-mode menu. When returning from the minibuffer, we get
1821 back the C-mode menu. Thus we do:
1822 Rename B to X (C-mode to minibuf menu)
1823 Rename X to B (minibuf to C-mode menu).
1824 If the X menu hasn't been invoked, the menu under B
1825 is up to date when leaving the minibuffer. */
1826 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
1827 char *utf8_label = get_utf8_string (val->name);
1829 gtk_label_set_text (wlabel, utf8_label);
1831 iter = g_list_next (iter);
1832 val = val->next;
1833 ++pos;
1835 else if (! val_in_menubar && iter_in_new_menubar)
1837 /* This corresponds to:
1838 Current: A B C
1839 New: A X B C
1840 Insert X. */
1842 int nr = pos;
1843 GList *group = 0;
1844 GtkWidget *w = xg_create_one_menuitem (val,
1846 select_cb,
1847 highlight_cb,
1848 cl_data,
1849 &group);
1851 gtk_widget_set_name (w, MENU_ITEM_NAME);
1852 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
1854 g_list_free (*list);
1855 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1856 while (nr-- > 0) iter = g_list_next (iter);
1857 iter = g_list_next (iter);
1858 val = val->next;
1859 ++pos;
1861 else /* if (val_in_menubar && iter_in_new_menubar) */
1863 int nr = pos;
1864 /* This corresponds to:
1865 Current: A B C
1866 New: A C B
1867 Move C before B */
1869 gtk_widget_ref (GTK_WIDGET (witem2));
1870 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
1871 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
1872 GTK_WIDGET (witem2), pos);
1873 gtk_widget_unref (GTK_WIDGET (witem2));
1875 g_list_free (*list);
1876 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1877 while (nr-- > 0) iter = g_list_next (iter);
1878 val = val->next;
1879 ++pos;
1883 /* Update the rest of the menu bar. */
1884 xg_update_menubar (menubar, f, list, iter, pos, val,
1885 select_cb, highlight_cb, cl_data);
1888 /* Update the menu item W so it corresponds to VAL.
1889 SELECT_CB is the callback to use when a menu item is selected.
1890 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1891 CL_DATA is the data to set in the widget for menu invokation. */
1892 static void
1893 xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
1894 widget_value *val;
1895 GtkWidget *w;
1896 GCallback select_cb;
1897 GCallback highlight_cb;
1898 xg_menu_cb_data *cl_data;
1900 GtkWidget *wchild;
1901 GtkLabel *wlbl = 0;
1902 GtkLabel *wkey = 0;
1903 char *utf8_label;
1904 char *utf8_key;
1905 const char *old_label = 0;
1906 const char *old_key = 0;
1907 xg_menu_item_cb_data *cb_data;
1909 wchild = gtk_bin_get_child (GTK_BIN (w));
1910 utf8_label = get_utf8_string (val->name);
1911 utf8_key = get_utf8_string (val->key);
1913 /* See if W is a menu item with a key. See make_menu_item above. */
1914 if (GTK_IS_HBOX (wchild))
1916 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
1918 wlbl = GTK_LABEL (list->data);
1919 wkey = GTK_LABEL (list->next->data);
1920 g_list_free (list);
1922 if (! utf8_key)
1924 /* Remove the key and keep just the label. */
1925 gtk_widget_ref (GTK_WIDGET (wlbl));
1926 gtk_container_remove (GTK_CONTAINER (w), wchild);
1927 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
1928 wkey = 0;
1932 else /* Just a label. */
1934 wlbl = GTK_LABEL (wchild);
1936 /* Check if there is now a key. */
1937 if (utf8_key)
1939 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
1940 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
1942 wlbl = GTK_LABEL (list->data);
1943 wkey = GTK_LABEL (list->next->data);
1944 g_list_free (list);
1946 gtk_container_remove (GTK_CONTAINER (w), wchild);
1947 gtk_container_add (GTK_CONTAINER (w), wtoadd);
1952 if (wkey) old_key = gtk_label_get_label (wkey);
1953 if (wlbl) old_label = gtk_label_get_label (wlbl);
1955 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
1956 gtk_label_set_text (wkey, utf8_key);
1958 if (! old_label || strcmp (utf8_label, old_label) != 0)
1959 gtk_label_set_text (wlbl, utf8_label);
1961 if (utf8_key && utf8_key != val->key) g_free (utf8_key);
1962 if (utf8_label && utf8_label != val->name) g_free (utf8_label);
1964 if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
1965 gtk_widget_set_sensitive (w, FALSE);
1966 else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
1967 gtk_widget_set_sensitive (w, TRUE);
1969 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
1970 XG_ITEM_DATA);
1971 if (cb_data)
1973 cb_data->call_data = val->call_data;
1974 cb_data->help = val->help;
1975 cb_data->cl_data = cl_data;
1977 /* We assume the callback functions don't change. */
1978 if (val->call_data && ! val->contents)
1980 /* This item shall have a select callback. */
1981 if (! cb_data->select_id)
1982 cb_data->select_id
1983 = g_signal_connect (G_OBJECT (w), "activate",
1984 select_cb, cb_data);
1986 else if (cb_data->select_id)
1988 g_signal_handler_disconnect (w, cb_data->select_id);
1989 cb_data->select_id = 0;
1992 if (NILP (cb_data->help))
1994 /* Shall not have help. Remove if any existed previously. */
1995 if (cb_data->highlight_id)
1997 g_signal_handler_disconnect (G_OBJECT (w),
1998 cb_data->highlight_id);
1999 cb_data->highlight_id = 0;
2001 if (cb_data->unhighlight_id)
2003 g_signal_handler_disconnect (G_OBJECT (w),
2004 cb_data->unhighlight_id);
2005 cb_data->unhighlight_id = 0;
2008 else if (! cb_data->highlight_id && highlight_cb)
2010 /* Have help now, but didn't previously. Add callback. */
2011 cb_data->highlight_id
2012 = g_signal_connect (G_OBJECT (w),
2013 "enter-notify-event",
2014 G_CALLBACK (menuitem_highlight_callback),
2015 cb_data);
2016 cb_data->unhighlight_id
2017 = g_signal_connect (G_OBJECT (w),
2018 "leave-notify-event",
2019 G_CALLBACK (menuitem_highlight_callback),
2020 cb_data);
2025 /* Update the toggle menu item W so it corresponds to VAL. */
2026 static void
2027 xg_update_toggle_item (val, w)
2028 widget_value *val;
2029 GtkWidget *w;
2031 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2034 /* Update the radio menu item W so it corresponds to VAL. */
2035 static void
2036 xg_update_radio_item (val, w)
2037 widget_value *val;
2038 GtkWidget *w;
2040 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2043 /* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
2044 SUBMENU may be NULL, in that case a new menu is created.
2045 F is the frame the menu bar belongs to.
2046 VAL describes the contents of the menu bar.
2047 SELECT_CB is the callback to use when a menu item is selected.
2048 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2049 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2050 CL_DATA is the call back data to use for any newly created items.
2052 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
2053 was NULL. */
2055 static GtkWidget *
2056 xg_update_submenu (submenu, f, val,
2057 select_cb, deactivate_cb, highlight_cb, cl_data)
2058 GtkWidget *submenu;
2059 FRAME_PTR f;
2060 widget_value *val;
2061 GCallback select_cb;
2062 GCallback deactivate_cb;
2063 GCallback highlight_cb;
2064 xg_menu_cb_data *cl_data;
2066 GtkWidget *newsub = submenu;
2067 GList *list = 0;
2068 GList *iter;
2069 widget_value *cur;
2070 int has_tearoff_p = 0;
2071 GList *first_radio = 0;
2073 if (submenu)
2074 list = gtk_container_get_children (GTK_CONTAINER (submenu));
2076 for (cur = val, iter = list;
2077 cur && iter;
2078 iter = g_list_next (iter), cur = cur->next)
2080 GtkWidget *w = GTK_WIDGET (iter->data);
2082 /* Skip tearoff items, they have no counterpart in val. */
2083 if (GTK_IS_TEAROFF_MENU_ITEM (w))
2085 has_tearoff_p = 1;
2086 iter = g_list_next (iter);
2087 if (iter) w = GTK_WIDGET (iter->data);
2088 else break;
2091 /* Remember first radio button in a group. If we get a mismatch in
2092 a radio group we must rebuild the whole group so that the connections
2093 in GTK becomes correct. */
2094 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
2095 first_radio = iter;
2096 else if (cur->button_type != BUTTON_TYPE_RADIO
2097 && ! GTK_IS_RADIO_MENU_ITEM (w))
2098 first_radio = 0;
2100 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
2102 if (! xg_separator_p (cur->name))
2103 break;
2105 else if (GTK_IS_CHECK_MENU_ITEM (w))
2107 if (cur->button_type != BUTTON_TYPE_TOGGLE)
2108 break;
2109 xg_update_toggle_item (cur, w);
2110 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2112 else if (GTK_IS_RADIO_MENU_ITEM (w))
2114 if (cur->button_type != BUTTON_TYPE_RADIO)
2115 break;
2116 xg_update_radio_item (cur, w);
2117 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2119 else if (GTK_IS_MENU_ITEM (w))
2121 GtkMenuItem *witem = GTK_MENU_ITEM (w);
2122 GtkWidget *sub;
2124 if (cur->button_type != BUTTON_TYPE_NONE ||
2125 xg_separator_p (cur->name))
2126 break;
2128 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2130 sub = gtk_menu_item_get_submenu (witem);
2131 if (sub && ! cur->contents)
2133 /* Not a submenu anymore. */
2134 gtk_widget_ref (sub);
2135 gtk_menu_item_remove_submenu (witem);
2136 gtk_widget_destroy (sub);
2138 else if (cur->contents)
2140 GtkWidget *nsub;
2142 nsub = xg_update_submenu (sub, f, cur->contents,
2143 select_cb, deactivate_cb,
2144 highlight_cb, cl_data);
2146 /* If this item just became a submenu, we must set it. */
2147 if (nsub != sub)
2148 gtk_menu_item_set_submenu (witem, nsub);
2151 else
2153 /* Structural difference. Remove everything from here and down
2154 in SUBMENU. */
2155 break;
2159 /* Remove widgets from first structual change. */
2160 if (iter)
2162 /* If we are adding new menu items below, we must remove from
2163 first radio button so that radio groups become correct. */
2164 if (cur && first_radio) remove_from_container (submenu, first_radio);
2165 else remove_from_container (submenu, iter);
2168 if (cur)
2170 /* More items added. Create them. */
2171 newsub = create_menus (cur,
2173 select_cb,
2174 deactivate_cb,
2175 highlight_cb,
2178 ! has_tearoff_p,
2179 submenu,
2180 cl_data,
2184 if (list) g_list_free (list);
2186 return newsub;
2189 /* Update the MENUBAR.
2190 F is the frame the menu bar belongs to.
2191 VAL describes the contents of the menu bar.
2192 If DEEP_P is non-zero, rebuild all but the top level menu names in
2193 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
2194 SELECT_CB is the callback to use when a menu item is selected.
2195 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2196 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
2197 void
2198 xg_modify_menubar_widgets (menubar, f, val, deep_p,
2199 select_cb, deactivate_cb, highlight_cb)
2200 GtkWidget *menubar;
2201 FRAME_PTR f;
2202 widget_value *val;
2203 int deep_p;
2204 GCallback select_cb;
2205 GCallback deactivate_cb;
2206 GCallback highlight_cb;
2208 xg_menu_cb_data *cl_data;
2209 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
2211 if (! list) return;
2213 cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
2214 XG_FRAME_DATA);
2216 if (! deep_p)
2218 widget_value *cur = val->contents;
2219 xg_update_menubar (menubar, f, &list, list, 0, cur,
2220 select_cb, highlight_cb, cl_data);
2222 else
2224 widget_value *cur;
2226 /* Update all sub menus.
2227 We must keep the submenu names (GTK menu item widgets) since the
2228 X Window in the XEvent that activates the menu are those widgets. */
2230 /* Update cl_data, menu_item things in F may have changed. */
2231 update_cl_data (cl_data, f, highlight_cb);
2233 for (cur = val->contents; cur; cur = cur->next)
2235 GList *iter;
2236 GtkWidget *sub = 0;
2237 GtkWidget *newsub;
2238 GtkMenuItem *witem;
2240 /* Find sub menu that corresponds to val and update it. */
2241 for (iter = list ; iter; iter = g_list_next (iter))
2243 witem = GTK_MENU_ITEM (iter->data);
2244 if (xg_item_label_same_p (witem, cur->name))
2246 sub = gtk_menu_item_get_submenu (witem);
2247 break;
2251 newsub = xg_update_submenu (sub,
2253 cur->contents,
2254 select_cb,
2255 deactivate_cb,
2256 highlight_cb,
2257 cl_data);
2258 /* sub may still be NULL. If we just updated non deep and added
2259 a new menu bar item, it has no sub menu yet. So we set the
2260 newly created sub menu under witem. */
2261 if (newsub != sub)
2262 gtk_menu_item_set_submenu (witem, newsub);
2267 g_list_free (list);
2268 gtk_widget_show_all (menubar);
2271 /* Recompute all the widgets of frame F, when the menu bar has been
2272 changed. Value is non-zero if widgets were updated. */
2275 xg_update_frame_menubar (f)
2276 FRAME_PTR f;
2278 struct x_output *x = f->output_data.x;
2279 GtkRequisition req;
2281 if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
2282 return 0;
2284 BLOCK_INPUT;
2286 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
2287 FALSE, FALSE, 0);
2288 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
2290 gtk_widget_show_all (x->menubar_widget);
2291 gtk_widget_size_request (x->menubar_widget, &req);
2293 FRAME_MENUBAR_HEIGHT (f) = req.height;
2295 /* The height has changed, resize outer widget and set columns
2296 rows to what we had before adding the menu bar. */
2297 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2299 SET_FRAME_GARBAGED (f);
2300 UNBLOCK_INPUT;
2302 return 1;
2305 /* Get rid of the menu bar of frame F, and free its storage.
2306 This is used when deleting a frame, and when turning off the menu bar. */
2308 void
2309 free_frame_menubar (f)
2310 FRAME_PTR f;
2312 struct x_output *x = f->output_data.x;
2314 if (x->menubar_widget)
2316 BLOCK_INPUT;
2318 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
2319 /* The menubar and its children shall be deleted when removed from
2320 the container. */
2321 x->menubar_widget = 0;
2322 FRAME_MENUBAR_HEIGHT (f) = 0;
2324 /* The height has changed, resize outer widget and set columns
2325 rows to what we had before removing the menu bar. */
2326 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2328 SET_FRAME_GARBAGED (f);
2329 UNBLOCK_INPUT;
2335 /***********************************************************************
2336 Scroll bar functions
2337 ***********************************************************************/
2340 /* Setting scroll bar values invokes the callback. Use this variable
2341 to indicate that callback should do nothing. */
2342 int xg_ignore_gtk_scrollbar;
2344 /* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
2345 32 bits. But we want to store pointers, and they may be larger
2346 than 32 bits. Keep a mapping from integer index to widget pointers
2347 to get around the 32 bit limitation. */
2348 static struct
2350 GtkWidget **widgets;
2351 int max_size;
2352 int used;
2353 } id_to_widget;
2355 /* Grow this much every time we need to allocate more */
2356 #define ID_TO_WIDGET_INCR 32
2358 /* Store the widget pointer W in id_to_widget and return the integer index. */
2359 static int
2360 xg_store_widget_in_map (w)
2361 GtkWidget *w;
2363 int i;
2365 if (id_to_widget.max_size == id_to_widget.used)
2367 int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
2369 id_to_widget.widgets = xrealloc (id_to_widget.widgets,
2370 sizeof (GtkWidget *)*new_size);
2372 for (i = id_to_widget.max_size; i < new_size; ++i)
2373 id_to_widget.widgets[i] = 0;
2374 id_to_widget.max_size = new_size;
2377 /* Just loop over the array and find a free place. After all,
2378 how many scroll bars are we creating? Should be a small number.
2379 The check above guarantees we will find a free place. */
2380 for (i = 0; i < id_to_widget.max_size; ++i)
2382 if (! id_to_widget.widgets[i])
2384 id_to_widget.widgets[i] = w;
2385 ++id_to_widget.used;
2387 return i;
2391 /* Should never end up here */
2392 abort ();
2395 /* Remove pointer at IDX from id_to_widget.
2396 Called when scroll bar is destroyed. */
2397 static void
2398 xg_remove_widget_from_map (idx)
2399 int idx;
2401 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2403 id_to_widget.widgets[idx] = 0;
2404 --id_to_widget.used;
2408 /* Get the widget pointer at IDX from id_to_widget. */
2409 static GtkWidget *
2410 xg_get_widget_from_map (idx)
2411 int idx;
2413 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2414 return id_to_widget.widgets[idx];
2416 return 0;
2419 /* Return the scrollbar id for X Window WID.
2420 Return -1 if WID not in id_to_widget. */
2422 xg_get_scroll_id_for_window (wid)
2423 Window wid;
2425 int idx;
2426 GtkWidget *w;
2428 w = xg_win_to_widget (wid);
2430 if (w)
2432 for (idx = 0; idx < id_to_widget.max_size; ++idx)
2433 if (id_to_widget.widgets[idx] == w)
2434 return idx;
2437 return -1;
2440 /* Callback invoked when scroll bar WIDGET is destroyed.
2441 DATA is the index into id_to_widget for WIDGET.
2442 We free pointer to last scroll bar values here and remove the index. */
2443 static void
2444 xg_gtk_scroll_destroy (widget, data)
2445 GtkWidget *widget;
2446 gpointer data;
2448 gpointer p;
2449 int id = (int)data;
2451 p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
2452 if (p) xfree (p);
2453 xg_remove_widget_from_map (id);
2456 /* Callback for button press/release events. Used to start timer so that
2457 the scroll bar repetition timer in GTK gets handeled.
2458 Also, sets bar->dragging to Qnil when dragging (button release) is done.
2459 WIDGET is the scroll bar widget the event is for (not used).
2460 EVENT contains the event.
2461 USER_DATA points to the struct scrollbar structure.
2463 Returns FALSE to tell GTK that it shall continue propagate the event
2464 to widgets. */
2465 static gboolean
2466 scroll_bar_button_cb (widget, event, user_data)
2467 GtkWidget *widget;
2468 GdkEventButton *event;
2469 gpointer user_data;
2471 if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
2472 xg_start_timer ();
2473 else if (event->type == GDK_BUTTON_RELEASE)
2475 struct scroll_bar *bar = (struct scroll_bar *) user_data;
2476 if (xg_timer) xg_stop_timer ();
2477 bar->dragging = Qnil;
2480 return FALSE;
2483 /* Create a scroll bar widget for frame F. Store the scroll bar
2484 in BAR.
2485 SCROLL_CALLBACK is the callback to invoke when the value of the
2486 bar changes.
2487 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
2488 to set resources for the widget. */
2489 void
2490 xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
2491 FRAME_PTR f;
2492 struct scroll_bar *bar;
2493 GCallback scroll_callback;
2494 char *scroll_bar_name;
2496 GtkWidget *wscroll;
2497 GtkObject *vadj;
2498 int scroll_id;
2500 /* Page, step increment values are not so important here, they
2501 will be corrected in x_set_toolkit_scroll_bar_thumb. */
2502 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
2503 0.1, 0.1, 0.1);
2505 wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
2506 gtk_widget_set_name (wscroll, scroll_bar_name);
2507 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
2509 scroll_id = xg_store_widget_in_map (wscroll);
2511 g_signal_connect (G_OBJECT (wscroll),
2512 "value-changed",
2513 scroll_callback,
2514 (gpointer) bar);
2515 g_signal_connect (G_OBJECT (wscroll),
2516 "destroy",
2517 G_CALLBACK (xg_gtk_scroll_destroy),
2518 (gpointer) scroll_id);
2520 /* Connect to button press and button release to detect if any scroll bar
2521 has the pointer. */
2522 g_signal_connect (G_OBJECT (wscroll),
2523 "button-press-event",
2524 G_CALLBACK (scroll_bar_button_cb),
2525 (gpointer) bar);
2526 g_signal_connect (G_OBJECT (wscroll),
2527 "button-release-event",
2528 G_CALLBACK (scroll_bar_button_cb),
2529 (gpointer) bar);
2531 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
2532 wscroll, -1, -1);
2534 /* Set the cursor to an arrow. */
2535 xg_set_cursor (wscroll, &xg_left_ptr_cursor);
2537 SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
2540 /* Make the scroll bar represented by SCROLLBAR_ID visible. */
2541 void
2542 xg_show_scroll_bar (scrollbar_id)
2543 int scrollbar_id;
2545 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2546 if (w)
2547 gtk_widget_show (w);
2550 /* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
2551 void
2552 xg_remove_scroll_bar (f, scrollbar_id)
2553 FRAME_PTR f;
2554 int scrollbar_id;
2556 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2557 if (w)
2559 gtk_widget_destroy (w);
2560 SET_FRAME_GARBAGED (f);
2564 /* Find left/top for widget W in GtkFixed widget WFIXED. */
2565 static void
2566 xg_find_top_left_in_fixed (w, wfixed, left, top)
2567 GtkWidget *w, *wfixed;
2568 int *left, *top;
2570 GList *iter;
2572 for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
2574 GtkFixedChild *child = (GtkFixedChild *) iter->data;
2576 if (child->widget == w)
2578 *left = child->x;
2579 *top = child->y;
2580 return;
2584 /* Shall never end up here. */
2585 abort ();
2588 /* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
2589 in frame F.
2590 TOP/LEFT are the new pixel positions where the bar shall appear.
2591 WIDTH, HEIGHT is the size in pixels the bar shall have. */
2592 void
2593 xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height,
2594 real_left, canon_width)
2595 FRAME_PTR f;
2596 int scrollbar_id;
2597 int top;
2598 int left;
2599 int width;
2600 int height;
2603 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
2605 if (wscroll)
2607 GtkWidget *wfixed = f->output_data.x->edit_widget;
2608 int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
2609 int bottom = top + height;
2611 gint slider_width;
2612 int oldtop, oldleft, oldbottom;
2613 GtkRequisition req;
2615 /* Get old values. */
2616 xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop);
2617 gtk_widget_size_request (wscroll, &req);
2618 oldbottom = oldtop + req.height;
2620 /* Scroll bars in GTK has a fixed width, so if we say width 16, it
2621 will only be its fixed width (14 is default) anyway, the rest is
2622 blank. We are drawing the mode line across scroll bars when
2623 the frame is split:
2624 |bar| |fringe|
2625 ----------------
2626 mode line
2627 ----------------
2628 |bar| |fringe|
2630 When we "unsplit" the frame:
2632 |bar| |fringe|
2633 -| |-| |
2634 m¦ |i| |
2635 -| |-| |
2636 | | | |
2639 the remains of the mode line can be seen in these blank spaces.
2640 So we must clear them explicitly.
2641 GTK scroll bars should do that, but they don't.
2642 Also, the canonical width may be wider than the width for the
2643 scroll bar so that there is some space (typically 1 pixel) between
2644 the scroll bar and the edge of the window and between the scroll
2645 bar and the fringe. */
2647 if (oldtop != -1 && oldleft != -1)
2649 int gtkextral, gtkextrah;
2650 int xl, xr, wbl, wbr;
2651 int bottomdiff, topdiff;
2653 gtk_widget_style_get (wscroll, "slider_width", &slider_width, NULL);
2654 gtkextral = width > slider_width ? (width - slider_width) / 2 : 0;
2655 gtkextrah = gtkextral ? (width - slider_width - gtkextral) : 0;
2657 xl = real_left;
2658 wbl = gtkextral + winextra;
2659 wbr = gtkextrah + winextra;
2660 xr = left + gtkextral + slider_width;
2661 bottomdiff = abs (oldbottom - bottom);
2662 topdiff = abs (oldtop - top);
2664 if (oldleft != left)
2666 gdk_window_clear_area (wfixed->window, xl, top, wbl, height);
2667 gdk_window_clear_area (wfixed->window, xr, top, wbr, height);
2670 if (oldtop > top)
2672 gdk_window_clear_area (wfixed->window, xl, top, wbl, topdiff);
2673 gdk_window_clear_area (wfixed->window, xr, top, wbr, topdiff);
2675 else if (oldtop < top)
2677 gdk_window_clear_area (wfixed->window, xl, oldtop, wbl, topdiff);
2678 gdk_window_clear_area (wfixed->window, xr, oldtop, wbr, topdiff);
2681 if (oldbottom > bottom)
2683 gdk_window_clear_area (wfixed->window, xl, bottom, wbl,
2684 bottomdiff);
2685 gdk_window_clear_area (wfixed->window, xr, bottom, wbr,
2686 bottomdiff);
2688 else if (oldbottom < bottom)
2690 gdk_window_clear_area (wfixed->window, xl, oldbottom, wbl,
2691 bottomdiff);
2692 gdk_window_clear_area (wfixed->window, xr, oldbottom, wbr,
2693 bottomdiff);
2697 /* Move and resize to new values. */
2698 gtk_fixed_move (GTK_FIXED (wfixed), wscroll, left, top);
2699 gtk_widget_set_size_request (wscroll, width, height);
2701 /* Must force out update so changed scroll bars gets redrawn. */
2702 gdk_window_process_all_updates ();
2704 SET_FRAME_GARBAGED (f);
2705 cancel_mouse_face (f);
2709 /* Set the thumb size and position of scroll bar BAR. We are currently
2710 displaying PORTION out of a whole WHOLE, and our position POSITION. */
2711 void
2712 xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
2713 struct scroll_bar *bar;
2714 int portion, position, whole;
2716 GtkWidget *wscroll = xg_get_widget_from_map (SCROLL_BAR_X_WINDOW (bar));
2718 FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
2720 if (wscroll && NILP (bar->dragging))
2722 GtkAdjustment *adj;
2723 gdouble shown;
2724 gdouble top;
2725 int size, value;
2726 int new_step;
2727 int changed = 0;
2729 adj = gtk_range_get_adjustment (GTK_RANGE (wscroll));
2731 /* We do the same as for MOTIF in xterm.c, assume 30 chars per line
2732 rather than the real portion value. This makes the thumb less likely
2733 to resize and that looks better. */
2734 portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
2735 /* When the thumb is at the bottom, position == whole.
2736 So we need to increase `whole' to make space for the thumb. */
2737 whole += portion;
2739 if (whole <= 0)
2740 top = 0, shown = 1;
2741 else
2743 top = (gdouble) position / whole;
2744 shown = (gdouble) portion / whole;
2747 size = shown * XG_SB_RANGE;
2748 size = min (size, XG_SB_RANGE);
2749 size = max (size, 1);
2751 value = top * XG_SB_RANGE;
2752 value = min (value, XG_SB_MAX - size);
2753 value = max (value, XG_SB_MIN);
2755 /* Assume all lines are of equal size. */
2756 new_step = size / max (1, FRAME_LINES (f));
2758 if ((int) adj->page_size != size
2759 || (int) adj->step_increment != new_step)
2761 adj->page_size = size;
2762 adj->step_increment = new_step;
2763 /* Assume a page increment is about 95% of the page size */
2764 adj->page_increment = (int) (0.95*adj->page_size);
2765 changed = 1;
2768 if (changed || (int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
2770 GtkWidget *wfixed = f->output_data.x->edit_widget;
2772 BLOCK_INPUT;
2774 /* gtk_range_set_value invokes the callback. Set
2775 ignore_gtk_scrollbar to make the callback do nothing */
2776 xg_ignore_gtk_scrollbar = 1;
2778 if ((int) gtk_range_get_value (GTK_RANGE (wscroll)) != value)
2779 gtk_range_set_value (GTK_RANGE (wscroll), (gdouble)value);
2780 else if (changed)
2781 gtk_adjustment_changed (adj);
2783 xg_ignore_gtk_scrollbar = 0;
2785 UNBLOCK_INPUT;
2791 /***********************************************************************
2792 Tool bar functions
2793 ***********************************************************************/
2794 /* The key for the data we put in the GtkImage widgets. The data is
2795 the image used by Emacs. We use this to see if we need to update
2796 the GtkImage with a new image. */
2797 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
2799 /* Callback function invoked when a tool bar item is pressed.
2800 W is the button widget in the tool bar that got pressed,
2801 CLIENT_DATA is an integer that is the index of the button in the
2802 tool bar. 0 is the first button. */
2803 static void
2804 xg_tool_bar_callback (w, client_data)
2805 GtkWidget *w;
2806 gpointer client_data;
2808 int idx = (int)client_data;
2809 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
2810 Lisp_Object key, frame;
2811 struct input_event event;
2813 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
2814 return;
2816 idx *= TOOL_BAR_ITEM_NSLOTS;
2818 key = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_KEY);
2819 XSETFRAME (frame, f);
2820 event.kind = TOOL_BAR_EVENT;
2821 event.frame_or_window = frame;
2822 event.arg = frame;
2823 kbd_buffer_store_event (&event);
2825 event.kind = TOOL_BAR_EVENT;
2826 event.frame_or_window = frame;
2827 event.arg = key;
2828 event.modifiers = 0; /* These are not available. */
2829 kbd_buffer_store_event (&event);
2832 /* This callback is called when a tool bar is detached. We must set
2833 the height of the tool bar to zero when this happens so frame sizes
2834 are correctly calculated.
2835 WBOX is the handle box widget that enables detach/attach of the tool bar.
2836 W is the tool bar widget.
2837 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
2838 static void
2839 xg_tool_bar_detach_callback (wbox, w, client_data)
2840 GtkHandleBox *wbox;
2841 GtkWidget *w;
2842 gpointer client_data;
2844 FRAME_PTR f = (FRAME_PTR) client_data;
2846 if (f)
2848 /* When detaching a tool bar, not everything dissapear. There are
2849 a few pixels left that are used to drop the tool bar back into
2850 place. */
2851 int bw = gtk_container_get_border_width (GTK_CONTAINER (wbox));
2852 FRAME_TOOLBAR_HEIGHT (f) = 2;
2854 /* The height has changed, resize outer widget and set columns
2855 rows to what we had before detaching the tool bar. */
2856 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2860 /* This callback is called when a tool bar is reattached. We must set
2861 the height of the tool bar when this happens so frame sizes
2862 are correctly calculated.
2863 WBOX is the handle box widget that enables detach/attach of the tool bar.
2864 W is the tool bar widget.
2865 CLIENT_DATA is a pointer to the frame the tool bar belongs to. */
2866 static void
2867 xg_tool_bar_attach_callback (wbox, w, client_data)
2868 GtkHandleBox *wbox;
2869 GtkWidget *w;
2870 gpointer client_data;
2872 FRAME_PTR f = (FRAME_PTR) client_data;
2874 if (f)
2876 GtkRequisition req;
2878 gtk_widget_size_request (w, &req);
2879 FRAME_TOOLBAR_HEIGHT (f) = req.height;
2881 /* The height has changed, resize outer widget and set columns
2882 rows to what we had before detaching the tool bar. */
2883 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
2887 /* This callback is called when the mouse enters or leaves a tool bar item.
2888 It is used for displaying and hiding the help text.
2889 W is the tool bar item, a button.
2890 EVENT is either an enter event or leave event.
2891 CLIENT_DATA is an integer that is the index of the button in the
2892 tool bar. 0 is the first button.
2894 Returns FALSE to tell GTK to keep processing this event. */
2895 static gboolean
2896 xg_tool_bar_help_callback (w, event, client_data)
2897 GtkWidget *w;
2898 GdkEventCrossing *event;
2899 gpointer client_data;
2901 int idx = (int)client_data;
2902 FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
2903 Lisp_Object help, frame;
2905 if (! GTK_IS_BUTTON (w))
2907 return FALSE;
2910 if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
2911 return FALSE;
2913 if (event->type == GDK_ENTER_NOTIFY)
2915 idx *= TOOL_BAR_ITEM_NSLOTS;
2916 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_HELP);
2918 if (NILP (help))
2919 help = AREF (f->tool_bar_items, idx + TOOL_BAR_ITEM_CAPTION);
2921 else
2922 help = Qnil;
2924 XSETFRAME (frame, f);
2925 kbd_buffer_store_help_event (frame, help);
2927 return FALSE;
2931 /* This callback is called when a tool bar item shall be redrawn.
2932 It modifies the expose event so that the GtkImage widget redraws the
2933 whole image. This to overcome a bug that makes GtkImage draw the image
2934 in the wrong place when it tries to redraw just a part of the image.
2935 W is the GtkImage to be redrawn.
2936 EVENT is the expose event for W.
2937 CLIENT_DATA is unused.
2939 Returns FALSE to tell GTK to keep processing this event. */
2940 static gboolean
2941 xg_tool_bar_item_expose_callback (w, event, client_data)
2942 GtkWidget *w;
2943 GdkEventExpose *event;
2944 gpointer client_data;
2946 gint width, height;
2948 gdk_drawable_get_size (event->window, &width, &height);
2950 event->area.x -= width > event->area.width ? width-event->area.width : 0;
2951 event->area.y -= height > event->area.height ? height-event->area.height : 0;
2953 event->area.x = max(0, event->area.x);
2954 event->area.y = max(0, event->area.y);
2956 event->area.width = max (width, event->area.width);
2957 event->area.height = max (height, event->area.height);
2959 return FALSE;
2962 /* This callback is called when a tool bar shall be redrawn.
2963 We need to update the tool bar from here in case the image cache
2964 has deleted the pixmaps used in the tool bar.
2965 W is the GtkToolbar to be redrawn.
2966 EVENT is the expose event for W.
2967 CLIENT_DATA is pointing to the frame for this tool bar.
2969 Returns FALSE to tell GTK to keep processing this event. */
2970 static gboolean
2971 xg_tool_bar_expose_callback (w, event, client_data)
2972 GtkWidget *w;
2973 GdkEventExpose *event;
2974 gpointer client_data;
2976 update_frame_tool_bar((FRAME_PTR)client_data);
2977 return FALSE;
2980 static void
2981 xg_create_tool_bar (f)
2982 FRAME_PTR f;
2984 struct x_output *x = f->output_data.x;
2985 GtkRequisition req;
2986 int vbox_pos = x->menubar_widget ? 1 : 0;
2988 x->toolbar_widget = gtk_toolbar_new ();
2989 x->handlebox_widget = gtk_handle_box_new ();
2990 gtk_container_add (GTK_CONTAINER (x->handlebox_widget),
2991 x->toolbar_widget);
2993 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->handlebox_widget,
2994 FALSE, FALSE, 0);
2996 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->handlebox_widget,
2997 vbox_pos);
2999 gtk_widget_set_name (x->toolbar_widget, "emacs-toolbar");
3001 /* We only have icons, so override any user setting. We could
3002 use the caption property of the toolbar item (see update_frame_tool_bar
3003 below), but some of those strings are long, making the toolbar so
3004 long it does not fit on the screen. The GtkToolbar widget makes every
3005 item equal size, so the longest caption determine the size of every
3006 tool bar item. I think the creators of the GtkToolbar widget
3007 counted on 4 or 5 character long strings. */
3008 gtk_toolbar_set_style (GTK_TOOLBAR (x->toolbar_widget), GTK_TOOLBAR_ICONS);
3009 gtk_toolbar_set_orientation (GTK_TOOLBAR (x->toolbar_widget),
3010 GTK_ORIENTATION_HORIZONTAL);
3012 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-detached",
3013 G_CALLBACK (xg_tool_bar_detach_callback), f);
3014 g_signal_connect (G_OBJECT (x->handlebox_widget), "child-attached",
3015 G_CALLBACK (xg_tool_bar_attach_callback), f);
3016 g_signal_connect (G_OBJECT (x->toolbar_widget),
3017 "expose-event",
3018 G_CALLBACK (xg_tool_bar_expose_callback),
3021 gtk_widget_show_all (x->handlebox_widget);
3023 gtk_widget_size_request (x->toolbar_widget, &req);
3024 FRAME_TOOLBAR_HEIGHT (f) = req.height;
3026 /* The height has changed, resize outer widget and set columns
3027 rows to what we had before adding the tool bar. */
3028 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3030 SET_FRAME_GARBAGED (f);
3033 void
3034 update_frame_tool_bar (f)
3035 FRAME_PTR f;
3037 int i;
3038 GtkRequisition old_req, new_req;
3039 GList *icon_list;
3040 GList *iter;
3041 struct x_output *x = f->output_data.x;
3043 if (! FRAME_GTK_WIDGET (f))
3044 return;
3046 BLOCK_INPUT;
3048 if (! x->toolbar_widget)
3049 xg_create_tool_bar (f);
3051 gtk_widget_size_request (x->toolbar_widget, &old_req);
3053 icon_list = gtk_container_get_children (GTK_CONTAINER (x->toolbar_widget));
3054 iter = icon_list;
3056 for (i = 0; i < f->n_tool_bar_items; ++i)
3058 #define PROP(IDX) AREF (f->tool_bar_items, i * TOOL_BAR_ITEM_NSLOTS + (IDX))
3060 int enabled_p = !NILP (PROP (TOOL_BAR_ITEM_ENABLED_P));
3061 int selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P));
3062 int idx;
3063 int img_id;
3064 struct image *img;
3065 Lisp_Object image;
3066 GtkWidget *wicon = iter ? GTK_WIDGET (iter->data) : 0;
3068 if (iter) iter = g_list_next (iter);
3070 /* If image is a vector, choose the image according to the
3071 button state. */
3072 image = PROP (TOOL_BAR_ITEM_IMAGES);
3073 if (VECTORP (image))
3075 if (enabled_p)
3076 idx = (selected_p
3077 ? TOOL_BAR_IMAGE_ENABLED_SELECTED
3078 : TOOL_BAR_IMAGE_ENABLED_DESELECTED);
3079 else
3080 idx = (selected_p
3081 ? TOOL_BAR_IMAGE_DISABLED_SELECTED
3082 : TOOL_BAR_IMAGE_DISABLED_DESELECTED);
3084 xassert (ASIZE (image) >= idx);
3085 image = AREF (image, idx);
3087 else
3088 idx = -1;
3090 /* Ignore invalid image specifications. */
3091 if (!valid_image_p (image))
3093 if (wicon) gtk_widget_hide (wicon);
3094 continue;
3097 img_id = lookup_image (f, image);
3098 img = IMAGE_FROM_ID (f, img_id);
3099 prepare_image_for_display (f, img);
3101 if (img->load_failed_p || img->pixmap == None)
3103 if (wicon) gtk_widget_hide (wicon);
3104 continue;
3107 if (! wicon)
3109 GdkPixmap *gpix = gdk_pixmap_foreign_new (img->pixmap);
3110 GdkBitmap *gmask = img->mask ?
3111 (GdkBitmap*) gdk_pixmap_foreign_new (img->mask) : 0;
3113 GtkWidget *w = gtk_image_new_from_pixmap (gpix, gmask);
3114 gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
3115 0, 0, 0,
3117 GTK_SIGNAL_FUNC (xg_tool_bar_callback),
3118 (gpointer)i);
3120 /* Save the image so we can see if an update is needed when
3121 this function is called again. */
3122 g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
3123 (gpointer)img->pixmap);
3125 /* Catch expose events to overcome an annoying redraw bug, see
3126 comment for xg_tool_bar_item_expose_callback. */
3127 g_signal_connect (G_OBJECT (w),
3128 "expose-event",
3129 G_CALLBACK (xg_tool_bar_item_expose_callback),
3132 /* We must set sensitive on the button that is the parent
3133 of the GtkImage parent. Go upwards until we find the button. */
3134 while (! GTK_IS_BUTTON (w))
3135 w = gtk_widget_get_parent (w);
3137 if (w)
3139 /* Save the frame in the button so the xg_tool_bar_callback
3140 can get at it. */
3141 g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
3142 gtk_widget_set_sensitive (w, enabled_p);
3144 /* Use enter/leave notify to show help. We use the events
3145 rather than the GtkButton specific signals "enter" and
3146 "leave", so we can have only one callback. The event
3147 will tell us what kind of event it is. */
3148 g_signal_connect (G_OBJECT (w),
3149 "enter-notify-event",
3150 G_CALLBACK (xg_tool_bar_help_callback),
3151 (gpointer)i);
3152 g_signal_connect (G_OBJECT (w),
3153 "leave-notify-event",
3154 G_CALLBACK (xg_tool_bar_help_callback),
3155 (gpointer)i);
3158 else
3160 /* The child of the tool bar is a button. Inside that button
3161 is a vbox. Inside that vbox is the GtkImage. */
3162 GtkWidget *wvbox = gtk_bin_get_child (GTK_BIN (wicon));
3163 GList *chlist = gtk_container_get_children (GTK_CONTAINER (wvbox));
3164 GtkImage *wimage = GTK_IMAGE (chlist->data);
3165 Pixmap old_img = (Pixmap)g_object_get_data (G_OBJECT (wimage),
3166 XG_TOOL_BAR_IMAGE_DATA);
3167 g_list_free (chlist);
3169 if (old_img != img->pixmap)
3171 GdkPixmap *gpix = gdk_pixmap_foreign_new (img->pixmap);
3172 GdkBitmap *gmask = img->mask ?
3173 (GdkBitmap*) gdk_pixmap_foreign_new (img->mask) : 0;
3175 gtk_image_set_from_pixmap (wimage, gpix, gmask);
3178 g_object_set_data (G_OBJECT (wimage), XG_TOOL_BAR_IMAGE_DATA,
3179 (gpointer)img->pixmap);
3181 gtk_widget_set_sensitive (wicon, enabled_p);
3182 gtk_widget_show (wicon);
3185 #undef PROP
3188 /* Remove buttons not longer needed. We just hide them so they
3189 can be reused later on. */
3190 while (iter)
3192 GtkWidget *w = GTK_WIDGET (iter->data);
3193 gtk_widget_hide (w);
3194 iter = g_list_next (iter);
3197 gtk_widget_size_request (x->toolbar_widget, &new_req);
3198 if (old_req.height != new_req.height)
3200 FRAME_TOOLBAR_HEIGHT (f) = new_req.height;
3201 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3204 if (icon_list) g_list_free (icon_list);
3206 UNBLOCK_INPUT;
3209 void
3210 free_frame_tool_bar (f)
3211 FRAME_PTR f;
3213 struct x_output *x = f->output_data.x;
3215 if (x->toolbar_widget)
3217 BLOCK_INPUT;
3218 gtk_container_remove (GTK_CONTAINER (x->vbox_widget),
3219 x->handlebox_widget);
3220 x->toolbar_widget = 0;
3221 x->handlebox_widget = 0;
3222 FRAME_TOOLBAR_HEIGHT (f) = 0;
3224 /* The height has changed, resize outer widget and set columns
3225 rows to what we had before removing the tool bar. */
3226 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
3228 SET_FRAME_GARBAGED (f);
3229 UNBLOCK_INPUT;
3235 /***********************************************************************
3236 Initializing
3237 ***********************************************************************/
3238 void
3239 xg_initialize ()
3241 xg_ignore_gtk_scrollbar = 0;
3242 xg_left_ptr_cursor = 0;
3243 xg_did_tearoff = 0;
3245 xg_menu_cb_list.prev = xg_menu_cb_list.next =
3246 xg_menu_item_cb_list.prev = xg_menu_item_cb_list.next = 0;
3248 id_to_widget.max_size = id_to_widget.used = 0;
3249 id_to_widget.widgets = 0;
3251 /* Remove F10 as a menu accelerator, it does not mix well with Emacs key
3252 bindings. It doesn't seem to be any way to remove properties,
3253 so we set it to VoidSymbol which in X means "no key". */
3254 gtk_settings_set_string_property (gtk_settings_get_default (),
3255 "gtk-menu-bar-accel",
3256 "VoidSymbol",
3257 EMACS_CLASS);
3259 /* Make GTK text input widgets use Emacs style keybindings. This is
3260 Emacs after all. */
3261 gtk_settings_set_string_property (gtk_settings_get_default (),
3262 "gtk-key-theme-name",
3263 "Emacs",
3264 EMACS_CLASS);
3267 #endif /* USE_GTK */