Remove tear off capability for GTK popup menus.
[bpt/emacs.git] / src / gtkutil.c
CommitLineData
f392e843
JD
1/* Functions for creating and updating GTK widgets.
2 Copyright (C) 2003
3 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
19the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA. */
21
22#include "config.h"
23
24#ifdef USE_GTK
e8794476
JD
25#include <string.h>
26#include <stdio.h>
f392e843
JD
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"
5b07197a
DL
34#include "keyboard.h"
35#include "charset.h"
36#include "coding.h"
f392e843
JD
37#include <gdk/gdkkeysyms.h>
38
39#define FRAME_TOTAL_PIXEL_HEIGHT(f) \
be786000 40 (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
cea9be54
JD
41
42
f392e843
JD
43\f
44/***********************************************************************
45 Utility functions
46 ***********************************************************************/
47/* The timer for scroll bar repetition and menu bar timeouts.
48 NULL if no timer is started. */
49static struct atimer *xg_timer;
50
51/* The cursor used for scroll bars and popup menus.
52 We only have one cursor for all scroll bars and all popup menus. */
53static GdkCursor *xg_left_ptr_cursor;
54
55
56/* The next two variables and functions are taken from lwlib. */
57static widget_value *widget_value_free_list;
58static int malloc_cpt;
59
60/* Allocate a widget_value structure, either by taking one from the
61 widget_value_free_list or by malloc:ing a new one.
62
63 Return a pointer to the allocated structure. */
64widget_value *
65malloc_widget_value ()
66{
67 widget_value *wv;
68 if (widget_value_free_list)
69 {
70 wv = widget_value_free_list;
71 widget_value_free_list = wv->free_list;
72 wv->free_list = 0;
73 }
74 else
75 {
76 wv = (widget_value *) malloc (sizeof (widget_value));
77 malloc_cpt++;
78 }
79 memset (wv, 0, sizeof (widget_value));
80 return wv;
81}
82
83/* This is analogous to free. It frees only what was allocated
84 by malloc_widget_value, and no substructures. */
85void
86free_widget_value (wv)
87 widget_value *wv;
88{
89 if (wv->free_list)
90 abort ();
91
92 if (malloc_cpt > 25)
93 {
94 /* When the number of already allocated cells is too big,
95 We free it. */
96 free (wv);
97 malloc_cpt--;
98 }
99 else
100 {
101 wv->free_list = widget_value_free_list;
102 widget_value_free_list = wv;
103 }
104}
105
106/* Set *CURSOR on W and all widgets W contain. We must do like this
107 for scroll bars and menu because they create widgets internally,
108 and it is those widgets that are visible.
109
110 If *CURSOR is NULL, create a GDK_LEFT_PTR cursor and set *CURSOR to
111 the created cursor. */
112void
113xg_set_cursor (w, cursor)
114 GtkWidget *w;
115 GdkCursor **cursor;
116{
117 GList *children = gdk_window_peek_children (w->window);
118
119 /* Create the cursor unless already created. */
120 if (! *cursor)
121 *cursor = gdk_cursor_new (GDK_LEFT_PTR);
122
123 gdk_window_set_cursor (w->window, *cursor);
124
125 /* The scroll bar widget has more than one GDK window (had to look at
126 the source to figure this out), and there is no way to set cursor
127 on widgets in GTK. So we must set the cursor for all GDK windows.
128 Ditto for menus. */
129
130 for ( ; children; children = g_list_next (children))
131 gdk_window_set_cursor (GDK_WINDOW (children->data), *cursor);
132}
133
134/* Timer function called when a timeout occurs for xg_timer.
135 This function processes all GTK events in a recursive event loop.
136 This is done because GTK timer events are not seen by Emacs event
137 detection, Emacs only looks for X events. When a scroll bar has the
138 pointer (detected by button press/release events below) an Emacs
139 timer is started, and this function can then check if the GTK timer
140 has expired by calling the GTK event loop.
141 Also, when a menu is active, it has a small timeout before it
142 pops down the sub menu under it. */
143static void
144xg_process_timeouts (timer)
145 struct atimer *timer;
146{
147 BLOCK_INPUT;
148 /* Ideally we would like to just handle timer events, like the Xt version
149 of this does in xterm.c, but there is no such feature in GTK. */
150 while (gtk_events_pending ())
151 gtk_main_iteration ();
152 UNBLOCK_INPUT;
153}
154
155/* Start the xg_timer with an interval of 0.1 seconds, if not already started.
156 xg_process_timeouts is called when the timer expires. The timer
7863d625 157 started is continuous, i.e. runs until xg_stop_timer is called. */
f392e843
JD
158static void
159xg_start_timer ()
160{
161 if (! xg_timer)
162 {
163 EMACS_TIME interval;
164 EMACS_SET_SECS_USECS (interval, 0, 100000);
165 xg_timer = start_atimer (ATIMER_CONTINUOUS,
166 interval,
167 xg_process_timeouts,
168 0);
169 }
170}
171
172/* Stop the xg_timer if started. */
173static void
174xg_stop_timer ()
175{
176 if (xg_timer)
177 {
178 cancel_atimer (xg_timer);
179 xg_timer = 0;
180 }
181}
182
183/* Insert NODE into linked LIST. */
184static void
185xg_list_insert (xg_list_node *list, xg_list_node *node)
186{
187 xg_list_node *list_start = list->next;
177c0ea7 188
f392e843
JD
189 if (list_start) list_start->prev = node;
190 node->next = list_start;
191 node->prev = 0;
192 list->next = node;
193}
194
195/* Remove NODE from linked LIST. */
196static void
197xg_list_remove (xg_list_node *list, xg_list_node *node)
198{
199 xg_list_node *list_start = list->next;
200 if (node == list_start)
201 {
202 list->next = node->next;
203 if (list->next) list->next->prev = 0;
204 }
205 else
206 {
207 node->prev->next = node->next;
208 if (node->next) node->next->prev = node->prev;
209 }
210}
211
212/* Allocate and return a utf8 version of STR. If STR is already
213 utf8 or NULL, just return STR.
214 If not, a new string is allocated and the caller must free the result
215 with g_free. */
216static char *
217get_utf8_string (str)
218 char *str;
219{
220 char *utf8_str = str;
177c0ea7 221
f392e843
JD
222 /* If not UTF-8, try current locale. */
223 if (str && !g_utf8_validate (str, -1, NULL))
224 utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
225
226 return utf8_str;
227}
228
229
230\f
231/***********************************************************************
232 General functions for creating widgets, resizing, events, e.t.c.
233 ***********************************************************************/
234
235/* Make a geometry string and pass that to GTK. It seems this is the
236 only way to get geometry position right if the user explicitly
237 asked for a position when starting Emacs.
238 F is the frame we shall set geometry for. */
239static void
240xg_set_geometry (f)
241 FRAME_PTR f;
242{
be786000 243 if (f->size_hint_flags & USPosition)
f392e843 244 {
be786000
KS
245 int left = f->left_pos;
246 int xneg = f->size_hint_flags & XNegative;
247 int top = f->top_pos;
248 int yneg = f->size_hint_flags & YNegative;
f392e843 249 char geom_str[32];
177c0ea7 250
f392e843
JD
251 if (xneg)
252 left = -left;
253 if (yneg)
254 top = -top;
255
256 sprintf (geom_str, "=%dx%d%c%d%c%d",
be786000 257 FRAME_PIXEL_WIDTH (f),
f392e843
JD
258 FRAME_TOTAL_PIXEL_HEIGHT (f),
259 (xneg ? '-' : '+'), left,
260 (yneg ? '-' : '+'), top);
261
262 if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
263 geom_str))
264 fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
265 }
266}
267
177c0ea7 268
f392e843
JD
269/* Resize the outer window of frame F after chainging the height.
270 This happend when the menu bar or the tool bar is added or removed.
271 COLUMNS/ROWS is the size the edit area shall have after the resize. */
272static void
273xg_resize_outer_widget (f, columns, rows)
274 FRAME_PTR f;
275 int columns;
276 int rows;
277{
278 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
be786000 279 FRAME_PIXEL_WIDTH (f), FRAME_TOTAL_PIXEL_HEIGHT (f));
f392e843
JD
280
281 /* base_height is now changed. */
282 x_wm_set_size_hint (f, 0, 0);
283
284 /* If we are not mapped yet, set geometry once again, as window
285 height now have changed. */
286 if (! GTK_WIDGET_MAPPED (FRAME_GTK_OUTER_WIDGET (f)))
287 xg_set_geometry (f);
288
289 xg_frame_set_char_size (f, columns, rows);
290 gdk_window_process_all_updates ();
291}
292
0cb35f4e
JD
293/* This gets called after the frame F has been cleared. Since that is
294 done with X calls, we need to redraw GTK widget (scroll bars). */
295void
296xg_frame_cleared (f)
297 FRAME_PTR f;
298{
de38ae5a 299 GtkWidget *w = f->output_data.x->widget;
0cb35f4e 300
de38ae5a 301 if (w)
0cb35f4e 302 {
de38ae5a
JD
303 gtk_container_set_reallocate_redraws (GTK_CONTAINER (w), TRUE);
304 gtk_container_foreach (GTK_CONTAINER (w),
305 (GtkCallback) gtk_widget_queue_draw,
306 0);
0cb35f4e
JD
307 gdk_window_process_all_updates ();
308 }
309}
310
f392e843
JD
311/* Function to handle resize of our widgets. Since Emacs has some layouts
312 that does not fit well with GTK standard containers, we do most layout
313 manually.
314 F is the frame to resize.
315 PIXELWIDTH, PIXELHEIGHT is the new size in pixels. */
316void
317xg_resize_widgets (f, pixelwidth, pixelheight)
318 FRAME_PTR f;
319 int pixelwidth, pixelheight;
320{
321 int mbheight = FRAME_MENUBAR_HEIGHT (f);
322 int tbheight = FRAME_TOOLBAR_HEIGHT (f);
be786000
KS
323 int rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, (pixelheight
324 - mbheight - tbheight));
325 int columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, pixelwidth);
177c0ea7 326
f392e843 327 if (FRAME_GTK_WIDGET (f)
be786000
KS
328 && (columns != FRAME_COLS (f) || rows != FRAME_LINES (f)
329 || pixelwidth != FRAME_PIXEL_WIDTH (f) || pixelheight != FRAME_PIXEL_HEIGHT (f)))
f392e843
JD
330 {
331 struct x_output *x = f->output_data.x;
332 GtkAllocation all;
333
334 all.y = mbheight + tbheight;
335 all.x = 0;
336
337 all.width = pixelwidth;
338 all.height = pixelheight - mbheight - tbheight;
339
340 gtk_widget_size_allocate (x->edit_widget, &all);
cea9be54 341
f392e843
JD
342 change_frame_size (f, rows, columns, 0, 1, 0);
343 SET_FRAME_GARBAGED (f);
344 cancel_mouse_face (f);
345 }
346}
347
348
349/* Update our widget size to be COLS/ROWS characters for frame F. */
350void
351xg_frame_set_char_size (f, cols, rows)
352 FRAME_PTR f;
353 int cols;
354 int rows;
355{
be786000 356 int pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, rows)
f392e843 357 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
5fd6f727 358 int pixelwidth;
177c0ea7 359
f392e843
JD
360 /* Take into account the size of the scroll bar. Always use the
361 number of columns occupied by the scroll bar here otherwise we
362 might end up with a frame width that is not a multiple of the
363 frame's character width which is bad for vertically split
364 windows. */
be786000
KS
365 f->scroll_bar_actual_width
366 = FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f);
f392e843 367
055d3c98 368 compute_fringe_widths (f, 0);
f392e843 369
be786000 370 /* FRAME_TEXT_COLS_TO_PIXEL_WIDTH uses scroll_bar_actual_width, so call it
5fd6f727 371 after calculating that value. */
be786000 372 pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, cols);
5fd6f727 373
f392e843
JD
374 /* Must resize our top level widget. Font size may have changed,
375 but not rows/cols. */
376 gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
377 pixelwidth, pixelheight);
378 xg_resize_widgets (f, pixelwidth, pixelheight);
5fd6f727
JD
379
380 SET_FRAME_GARBAGED (f);
381 cancel_mouse_face (f);
f392e843
JD
382}
383
384/* Convert an X Window WSESC to its corresponding GtkWidget.
385 Must be done like this, because GtkWidget:s can have "hidden"
386 X Window that aren't accessible.
387
388 Return 0 if no widget match WDESC. */
389GtkWidget *
390xg_win_to_widget (wdesc)
391 Window wdesc;
392{
393 gpointer gdkwin;
394 GtkWidget *gwdesc = 0;
395
396 BLOCK_INPUT;
397 gdkwin = gdk_xid_table_lookup (wdesc);
398 if (gdkwin)
399 {
400 GdkEvent event;
401 event.any.window = gdkwin;
402 gwdesc = gtk_get_event_widget (&event);
403 }
177c0ea7 404
f392e843
JD
405 UNBLOCK_INPUT;
406 return gwdesc;
407}
408
409/* Fill in the GdkColor C so that it represents PIXEL.
410 W is the widget that color will be used for. Used to find colormap. */
411static void
412xg_pix_to_gcolor (w, pixel, c)
413 GtkWidget *w;
414 unsigned long pixel;
415 GdkColor *c;
416{
417 GdkColormap *map = gtk_widget_get_colormap (w);
418 gdk_colormap_query_color (map, pixel, c);
419}
420
7863d625
JD
421/* Turning off double buffering for our GtkFixed widget has the side
422 effect of turning it off also for its children (scroll bars).
423 But we want those to be double buffered to not flicker so handle
424 expose manually here.
425 WIDGET is the GtkFixed widget that gets exposed.
426 EVENT is the expose event.
427 USER_DATA is unused.
428
429 Return TRUE to tell GTK that this expose event has been fully handeled
430 and that GTK shall do nothing more with it. */
431static gboolean
432xg_fixed_handle_expose(GtkWidget *widget,
433 GdkEventExpose *event,
434 gpointer user_data)
435{
436 GList *iter;
5fd6f727 437
7863d625
JD
438 for (iter = GTK_FIXED (widget)->children; iter; iter = g_list_next (iter))
439 {
440 GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
441 GtkWidget *child = child_data->widget;
442 GdkWindow *window = child->window;
443 GdkRegion *region = gtk_widget_region_intersect (child, event->region);
444
445 if (! gdk_region_empty (region))
446 {
447 GdkEvent child_event;
448 child_event.expose = *event;
449 child_event.expose.region = region;
450
451 /* Turn on double buffering, i.e. draw to an off screen area. */
452 gdk_window_begin_paint_region (window, region);
453
454 /* Tell child to redraw itself. */
455 gdk_region_get_clipbox (region, &child_event.expose.area);
456 gtk_widget_send_expose (child, &child_event);
457 gdk_window_process_updates (window, TRUE);
458
459 /* Copy off screen area to the window. */
460 gdk_window_end_paint (window);
461 }
462
463 gdk_region_destroy (region);
464 }
465
466 return TRUE;
467}
468
f392e843
JD
469/* Create and set up the GTK widgets for frame F.
470 Return 0 if creation failed, non-zero otherwise. */
471int
472xg_create_frame_widgets (f)
473 FRAME_PTR f;
474{
475 GtkWidget *wtop;
476 GtkWidget *wvbox;
477 GtkWidget *wfixed;
478 GdkColor bg;
479 GtkRcStyle *style;
480 int i;
481 char *title = 0;
177c0ea7 482
f392e843
JD
483 BLOCK_INPUT;
484
485 wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
486 wvbox = gtk_vbox_new (FALSE, 0);
487 wfixed = gtk_fixed_new (); /* Must have this to place scroll bars */
177c0ea7 488
f392e843
JD
489 if (! wtop || ! wvbox || ! wfixed)
490 {
491 if (wtop) gtk_widget_destroy (wtop);
492 if (wvbox) gtk_widget_destroy (wvbox);
493 if (wfixed) gtk_widget_destroy (wfixed);
494
495 return 0;
496 }
497
498 /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
499 gtk_widget_set_name (wtop, EMACS_CLASS);
500 gtk_widget_set_name (wvbox, "pane");
501 gtk_widget_set_name (wfixed, SDATA (Vx_resource_name));
502
503 /* If this frame has a title or name, set it in the title bar. */
5b07197a
DL
504 if (! NILP (f->title)) title = SDATA (ENCODE_UTF_8 (f->title));
505 else if (! NILP (f->name)) title = SDATA (ENCODE_UTF_8 (f->name));
f392e843
JD
506
507 if (title) gtk_window_set_title (GTK_WINDOW (wtop), title);
177c0ea7 508
f392e843
JD
509 FRAME_GTK_OUTER_WIDGET (f) = wtop;
510 FRAME_GTK_WIDGET (f) = wfixed;
511 f->output_data.x->vbox_widget = wvbox;
512
513 gtk_fixed_set_has_window (GTK_FIXED (wfixed), TRUE);
514
be786000 515 gtk_widget_set_size_request (wfixed, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
177c0ea7 516
f392e843
JD
517 gtk_container_add (GTK_CONTAINER (wtop), wvbox);
518 gtk_box_pack_end (GTK_BOX (wvbox), wfixed, TRUE, TRUE, 0);
519
520 if (FRAME_EXTERNAL_TOOL_BAR (f))
521 update_frame_tool_bar (f);
522
523 /* The tool bar is created but first there are no items in it.
524 This causes it to be zero height. Later items are added, but then
525 the frame is already mapped, so there is a "jumping" resize.
526 This makes geometry handling difficult, for example -0-0 will end
527 up in the wrong place as tool bar height has not been taken into account.
528 So we cheat a bit by setting a height that is what it will have
529 later on when tool bar items are added. */
b73e73bf 530 if (FRAME_EXTERNAL_TOOL_BAR (f) && FRAME_TOOLBAR_HEIGHT (f) == 0)
f392e843 531 FRAME_TOOLBAR_HEIGHT (f) = 34;
177c0ea7 532
7863d625
JD
533
534 /* We don't want this widget double buffered, because we draw on it
535 with regular X drawing primitives, so from a GTK/GDK point of
536 view, the widget is totally blank. When an expose comes, this
537 will make the widget blank, and then Emacs redraws it. This flickers
538 a lot, so we turn off double buffering. */
f392e843 539 gtk_widget_set_double_buffered (wfixed, FALSE);
7863d625
JD
540
541 /* Turning off double buffering above has the side effect of turning
542 it off also for its children (scroll bars). But we want those
543 to be double buffered to not flicker so handle expose manually. */
544 g_signal_connect (G_OBJECT (wfixed), "expose-event",
545 G_CALLBACK (xg_fixed_handle_expose), 0);
177c0ea7 546
f392e843
JD
547 /* GTK documents says use gtk_window_set_resizable. But then a user
548 can't shrink the window from its starting size. */
549 gtk_window_set_policy (GTK_WINDOW (wtop), TRUE, TRUE, TRUE);
550 gtk_window_set_wmclass (GTK_WINDOW (wtop),
551 SDATA (Vx_resource_name),
552 SDATA (Vx_resource_class));
553
554 /* Add callback to do nothing on WM_DELETE_WINDOW. The default in
555 GTK is to destroy the widget. We want Emacs to do that instead. */
556 g_signal_connect (G_OBJECT (wtop), "delete-event",
557 G_CALLBACK (gtk_true), 0);
177c0ea7 558
f392e843
JD
559 /* Convert our geometry parameters into a geometry string
560 and specify it.
561 GTK will itself handle calculating the real position this way. */
562 xg_set_geometry (f);
563
564 gtk_widget_add_events (wfixed,
565 GDK_POINTER_MOTION_MASK
566 | GDK_EXPOSURE_MASK
567 | GDK_BUTTON_PRESS_MASK
568 | GDK_BUTTON_RELEASE_MASK
569 | GDK_KEY_PRESS_MASK
570 | GDK_ENTER_NOTIFY_MASK
571 | GDK_LEAVE_NOTIFY_MASK
572 | GDK_FOCUS_CHANGE_MASK
573 | GDK_STRUCTURE_MASK
574 | GDK_VISIBILITY_NOTIFY_MASK);
575
576 /* Must realize the windows so the X window gets created. It is used
577 by callers of this function. */
578 gtk_widget_realize (wfixed);
579 FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
580
581 /* Since GTK clears its window by filling with the background color,
582 we must keep X and GTK background in sync. */
583 xg_pix_to_gcolor (wfixed, f->output_data.x->background_pixel, &bg);
584 gtk_widget_modify_bg (wfixed, GTK_STATE_NORMAL, &bg);
585
586 /* Also, do not let any background pixmap to be set, this looks very
587 bad as Emacs overwrites the background pixmap with its own idea
588 of background color. */
589 style = gtk_widget_get_modifier_style (wfixed);
590
591 /* Must use g_strdup because gtk_widget_modify_style does g_free. */
592 style->bg_pixmap_name[GTK_STATE_NORMAL] = g_strdup ("<none>");
593 gtk_widget_modify_style (wfixed, style);
177c0ea7 594
f392e843 595 /* GTK does not set any border, and they look bad with GTK. */
be786000
KS
596 f->border_width = 0;
597 f->internal_border_width = 0;
f392e843
JD
598
599 UNBLOCK_INPUT;
600
601 return 1;
602}
603
604/* Set the normal size hints for the window manager, for frame F.
605 FLAGS is the flags word to use--or 0 meaning preserve the flags
606 that the window now has.
607 If USER_POSITION is nonzero, we set the User Position
608 flag (this is useful when FLAGS is 0). */
609void
610x_wm_set_size_hint (f, flags, user_position)
611 FRAME_PTR f;
612 long flags;
613 int user_position;
614{
615 if (FRAME_GTK_OUTER_WIDGET (f))
616 {
617 /* Must use GTK routines here, otherwise GTK resets the size hints
618 to its own defaults. */
619 GdkGeometry size_hints;
620 gint hint_flags = 0;
621 int base_width, base_height;
622 int min_rows = 0, min_cols = 0;
be786000 623 int win_gravity = f->win_gravity;
177c0ea7 624
f392e843
JD
625 if (flags)
626 {
627 memset (&size_hints, 0, sizeof (size_hints));
628 f->output_data.x->size_hints = size_hints;
629 f->output_data.x->hint_flags = hint_flags;
630 }
631 else
be786000 632 flags = f->size_hint_flags;
177c0ea7 633
f392e843
JD
634 size_hints = f->output_data.x->size_hints;
635 hint_flags = f->output_data.x->hint_flags;
636
637 hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
be786000
KS
638 size_hints.width_inc = FRAME_COLUMN_WIDTH (f);
639 size_hints.height_inc = FRAME_LINE_HEIGHT (f);
f392e843
JD
640
641 hint_flags |= GDK_HINT_BASE_SIZE;
be786000
KS
642 base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
643 base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
f392e843
JD
644 + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
645
646 check_frame_size (f, &min_rows, &min_cols);
647
648 size_hints.base_width = base_width;
649 size_hints.base_height = base_height;
650 size_hints.min_width = base_width + min_cols * size_hints.width_inc;
651 size_hints.min_height = base_height + min_rows * size_hints.height_inc;
652
177c0ea7 653
f392e843
JD
654 /* These currently have a one to one mapping with the X values, but I
655 don't think we should rely on that. */
656 hint_flags |= GDK_HINT_WIN_GRAVITY;
657 size_hints.win_gravity = 0;
658 if (win_gravity == NorthWestGravity)
659 size_hints.win_gravity = GDK_GRAVITY_NORTH_WEST;
660 else if (win_gravity == NorthGravity)
661 size_hints.win_gravity = GDK_GRAVITY_NORTH;
662 else if (win_gravity == NorthEastGravity)
663 size_hints.win_gravity = GDK_GRAVITY_NORTH_EAST;
664 else if (win_gravity == WestGravity)
665 size_hints.win_gravity = GDK_GRAVITY_WEST;
666 else if (win_gravity == CenterGravity)
667 size_hints.win_gravity = GDK_GRAVITY_CENTER;
668 else if (win_gravity == EastGravity)
669 size_hints.win_gravity = GDK_GRAVITY_EAST;
670 else if (win_gravity == SouthWestGravity)
671 size_hints.win_gravity = GDK_GRAVITY_SOUTH_WEST;
672 else if (win_gravity == SouthGravity)
673 size_hints.win_gravity = GDK_GRAVITY_SOUTH;
674 else if (win_gravity == SouthEastGravity)
675 size_hints.win_gravity = GDK_GRAVITY_SOUTH_EAST;
676 else if (win_gravity == StaticGravity)
677 size_hints.win_gravity = GDK_GRAVITY_STATIC;
678
679 if (flags & PPosition) hint_flags |= GDK_HINT_POS;
680 if (flags & USPosition) hint_flags |= GDK_HINT_USER_POS;
681 if (flags & USSize) hint_flags |= GDK_HINT_USER_SIZE;
682
683 if (user_position)
684 {
685 hint_flags &= ~GDK_HINT_POS;
686 hint_flags |= GDK_HINT_USER_POS;
687 }
688
689 BLOCK_INPUT;
690
691 gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
692 FRAME_GTK_OUTER_WIDGET (f),
693 &size_hints,
694 hint_flags);
695
696 f->output_data.x->size_hints = size_hints;
697 f->output_data.x->hint_flags = hint_flags;
698 UNBLOCK_INPUT;
699 }
700}
701
702/* Change background color of a frame.
703 Since GTK uses the background colour to clear the window, we must
704 keep the GTK and X colors in sync.
705 F is the frame to change,
706 BG is the pixel value to change to. */
707void
708xg_set_background_color (f, bg)
709 FRAME_PTR f;
710 unsigned long bg;
711{
712 if (FRAME_GTK_WIDGET (f))
713 {
714 GdkColor gdk_bg;
715
716 BLOCK_INPUT;
717 xg_pix_to_gcolor (FRAME_GTK_WIDGET (f), bg, &gdk_bg);
718 gtk_widget_modify_bg (FRAME_GTK_WIDGET (f), GTK_STATE_NORMAL, &gdk_bg);
719 UNBLOCK_INPUT;
720 }
721}
722
723
724\f
725/***********************************************************************
726 Dialog functions
727 ***********************************************************************/
728/* Return the dialog title to use for a dialog of type KEY.
729 This is the encoding used by lwlib. We use the same for GTK. */
730static char *
731get_dialog_title (char key)
732{
733 char *title = "";
177c0ea7 734
f392e843
JD
735 switch (key) {
736 case 'E': case 'e':
737 title = "Error";
738 break;
739
740 case 'I': case 'i':
741 title = "Information";
742 break;
743
744 case 'L': case 'l':
745 title = "Prompt";
746 break;
747
748 case 'P': case 'p':
749 title = "Prompt";
750 break;
751
752 case 'Q': case 'q':
753 title = "Question";
754 break;
755 }
756
757 return title;
758}
759
760/* Callback for dialogs that get WM_DELETE_WINDOW. We pop down
761 the dialog, but return TRUE so the event does not propagate further
762 in GTK. This prevents GTK from destroying the dialog widget automatically
763 and we can always destrou the widget manually, regardles of how
764 it was popped down (button press or WM_DELETE_WINDOW).
765 W is the dialog widget.
766 EVENT is the GdkEvent that represents WM_DELETE_WINDOW (not used).
767 user_data is NULL (not used).
768
769 Returns TRUE to end propagation of event. */
770static gboolean
771dialog_delete_callback (w, event, user_data)
772 GtkWidget *w;
773 GdkEvent *event;
774 gpointer user_data;
775{
776 gtk_widget_unmap (w);
777 return TRUE;
778}
779
780/* Create a popup dialog window. See also xg_create_widget below.
781 WV is a widget_value describing the dialog.
782 SELECT_CB is the callback to use when a button has been pressed.
783 DEACTIVATE_CB is the callback to use when the dialog pops down.
784
785 Returns the GTK dialog widget. */
786static GtkWidget *
787create_dialog (wv, select_cb, deactivate_cb)
788 widget_value *wv;
789 GCallback select_cb;
790 GCallback deactivate_cb;
791{
792 char *title = get_dialog_title (wv->name[0]);
793 int total_buttons = wv->name[1] - '0';
794 int right_buttons = wv->name[4] - '0';
795 int left_buttons;
796 int button_nr = 0;
797 int button_spacing = 10;
798 GtkWidget *wdialog = gtk_dialog_new ();
799 widget_value *item;
800 GtkBox *cur_box;
801 GtkWidget *wvbox;
802 GtkWidget *whbox_up;
803 GtkWidget *whbox_down;
804
805 /* If the number of buttons is greater than 4, make two rows of buttons
806 instead. This looks better. */
807 int make_two_rows = total_buttons > 4;
808
809 if (right_buttons == 0) right_buttons = total_buttons/2;
810 left_buttons = total_buttons - right_buttons;
811
812 gtk_window_set_title (GTK_WINDOW (wdialog), title);
813 gtk_widget_set_name (wdialog, "emacs-dialog");
814
815 cur_box = GTK_BOX (GTK_DIALOG (wdialog)->action_area);
816
817 if (make_two_rows)
818 {
819 wvbox = gtk_vbox_new (TRUE, button_spacing);
820 whbox_up = gtk_hbox_new (FALSE, 0);
821 whbox_down = gtk_hbox_new (FALSE, 0);
822
823 gtk_box_pack_start (cur_box, wvbox, FALSE, FALSE, 0);
824 gtk_box_pack_start (GTK_BOX (wvbox), whbox_up, FALSE, FALSE, 0);
825 gtk_box_pack_start (GTK_BOX (wvbox), whbox_down, FALSE, FALSE, 0);
826
827 cur_box = GTK_BOX (whbox_up);
828 }
829
830 g_signal_connect (G_OBJECT (wdialog), "delete-event",
831 G_CALLBACK (dialog_delete_callback), 0);
177c0ea7 832
f392e843
JD
833 if (deactivate_cb)
834 {
835 g_signal_connect (G_OBJECT (wdialog), "close", deactivate_cb, 0);
836 g_signal_connect (G_OBJECT (wdialog), "response", deactivate_cb, 0);
837 }
838
839 for (item = wv->contents; item; item = item->next)
840 {
841 char *utf8_label = get_utf8_string (item->value);
842 GtkWidget *w;
843 GtkRequisition req;
844
0a1d6de0 845 if (item->name && strcmp (item->name, "message") == 0)
f392e843
JD
846 {
847 /* This is the text part of the dialog. */
848 w = gtk_label_new (utf8_label);
849 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
850 gtk_label_new (""),
851 FALSE, FALSE, 0);
852 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (wdialog)->vbox), w,
853 TRUE, TRUE, 0);
854 gtk_misc_set_alignment (GTK_MISC (w), 0.1, 0.5);
855
856 /* Try to make dialog look better. Must realize first so
857 the widget can calculate the size it needs. */
858 gtk_widget_realize (w);
859 gtk_widget_size_request (w, &req);
860 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (wdialog)->vbox),
861 req.height);
0a1d6de0 862 if (item->value && strlen (item->value) > 0)
f392e843
JD
863 button_spacing = 2*req.width/strlen (item->value);
864 }
865 else
866 {
867 /* This is one button to add to the dialog. */
4b1b4443 868 w = gtk_button_new_with_label (utf8_label);
f392e843
JD
869 if (! item->enabled)
870 gtk_widget_set_sensitive (w, FALSE);
871 if (select_cb)
872 g_signal_connect (G_OBJECT (w), "clicked",
873 select_cb, item->call_data);
874
875 gtk_box_pack_start (cur_box, w, TRUE, TRUE, button_spacing);
876 if (++button_nr == left_buttons)
877 {
878 if (make_two_rows)
879 cur_box = GTK_BOX (whbox_down);
880 else
881 gtk_box_pack_start (cur_box,
882 gtk_label_new (""),
883 TRUE, TRUE,
884 button_spacing);
885 }
886 }
887
888 if (utf8_label && utf8_label != item->value)
889 g_free (utf8_label);
890 }
891
892 return wdialog;
893}
894
895
896enum
897{
898 XG_FILE_NOT_DONE,
899 XG_FILE_OK,
900 XG_FILE_CANCEL,
901 XG_FILE_DESTROYED,
902};
903
904/* Callback function invoked when the Ok button is pressed in
905 a file dialog.
906 W is the file dialog widget,
907 ARG points to an integer where we record what has happend. */
908static void
909xg_file_sel_ok (w, arg)
910 GtkWidget *w;
911 gpointer arg;
912{
913 *(int*)arg = XG_FILE_OK;
914}
915
916/* Callback function invoked when the Cancel button is pressed in
917 a file dialog.
918 W is the file dialog widget,
919 ARG points to an integer where we record what has happend. */
920static void
921xg_file_sel_cancel (w, arg)
922 GtkWidget *w;
923 gpointer arg;
924{
925 *(int*)arg = XG_FILE_CANCEL;
926}
927
928/* Callback function invoked when the file dialog is destroyed (i.e.
929 popped down). We must keep track of this, because if this
930 happens, GTK destroys the widget. But if for example, Ok is pressed,
931 the dialog is popped down, but the dialog widget is not destroyed.
932 W is the file dialog widget,
933 ARG points to an integer where we record what has happend. */
934static void
935xg_file_sel_destroy (w, arg)
936 GtkWidget *w;
937 gpointer arg;
938{
939 *(int*)arg = XG_FILE_DESTROYED;
940}
941
942/* Read a file name from the user using a file dialog.
943 F is the current frame.
944 PROMPT is a prompt to show to the user. May not be NULL.
945 DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
946 If MUSTMATCH_P is non-zero, the returned file name must be an existing
947 file.
948
949 Returns a file name or NULL if no file was selected.
950 The returned string must be freed by the caller. */
951char *
952xg_get_file_name (f, prompt, default_filename, mustmatch_p)
953 FRAME_PTR f;
954 char *prompt;
955 char *default_filename;
956 int mustmatch_p;
957{
958 GtkWidget *filewin;
959 GtkFileSelection *filesel;
960 int filesel_done = XG_FILE_NOT_DONE;
961 char *fn = 0;
177c0ea7 962
f392e843
JD
963 filewin = gtk_file_selection_new (prompt);
964 filesel = GTK_FILE_SELECTION (filewin);
965
966 gtk_widget_set_name (filewin, "emacs-filedialog");
967
968 gtk_window_set_transient_for (GTK_WINDOW (filewin),
969 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
970 gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
971
972 g_signal_connect (G_OBJECT (filesel->ok_button),
973 "clicked",
974 G_CALLBACK (xg_file_sel_ok),
975 &filesel_done);
976 g_signal_connect (G_OBJECT (filesel->cancel_button),
977 "clicked",
978 G_CALLBACK (xg_file_sel_cancel),
979 &filesel_done);
980 g_signal_connect (G_OBJECT (filesel),
981 "destroy",
982 G_CALLBACK (xg_file_sel_destroy),
983 &filesel_done);
984
985 if (default_filename)
986 gtk_file_selection_set_filename (filesel, default_filename);
987
988 if (mustmatch_p)
989 {
990 /* The selection_entry part of filesel is not documented. */
991 gtk_widget_set_sensitive (filesel->selection_entry, FALSE);
992 gtk_file_selection_hide_fileop_buttons (filesel);
993 }
994
177c0ea7 995
f392e843 996 gtk_widget_show_all (filewin);
177c0ea7 997
f392e843
JD
998 while (filesel_done == XG_FILE_NOT_DONE)
999 gtk_main_iteration ();
1000
1001 if (filesel_done == XG_FILE_OK)
1002 fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
1003
1004 if (filesel_done != XG_FILE_DESTROYED)
1005 gtk_widget_destroy (filewin);
1006
1007 return fn;
1008}
1009
1010\f
1011/***********************************************************************
1012 Menu functions.
1013 ***********************************************************************/
1014
1015/* The name of menu items that can be used for citomization. Since GTK
1016 RC files are very crude and primitive, we have to set this on all
1017 menu item names so a user can easily cutomize menu items. */
1018
1019#define MENU_ITEM_NAME "emacs-menuitem"
1020
1021
1022/* Linked list of all allocated struct xg_menu_cb_data. Used for marking
1023 during GC. The next member points to the items. */
1024static xg_list_node xg_menu_cb_list;
1025
1026/* Linked list of all allocated struct xg_menu_item_cb_data. Used for marking
1027 during GC. The next member points to the items. */
1028static xg_list_node xg_menu_item_cb_list;
1029
1030/* Allocate and initialize CL_DATA if NULL, otherwise increase ref_count.
1031 F is the frame CL_DATA will be initialized for.
1032 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1033
1034 The menu bar and all sub menus under the menu bar in a frame
1035 share the same structure, hence the reference count.
177c0ea7 1036
f392e843
JD
1037 Returns CL_DATA if CL_DATA is not NULL, or a pointer to a newly
1038 allocated xg_menu_cb_data if CL_DATA is NULL. */
1039static xg_menu_cb_data *
1040make_cl_data (cl_data, f, highlight_cb)
1041 xg_menu_cb_data *cl_data;
1042 FRAME_PTR f;
1043 GCallback highlight_cb;
1044{
1045 if (! cl_data)
1046 {
1047 cl_data = (xg_menu_cb_data*) xmalloc (sizeof (*cl_data));
1048 cl_data->f = f;
1049 cl_data->menu_bar_vector = f->menu_bar_vector;
1050 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1051 cl_data->highlight_cb = highlight_cb;
1052 cl_data->ref_count = 0;
1053
1054 xg_list_insert (&xg_menu_cb_list, &cl_data->ptrs);
1055 }
1056
1057 cl_data->ref_count++;
1058
1059 return cl_data;
1060}
1061
1062/* Update CL_DATA with values from frame F and with HIGHLIGHT_CB.
1063 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1064
1065 When the menu bar is updated, menu items may have been added and/or
1066 removed, so menu_bar_vector and menu_bar_items_used change. We must
1067 then update CL_DATA since it is used to determine which menu
1068 item that is invoked in the menu.
1069 HIGHLIGHT_CB could change, there is no check that the same
1070 function is given when modifying a menu bar as was given when
1071 creating the menu bar. */
1072static void
1073update_cl_data (cl_data, f, highlight_cb)
1074 xg_menu_cb_data *cl_data;
1075 FRAME_PTR f;
1076 GCallback highlight_cb;
1077{
1078 if (cl_data)
1079 {
1080 cl_data->f = f;
1081 cl_data->menu_bar_vector = f->menu_bar_vector;
1082 cl_data->menu_bar_items_used = f->menu_bar_items_used;
1083 cl_data->highlight_cb = highlight_cb;
1084 }
1085}
1086
1087/* Decrease reference count for CL_DATA.
1088 If reference count is zero, free CL_DATA. */
1089static void
1090unref_cl_data (cl_data)
1091 xg_menu_cb_data *cl_data;
1092{
1093 if (cl_data && cl_data->ref_count > 0)
1094 {
1095 cl_data->ref_count--;
1096 if (cl_data->ref_count == 0)
1097 {
1098 xg_list_remove (&xg_menu_cb_list, &cl_data->ptrs);
1099 xfree (cl_data);
1100 }
1101 }
1102}
1103
1104/* Function that marks all lisp data during GC. */
1105void
1106xg_mark_data ()
1107{
1108 xg_list_node *iter;
1109
1110 for (iter = xg_menu_cb_list.next; iter; iter = iter->next)
631f2082 1111 mark_object (((xg_menu_cb_data *) iter)->menu_bar_vector);
f392e843
JD
1112
1113 for (iter = xg_menu_item_cb_list.next; iter; iter = iter->next)
1114 {
1115 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data *) iter;
1116
1117 if (! NILP (cb_data->help))
631f2082 1118 mark_object (cb_data->help);
f392e843
JD
1119 }
1120}
1121
1122
1123/* Callback called when a menu item is destroyed. Used to free data.
1124 W is the widget that is being destroyed (not used).
1125 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */
1126static void
1127menuitem_destroy_callback (w, client_data)
1128 GtkWidget *w;
1129 gpointer client_data;
1130{
1131 if (client_data)
1132 {
1133 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1134 xg_list_remove (&xg_menu_item_cb_list, &data->ptrs);
1135 xfree (data);
1136 }
1137}
1138
1139/* Callback called when the pointer enters/leaves a menu item.
1140 W is the menu item.
1141 EVENT is either an enter event or leave event.
1142 CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.
1143
1144 Returns FALSE to tell GTK to keep processing this event. */
1145static gboolean
1146menuitem_highlight_callback (w, event, client_data)
1147 GtkWidget *w;
1148 GdkEventCrossing *event;
1149 gpointer client_data;
1150{
1151 if (client_data)
1152 {
1153 xg_menu_item_cb_data *data = (xg_menu_item_cb_data*) client_data;
1154 gpointer call_data = event->type == GDK_LEAVE_NOTIFY ? 0 : client_data;
177c0ea7 1155
f392e843
JD
1156 if (! NILP (data->help) && data->cl_data->highlight_cb)
1157 {
1158 GtkCallback func = (GtkCallback) data->cl_data->highlight_cb;
1159 (*func) (w, call_data);
1160 }
1161 }
1162
1163 return FALSE;
1164}
1165
1166/* Callback called when a menu is destroyed. Used to free data.
1167 W is the widget that is being destroyed (not used).
1168 CLIENT_DATA points to the xg_menu_cb_data associated with W. */
1169static void
1170menu_destroy_callback (w, client_data)
1171 GtkWidget *w;
1172 gpointer client_data;
1173{
1174 unref_cl_data ((xg_menu_cb_data*) client_data);
1175}
1176
1177/* Callback called when a menu does a grab or ungrab. That means the
1178 menu has been activated or deactivated.
1179 Used to start a timer so the small timeout the menus in GTK uses before
1180 popping down a menu is seen by Emacs (see xg_process_timeouts above).
1181 W is the widget that does the grab (not used).
1182 UNGRAB_P is TRUE if this is an ungrab, FALSE if it is a grab.
1183 CLIENT_DATA is NULL (not used). */
1184static void
1185menu_grab_callback (GtkWidget *widget,
1186 gboolean ungrab_p,
1187 gpointer client_data)
1188{
1189 /* Keep track of total number of grabs. */
1190 static int cnt;
1191
1192 if (ungrab_p) cnt--;
1193 else cnt++;
1194
1195 if (cnt > 0 && ! xg_timer) xg_start_timer ();
1196 else if (cnt == 0 && xg_timer) xg_stop_timer ();
1197}
1198
1199/* Make a GTK widget that contains both UTF8_LABEL and UTF8_KEY (both
1200 must be non-NULL) and can be inserted into a menu item.
1201
1202 Returns the GtkHBox. */
1203static GtkWidget *
1204make_widget_for_menu_item (utf8_label, utf8_key)
1205 char *utf8_label;
1206 char *utf8_key;
1207{
1208 GtkWidget *wlbl;
1209 GtkWidget *wkey;
1210 GtkWidget *wbox;
177c0ea7 1211
f392e843 1212 wbox = gtk_hbox_new (FALSE, 0);
4b1b4443 1213 wlbl = gtk_label_new (utf8_label);
f392e843
JD
1214 wkey = gtk_label_new (utf8_key);
1215
1216 gtk_misc_set_alignment (GTK_MISC (wlbl), 0.0, 0.5);
1217 gtk_misc_set_alignment (GTK_MISC (wkey), 0.0, 0.5);
177c0ea7 1218
f392e843
JD
1219 gtk_box_pack_start (GTK_BOX (wbox), wlbl, TRUE, TRUE, 0);
1220 gtk_box_pack_start (GTK_BOX (wbox), wkey, FALSE, FALSE, 0);
1221
1222 gtk_widget_set_name (wlbl, MENU_ITEM_NAME);
1223 gtk_widget_set_name (wkey, MENU_ITEM_NAME);
7863d625 1224 gtk_widget_set_name (wbox, MENU_ITEM_NAME);
f392e843
JD
1225
1226 return wbox;
1227}
1228
1229/* Make and return a menu item widget with the key to the right.
1230 UTF8_LABEL is the text for the menu item (GTK uses UTF8 internally).
1231 UTF8_KEY is the text representing the key binding.
1232 ITEM is the widget_value describing the menu item.
177c0ea7 1233
f392e843
JD
1234 GROUP is an in/out parameter. If the menu item to be created is not
1235 part of any radio menu group, *GROUP contains NULL on entry and exit.
1236 If the menu item to be created is part of a radio menu group, on entry
1237 *GROUP contains the group to use, or NULL if this is the first item
1238 in the group. On exit, *GROUP contains the radio item group.
1239
1240 Unfortunately, keys don't line up as nicely as in Motif,
1241 but the MacOS X version doesn't either, so I guess that is OK. */
1242static GtkWidget *
1243make_menu_item (utf8_label, utf8_key, item, group)
1244 char *utf8_label;
1245 char *utf8_key;
1246 widget_value *item;
1247 GSList **group;
1248{
1249 GtkWidget *w;
1250 GtkWidget *wtoadd = 0;
177c0ea7 1251
adcb132c
JD
1252 /* It has been observed that some menu items have a NULL name field.
1253 This will lead to this function being called with a NULL utf8_label.
1254 GTK crashes on that so we set a blank label. Why there is a NULL
1255 name remains to be investigated. */
1256 if (! utf8_label) utf8_label = " ";
1257
f392e843
JD
1258 if (utf8_key)
1259 wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
177c0ea7 1260
f392e843
JD
1261 if (item->button_type == BUTTON_TYPE_TOGGLE)
1262 {
1263 *group = NULL;
1264 if (utf8_key) w = gtk_check_menu_item_new ();
4b1b4443 1265 else w = gtk_check_menu_item_new_with_label (utf8_label);
f392e843
JD
1266 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), item->selected);
1267 }
1268 else if (item->button_type == BUTTON_TYPE_RADIO)
1269 {
1270 if (utf8_key) w = gtk_radio_menu_item_new (*group);
4b1b4443 1271 else w = gtk_radio_menu_item_new_with_label (*group, utf8_label);
f392e843
JD
1272 *group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (w));
1273 if (item->selected)
1274 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1275 }
1276 else
1277 {
1278 *group = NULL;
1279 if (utf8_key) w = gtk_menu_item_new ();
4b1b4443 1280 else w = gtk_menu_item_new_with_label (utf8_label);
f392e843 1281 }
177c0ea7 1282
f392e843
JD
1283 if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
1284 if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
1285
1286 return w;
1287}
1288
5fd6f727 1289/* Return non-zero if LABEL specifies a separator (GTK only has one
f392e843
JD
1290 separator type) */
1291static int
5fd6f727
JD
1292xg_separator_p (char *label)
1293{
1294 if (! label) return 0;
1295 else if (strlen (label) > 3
1296 && strncmp (label, "--", 2) == 0
1297 && label[2] != '-')
1298 {
1299 static char* separator_names[] = {
1300 "space",
1301 "no-line",
1302 "single-line",
1303 "double-line",
1304 "single-dashed-line",
1305 "double-dashed-line",
1306 "shadow-etched-in",
1307 "shadow-etched-out",
1308 "shadow-etched-in-dash",
1309 "shadow-etched-out-dash",
1310 "shadow-double-etched-in",
1311 "shadow-double-etched-out",
1312 "shadow-double-etched-in-dash",
1313 "shadow-double-etched-out-dash",
1314 0,
1315 };
1316
1317 int i;
1318
1319 label += 2;
1320 for (i = 0; separator_names[i]; ++i)
1321 if (strcmp (label, separator_names[i]) == 0)
1322 return 1;
1323 }
1324 else
1325 {
1326 /* Old-style separator, maybe. It's a separator if it contains
1327 only dashes. */
1328 while (*label == '-')
1329 ++label;
1330 if (*label == 0) return 1;
1331 }
0a1d6de0 1332
5fd6f727 1333 return 0;
f392e843
JD
1334}
1335
da18b5ac
JD
1336static int xg_detached_menus;
1337
1338/* Returns non-zero if there are detached menus. */
1339int
1340xg_have_tear_offs ()
1341{
1342 return xg_detached_menus > 0;
1343}
f392e843
JD
1344
1345/* Callback invoked when a detached menu window is removed. Here we
da18b5ac 1346 decrease the xg_detached_menus count.
f392e843 1347 WIDGET is the top level window that is removed (the parent of the menu).
da18b5ac
JD
1348 CLIENT_DATA is not used. */
1349static void
1350tearoff_remove (widget, client_data)
f392e843 1351 GtkWidget *widget;
f392e843
JD
1352 gpointer client_data;
1353{
da18b5ac 1354 if (xg_detached_menus > 0) --xg_detached_menus;
f392e843
JD
1355}
1356
da18b5ac
JD
1357/* Callback invoked when a menu is detached. It increases the
1358 xg_detached_menus count.
f392e843 1359 WIDGET is the GtkTearoffMenuItem.
177c0ea7 1360 CLIENT_DATA is not used. */
f392e843
JD
1361static void
1362tearoff_activate (widget, client_data)
1363 GtkWidget *widget;
1364 gpointer client_data;
1365{
1366 GtkWidget *menu = gtk_widget_get_parent (widget);
da18b5ac
JD
1367 if (gtk_menu_get_tearoff_state (GTK_MENU (menu)))
1368 {
1369 ++xg_detached_menus;
1370 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (widget)),
1371 "destroy",
1372 G_CALLBACK (tearoff_remove), 0);
1373 }
f392e843
JD
1374}
1375
f392e843 1376
f392e843
JD
1377/* Create a menu item widget, and connect the callbacks.
1378 ITEM decribes the menu item.
1379 F is the frame the created menu belongs to.
1380 SELECT_CB is the callback to use when a menu item is selected.
1381 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1382 CL_DATA points to the callback data to be used for this menu.
1383 GROUP is an in/out parameter. If the menu item to be created is not
1384 part of any radio menu group, *GROUP contains NULL on entry and exit.
1385 If the menu item to be created is part of a radio menu group, on entry
1386 *GROUP contains the group to use, or NULL if this is the first item
1387 in the group. On exit, *GROUP contains the radio item group.
1388
1389 Returns the created GtkWidget. */
1390static GtkWidget *
1391xg_create_one_menuitem (item, f, select_cb, highlight_cb, cl_data, group)
1392 widget_value *item;
1393 FRAME_PTR f;
1394 GCallback select_cb;
1395 GCallback highlight_cb;
1396 xg_menu_cb_data *cl_data;
1397 GSList **group;
1398{
1399 char *utf8_label;
1400 char *utf8_key;
1401 GtkWidget *w;
1402 xg_menu_item_cb_data *cb_data;
1403
1404 utf8_label = get_utf8_string (item->name);
1405 utf8_key = get_utf8_string (item->key);
1406
1407 w = make_menu_item (utf8_label, utf8_key, item, group);
1408
1409 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1410 if (utf8_key && utf8_key != item->key) g_free (utf8_key);
1411
1412 cb_data = xmalloc (sizeof (xg_menu_item_cb_data));
1413
1414 xg_list_insert (&xg_menu_item_cb_list, &cb_data->ptrs);
1415
1416 cb_data->unhighlight_id = cb_data->highlight_id = cb_data->select_id = 0;
1417 cb_data->help = item->help;
1418 cb_data->cl_data = cl_data;
1419 cb_data->call_data = item->call_data;
177c0ea7 1420
f392e843
JD
1421 g_signal_connect (G_OBJECT (w),
1422 "destroy",
1423 G_CALLBACK (menuitem_destroy_callback),
1424 cb_data);
1425
1426 /* Put cb_data in widget, so we can get at it when modifying menubar */
1427 g_object_set_data (G_OBJECT (w), XG_ITEM_DATA, cb_data);
1428
1429 /* final item, not a submenu */
1430 if (item->call_data && ! item->contents)
1431 {
1432 if (select_cb)
1433 cb_data->select_id
1434 = g_signal_connect (G_OBJECT (w), "activate", select_cb, cb_data);
1435 }
1436
1437 if (! NILP (item->help) && highlight_cb)
1438 {
1439 /* We use enter/leave notify instead of select/deselect because
1440 select/deselect doesn't go well with detached menus. */
1441 cb_data->highlight_id
1442 = g_signal_connect (G_OBJECT (w),
1443 "enter-notify-event",
1444 G_CALLBACK (menuitem_highlight_callback),
1445 cb_data);
1446 cb_data->unhighlight_id
1447 = g_signal_connect (G_OBJECT (w),
1448 "leave-notify-event",
1449 G_CALLBACK (menuitem_highlight_callback),
1450 cb_data);
1451 }
1452
1453 return w;
1454}
1455
9d6194d6
AS
1456static GtkWidget *create_menus P_ ((widget_value *, FRAME_PTR, GCallback,
1457 GCallback, GCallback, int, int, int,
1458 GtkWidget *, xg_menu_cb_data *, char *));
1459
f392e843
JD
1460/* Create a full menu tree specified by DATA.
1461 F is the frame the created menu belongs to.
1462 SELECT_CB is the callback to use when a menu item is selected.
1463 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
1464 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1465 POP_UP_P is non-zero if we shall create a popup menu.
1466 MENU_BAR_P is non-zero if we shall create a menu bar.
1467 ADD_TEAROFF_P is non-zero if we shall add a teroff menu item. Ignored
1468 if MENU_BAR_P is non-zero.
1469 TOPMENU is the topmost GtkWidget that others shall be placed under.
1470 It may be NULL, in that case we create the appropriate widget
1471 (menu bar or menu item depending on POP_UP_P and MENU_BAR_P)
1472 CL_DATA is the callback data we shall use for this menu, or NULL
1473 if we haven't set the first callback yet.
1474 NAME is the name to give to the top level menu if this function
1475 creates it. May be NULL to not set any name.
1476
1477 Returns the top level GtkWidget. This is TOPLEVEL if TOPLEVEL is
1478 not NULL.
1479
1480 This function calls itself to create submenus. */
1481
1482static GtkWidget *
1483create_menus (data, f, select_cb, deactivate_cb, highlight_cb,
1484 pop_up_p, menu_bar_p, add_tearoff_p, topmenu, cl_data, name)
1485 widget_value *data;
1486 FRAME_PTR f;
1487 GCallback select_cb;
1488 GCallback deactivate_cb;
1489 GCallback highlight_cb;
1490 int pop_up_p;
1491 int menu_bar_p;
1492 int add_tearoff_p;
1493 GtkWidget *topmenu;
1494 xg_menu_cb_data *cl_data;
1495 char *name;
1496{
1497 widget_value *item;
1498 GtkWidget *wmenu = topmenu;
1499 GSList *group = NULL;
1500
1501 if (! topmenu)
1502 {
1503 if (! menu_bar_p) wmenu = gtk_menu_new ();
1504 else wmenu = gtk_menu_bar_new ();
1505
1506 /* Put cl_data on the top menu for easier access. */
1507 cl_data = make_cl_data (cl_data, f, highlight_cb);
1508 g_object_set_data (G_OBJECT (wmenu), XG_FRAME_DATA, (gpointer)cl_data);
1509 g_signal_connect (G_OBJECT (wmenu), "destroy",
1510 G_CALLBACK (menu_destroy_callback), cl_data);
177c0ea7 1511
f392e843
JD
1512 if (name)
1513 gtk_widget_set_name (wmenu, name);
1514
1515 if (deactivate_cb)
1516 g_signal_connect (G_OBJECT (wmenu),
1517 "deactivate", deactivate_cb, 0);
1518
1519 g_signal_connect (G_OBJECT (wmenu),
1520 "grab-notify", G_CALLBACK (menu_grab_callback), 0);
1521 }
177c0ea7 1522
f392e843
JD
1523 if (! menu_bar_p && add_tearoff_p)
1524 {
1525 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
1526 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), tearoff);
1527
1528 g_signal_connect (G_OBJECT (tearoff), "activate",
1529 G_CALLBACK (tearoff_activate), 0);
1530 }
1531
1532 for (item = data; item; item = item->next)
1533 {
1534 GtkWidget *w;
177c0ea7 1535
f392e843
JD
1536 if (pop_up_p && !item->contents && !item->call_data
1537 && !xg_separator_p (item->name))
1538 {
1539 char *utf8_label;
1540 /* A title for a popup. We do the same as GTK does when
1541 creating titles, but it does not look good. */
1542 group = NULL;
1543 utf8_label = get_utf8_string (item->name);
1544
1545 gtk_menu_set_title (GTK_MENU (wmenu), utf8_label);
4b1b4443 1546 w = gtk_menu_item_new_with_label (utf8_label);
f392e843
JD
1547 gtk_widget_set_sensitive (w, FALSE);
1548 if (utf8_label && utf8_label != item->name) g_free (utf8_label);
1549 }
1550 else if (xg_separator_p (item->name))
1551 {
1552 group = NULL;
1553 /* GTK only have one separator type. */
1554 w = gtk_separator_menu_item_new ();
1555 }
1556 else
1557 {
1558 w = xg_create_one_menuitem (item,
1559 f,
1560 item->contents ? 0 : select_cb,
1561 highlight_cb,
1562 cl_data,
1563 &group);
1564
1565 if (item->contents)
1566 {
1567 GtkWidget *submenu = create_menus (item->contents,
1568 f,
1569 select_cb,
1570 deactivate_cb,
1571 highlight_cb,
1572 0,
1573 0,
da18b5ac 1574 add_tearoff_p,
f392e843
JD
1575 0,
1576 cl_data,
1577 0);
1578 gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), submenu);
1579 }
f392e843
JD
1580 }
1581
1582 gtk_menu_shell_append (GTK_MENU_SHELL (wmenu), w);
1583 gtk_widget_set_name (w, MENU_ITEM_NAME);
1584 }
1585
1586 return wmenu;
1587}
1588
1589/* Create a menubar, popup menu or dialog, depending on the TYPE argument.
1590 TYPE can be "menubar", "popup" for popup menu, or "dialog" for a dialog
1591 with some text and buttons.
1592 F is the frame the created item belongs to.
1593 NAME is the name to use for the top widget.
1594 VAL is a widget_value structure describing items to be created.
1595 SELECT_CB is the callback to use when a menu item is selected or
1596 a dialog button is pressed.
1597 DEACTIVATE_CB is the callback to use when an item is deactivated.
1598 For a menu, when a sub menu is not shown anymore, for a dialog it is
1599 called when the dialog is popped down.
1600 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1601
1602 Returns the widget created. */
1603GtkWidget *
1604xg_create_widget (type, name, f, val,
1605 select_cb, deactivate_cb, highlight_cb)
1606 char *type;
1607 char *name;
1608 FRAME_PTR f;
1609 widget_value *val;
1610 GCallback select_cb;
1611 GCallback deactivate_cb;
1612 GCallback highlight_cb;
1613{
1614 GtkWidget *w = 0;
da18b5ac
JD
1615 int menu_bar_p = strcmp (type, "menubar") == 0;
1616 int pop_up_p = strcmp (type, "popup") == 0;
1617
f392e843
JD
1618 if (strcmp (type, "dialog") == 0)
1619 {
1620 w = create_dialog (val, select_cb, deactivate_cb);
1621 gtk_window_set_transient_for (GTK_WINDOW (w),
1622 GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
1623 gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
1624
1625 if (w)
1626 gtk_widget_set_name (w, "emacs-dialog");
1627 }
da18b5ac 1628 else if (menu_bar_p || pop_up_p)
f392e843
JD
1629 {
1630 w = create_menus (val->contents,
1631 f,
1632 select_cb,
1633 deactivate_cb,
1634 highlight_cb,
da18b5ac
JD
1635 pop_up_p,
1636 menu_bar_p,
1637 menu_bar_p,
f392e843
JD
1638 0,
1639 0,
1640 name);
1641
1642 /* Set the cursor to an arrow for popup menus when they are mapped.
1643 This is done by default for menu bar menus. */
da18b5ac 1644 if (pop_up_p)
f392e843
JD
1645 {
1646 /* Must realize so the GdkWindow inside the widget is created. */
1647 gtk_widget_realize (w);
1648 xg_set_cursor (w, &xg_left_ptr_cursor);
1649 }
1650 }
1651 else
1652 {
1653 fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
1654 type);
1655 }
1656
1657 return w;
1658}
1659
0a1d6de0 1660/* Return the label for menu item WITEM. */
f392e843
JD
1661static const char *
1662xg_get_menu_item_label (witem)
1663 GtkMenuItem *witem;
1664{
1665 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
1666 return gtk_label_get_label (wlabel);
1667}
1668
0a1d6de0 1669/* Return non-zero if the menu item WITEM has the text LABEL. */
f392e843
JD
1670static int
1671xg_item_label_same_p (witem, label)
1672 GtkMenuItem *witem;
1673 char *label;
1674{
0a1d6de0 1675 int is_same = 0;
f392e843 1676 char *utf8_label = get_utf8_string (label);
0a1d6de0
JD
1677 const char *old_label = witem ? xg_get_menu_item_label (witem) : 0;
1678
1679 if (! old_label && ! utf8_label)
1680 is_same = 1;
1681 else if (old_label && utf8_label)
1682 is_same = strcmp (utf8_label, old_label) == 0;
1683
1684 if (utf8_label && utf8_label != label) g_free (utf8_label);
f392e843
JD
1685
1686 return is_same;
1687}
1688
1689/* Remove widgets in LIST from container WCONT. */
1690static void
1691remove_from_container (wcont, list)
1692 GtkWidget *wcont;
1693 GList *list;
1694{
f392e843
JD
1695 GList *iter;
1696
49853a4d 1697 for (iter = list; iter; iter = g_list_next (iter))
f392e843
JD
1698 {
1699 GtkWidget *w = GTK_WIDGET (iter->data);
1700
1701 /* Add a ref to w so we can explicitly destroy it later. */
1702 gtk_widget_ref (w);
1703 gtk_container_remove (GTK_CONTAINER (wcont), w);
177c0ea7 1704
f392e843
JD
1705 /* If there is a menu under this widget that has been detached,
1706 there is a reference to it, and just removing w from the
1707 container does not destroy the submenu. By explicitly
1708 destroying w we make sure the submenu is destroyed, thus
1709 removing the detached window also if there was one. */
1710 gtk_widget_destroy (w);
1711 }
f392e843
JD
1712}
1713
1714/* Update the top level names in MENUBAR (i.e. not submenus).
1715 F is the frame the menu bar belongs to.
49853a4d
JD
1716 *LIST is a list with the current menu bar names (menu item widgets).
1717 ITER is the item within *LIST that shall be updated.
1718 POS is the numerical position, starting at 0, of ITER in *LIST.
f392e843
JD
1719 VAL describes what the menu bar shall look like after the update.
1720 SELECT_CB is the callback to use when a menu item is selected.
1721 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
49853a4d 1722 CL_DATA points to the callback data to be used for this menu bar.
f392e843
JD
1723
1724 This function calls itself to walk through the menu bar names. */
1725static void
49853a4d
JD
1726xg_update_menubar (menubar, f, list, iter, pos, val,
1727 select_cb, highlight_cb, cl_data)
f392e843
JD
1728 GtkWidget *menubar;
1729 FRAME_PTR f;
49853a4d
JD
1730 GList **list;
1731 GList *iter;
1732 int pos;
f392e843
JD
1733 widget_value *val;
1734 GCallback select_cb;
1735 GCallback highlight_cb;
1736 xg_menu_cb_data *cl_data;
1737{
49853a4d 1738 if (! iter && ! val)
f392e843 1739 return;
49853a4d 1740 else if (iter && ! val)
f392e843 1741 {
49853a4d
JD
1742 /* Item(s) have been removed. Remove all remaining items. */
1743 remove_from_container (menubar, iter);
f392e843
JD
1744
1745 /* All updated. */
1746 val = 0;
49853a4d 1747 iter = 0;
f392e843 1748 }
49853a4d 1749 else if (! iter && val)
f392e843
JD
1750 {
1751 /* Item(s) added. Add all new items in one call. */
1752 create_menus (val, f, select_cb, 0, highlight_cb,
1753 0, 1, 0, menubar, cl_data, 0);
1754
1755 /* All updated. */
1756 val = 0;
49853a4d 1757 iter = 0;
f392e843 1758 }
49853a4d
JD
1759 /* Below this neither iter or val is NULL */
1760 else if (xg_item_label_same_p (GTK_MENU_ITEM (iter->data), val->name))
f392e843
JD
1761 {
1762 /* This item is still the same, check next item. */
1763 val = val->next;
49853a4d
JD
1764 iter = g_list_next (iter);
1765 ++pos;
f392e843
JD
1766 }
1767 else /* This item is changed. */
1768 {
49853a4d 1769 GtkMenuItem *witem = GTK_MENU_ITEM (iter->data);
f392e843 1770 GtkMenuItem *witem2 = 0;
f392e843 1771 int val_in_menubar = 0;
49853a4d
JD
1772 int iter_in_new_menubar = 0;
1773 GList *iter2;
f392e843
JD
1774 widget_value *cur;
1775
f392e843 1776 /* See if the changed entry (val) is present later in the menu bar */
49853a4d
JD
1777 for (iter2 = iter;
1778 iter2 && ! val_in_menubar;
1779 iter2 = g_list_next (iter2))
f392e843 1780 {
49853a4d 1781 witem2 = GTK_MENU_ITEM (iter2->data);
f392e843
JD
1782 val_in_menubar = xg_item_label_same_p (witem2, val->name);
1783 }
1784
49853a4d 1785 /* See if the current entry (iter) is present later in the
f392e843 1786 specification for the new menu bar. */
49853a4d
JD
1787 for (cur = val; cur && ! iter_in_new_menubar; cur = cur->next)
1788 iter_in_new_menubar = xg_item_label_same_p (witem, cur->name);
f392e843 1789
49853a4d 1790 if (val_in_menubar && ! iter_in_new_menubar)
f392e843 1791 {
49853a4d
JD
1792 int nr = pos;
1793
f392e843
JD
1794 /* This corresponds to:
1795 Current: A B C
1796 New: A C
1797 Remove B. */
177c0ea7 1798
f392e843
JD
1799 gtk_widget_ref (GTK_WIDGET (witem));
1800 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem));
1801 gtk_widget_destroy (GTK_WIDGET (witem));
1802
1803 /* Must get new list since the old changed. */
49853a4d
JD
1804 g_list_free (*list);
1805 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1806 while (nr-- > 0) iter = g_list_next (iter);
f392e843 1807 }
49853a4d 1808 else if (! val_in_menubar && ! iter_in_new_menubar)
f392e843
JD
1809 {
1810 /* This corresponds to:
1811 Current: A B C
1812 New: A X C
1813 Rename B to X. This might seem to be a strange thing to do,
1814 since if there is a menu under B it will be totally wrong for X.
1815 But consider editing a C file. Then there is a C-mode menu
1816 (corresponds to B above).
1817 If then doing C-x C-f the minibuf menu (X above) replaces the
1818 C-mode menu. When returning from the minibuffer, we get
1819 back the C-mode menu. Thus we do:
1820 Rename B to X (C-mode to minibuf menu)
1821 Rename X to B (minibuf to C-mode menu).
1822 If the X menu hasn't been invoked, the menu under B
1823 is up to date when leaving the minibuffer. */
1824 GtkLabel *wlabel = GTK_LABEL (gtk_bin_get_child (GTK_BIN (witem)));
1825 char *utf8_label = get_utf8_string (val->name);
da18b5ac 1826 GtkWidget *submenu = gtk_menu_item_get_submenu (witem);
177c0ea7 1827
4b1b4443 1828 gtk_label_set_text (wlabel, utf8_label);
f392e843 1829
da18b5ac
JD
1830 /* If this item has a submenu that has been detached, change
1831 the title in the WM decorations also. */
1832 if (submenu && gtk_menu_get_tearoff_state (GTK_MENU (submenu)))
1833 /* Set the title of the detached window. */
1834 gtk_menu_set_title (GTK_MENU (submenu), utf8_label);
1835
49853a4d 1836 iter = g_list_next (iter);
f392e843 1837 val = val->next;
49853a4d 1838 ++pos;
f392e843 1839 }
49853a4d 1840 else if (! val_in_menubar && iter_in_new_menubar)
f392e843
JD
1841 {
1842 /* This corresponds to:
1843 Current: A B C
1844 New: A X B C
1845 Insert X. */
1846
49853a4d 1847 int nr = pos;
f392e843
JD
1848 GList *group = 0;
1849 GtkWidget *w = xg_create_one_menuitem (val,
1850 f,
1851 select_cb,
1852 highlight_cb,
1853 cl_data,
1854 &group);
1855
1856 gtk_widget_set_name (w, MENU_ITEM_NAME);
1857 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar), w, pos);
1858
49853a4d
JD
1859 g_list_free (*list);
1860 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1861 while (nr-- > 0) iter = g_list_next (iter);
1862 iter = g_list_next (iter);
f392e843 1863 val = val->next;
49853a4d 1864 ++pos;
f392e843 1865 }
49853a4d 1866 else /* if (val_in_menubar && iter_in_new_menubar) */
f392e843 1867 {
49853a4d 1868 int nr = pos;
f392e843
JD
1869 /* This corresponds to:
1870 Current: A B C
1871 New: A C B
1872 Move C before B */
1873
1874 gtk_widget_ref (GTK_WIDGET (witem2));
1875 gtk_container_remove (GTK_CONTAINER (menubar), GTK_WIDGET (witem2));
1876 gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),
1877 GTK_WIDGET (witem2), pos);
1878 gtk_widget_unref (GTK_WIDGET (witem2));
1879
49853a4d
JD
1880 g_list_free (*list);
1881 *list = iter = gtk_container_get_children (GTK_CONTAINER (menubar));
1882 while (nr-- > 0) iter = g_list_next (iter);
f392e843 1883 val = val->next;
49853a4d 1884 ++pos;
f392e843 1885 }
f392e843
JD
1886 }
1887
1888 /* Update the rest of the menu bar. */
49853a4d
JD
1889 xg_update_menubar (menubar, f, list, iter, pos, val,
1890 select_cb, highlight_cb, cl_data);
f392e843
JD
1891}
1892
1893/* Update the menu item W so it corresponds to VAL.
1894 SELECT_CB is the callback to use when a menu item is selected.
1895 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
1896 CL_DATA is the data to set in the widget for menu invokation. */
1897static void
1898xg_update_menu_item (val, w, select_cb, highlight_cb, cl_data)
1899 widget_value *val;
1900 GtkWidget *w;
1901 GCallback select_cb;
1902 GCallback highlight_cb;
1903 xg_menu_cb_data *cl_data;
1904{
1905 GtkWidget *wchild;
1906 GtkLabel *wlbl = 0;
1907 GtkLabel *wkey = 0;
1908 char *utf8_label;
1909 char *utf8_key;
0a1d6de0
JD
1910 const char *old_label = 0;
1911 const char *old_key = 0;
f392e843 1912 xg_menu_item_cb_data *cb_data;
177c0ea7
JB
1913
1914 wchild = gtk_bin_get_child (GTK_BIN (w));
f392e843
JD
1915 utf8_label = get_utf8_string (val->name);
1916 utf8_key = get_utf8_string (val->key);
1917
1918 /* See if W is a menu item with a key. See make_menu_item above. */
1919 if (GTK_IS_HBOX (wchild))
1920 {
1921 GList *list = gtk_container_get_children (GTK_CONTAINER (wchild));
1922
1923 wlbl = GTK_LABEL (list->data);
1924 wkey = GTK_LABEL (list->next->data);
49853a4d
JD
1925 g_list_free (list);
1926
f392e843
JD
1927 if (! utf8_key)
1928 {
1929 /* Remove the key and keep just the label. */
1930 gtk_widget_ref (GTK_WIDGET (wlbl));
1931 gtk_container_remove (GTK_CONTAINER (w), wchild);
1932 gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (wlbl));
1933 wkey = 0;
1934 }
49853a4d 1935
f392e843
JD
1936 }
1937 else /* Just a label. */
1938 {
1939 wlbl = GTK_LABEL (wchild);
1940
1941 /* Check if there is now a key. */
1942 if (utf8_key)
1943 {
1944 GtkWidget *wtoadd = make_widget_for_menu_item (utf8_label, utf8_key);
1945 GList *list = gtk_container_get_children (GTK_CONTAINER (wtoadd));
49853a4d 1946
f392e843
JD
1947 wlbl = GTK_LABEL (list->data);
1948 wkey = GTK_LABEL (list->next->data);
49853a4d 1949 g_list_free (list);
f392e843
JD
1950
1951 gtk_container_remove (GTK_CONTAINER (w), wchild);
1952 gtk_container_add (GTK_CONTAINER (w), wtoadd);
1953 }
1954 }
1955
177c0ea7 1956
0a1d6de0
JD
1957 if (wkey) old_key = gtk_label_get_label (wkey);
1958 if (wlbl) old_label = gtk_label_get_label (wlbl);
177c0ea7 1959
0a1d6de0 1960 if (wkey && utf8_key && (! old_key || strcmp (utf8_key, old_key) != 0))
f392e843
JD
1961 gtk_label_set_text (wkey, utf8_key);
1962
0a1d6de0 1963 if (! old_label || strcmp (utf8_label, old_label) != 0)
4b1b4443 1964 gtk_label_set_text (wlbl, utf8_label);
f392e843 1965
0a1d6de0
JD
1966 if (utf8_key && utf8_key != val->key) g_free (utf8_key);
1967 if (utf8_label && utf8_label != val->name) g_free (utf8_label);
177c0ea7 1968
f392e843
JD
1969 if (! val->enabled && GTK_WIDGET_SENSITIVE (w))
1970 gtk_widget_set_sensitive (w, FALSE);
1971 else if (val->enabled && ! GTK_WIDGET_SENSITIVE (w))
1972 gtk_widget_set_sensitive (w, TRUE);
1973
1974 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (w),
1975 XG_ITEM_DATA);
1976 if (cb_data)
1977 {
1978 cb_data->call_data = val->call_data;
1979 cb_data->help = val->help;
1980 cb_data->cl_data = cl_data;
177c0ea7 1981
f392e843
JD
1982 /* We assume the callback functions don't change. */
1983 if (val->call_data && ! val->contents)
1984 {
1985 /* This item shall have a select callback. */
1986 if (! cb_data->select_id)
1987 cb_data->select_id
1988 = g_signal_connect (G_OBJECT (w), "activate",
1989 select_cb, cb_data);
1990 }
1991 else if (cb_data->select_id)
1992 {
1993 g_signal_handler_disconnect (w, cb_data->select_id);
1994 cb_data->select_id = 0;
1995 }
1996
1997 if (NILP (cb_data->help))
1998 {
1999 /* Shall not have help. Remove if any existed previously. */
2000 if (cb_data->highlight_id)
2001 {
2002 g_signal_handler_disconnect (G_OBJECT (w),
2003 cb_data->highlight_id);
2004 cb_data->highlight_id = 0;
2005 }
2006 if (cb_data->unhighlight_id)
2007 {
2008 g_signal_handler_disconnect (G_OBJECT (w),
2009 cb_data->unhighlight_id);
2010 cb_data->unhighlight_id = 0;
2011 }
2012 }
2013 else if (! cb_data->highlight_id && highlight_cb)
2014 {
2015 /* Have help now, but didn't previously. Add callback. */
2016 cb_data->highlight_id
2017 = g_signal_connect (G_OBJECT (w),
2018 "enter-notify-event",
2019 G_CALLBACK (menuitem_highlight_callback),
2020 cb_data);
2021 cb_data->unhighlight_id
2022 = g_signal_connect (G_OBJECT (w),
2023 "leave-notify-event",
2024 G_CALLBACK (menuitem_highlight_callback),
2025 cb_data);
2026 }
2027 }
2028}
2029
2030/* Update the toggle menu item W so it corresponds to VAL. */
2031static void
2032xg_update_toggle_item (val, w)
2033 widget_value *val;
2034 GtkWidget *w;
2035{
2036 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2037}
2038
2039/* Update the radio menu item W so it corresponds to VAL. */
2040static void
2041xg_update_radio_item (val, w)
2042 widget_value *val;
2043 GtkWidget *w;
2044{
2045 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), val->selected);
2046}
2047
2048/* Update the sub menu SUBMENU and all its children so it corresponds to VAL.
2049 SUBMENU may be NULL, in that case a new menu is created.
2050 F is the frame the menu bar belongs to.
2051 VAL describes the contents of the menu bar.
2052 SELECT_CB is the callback to use when a menu item is selected.
2053 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2054 HIGHLIGHT_CB is the callback to call when entering/leaving menu items.
2055 CL_DATA is the call back data to use for any newly created items.
2056
2057 Returns the updated submenu widget, that is SUBMENU unless SUBMENU
2058 was NULL. */
2059
2060static GtkWidget *
2061xg_update_submenu (submenu, f, val,
2062 select_cb, deactivate_cb, highlight_cb, cl_data)
2063 GtkWidget *submenu;
2064 FRAME_PTR f;
2065 widget_value *val;
2066 GCallback select_cb;
2067 GCallback deactivate_cb;
2068 GCallback highlight_cb;
2069 xg_menu_cb_data *cl_data;
2070{
2071 GtkWidget *newsub = submenu;
2072 GList *list = 0;
2073 GList *iter;
2074 widget_value *cur;
2075 int has_tearoff_p = 0;
2076 GList *first_radio = 0;
177c0ea7 2077
f392e843
JD
2078 if (submenu)
2079 list = gtk_container_get_children (GTK_CONTAINER (submenu));
177c0ea7 2080
f392e843
JD
2081 for (cur = val, iter = list;
2082 cur && iter;
2083 iter = g_list_next (iter), cur = cur->next)
2084 {
2085 GtkWidget *w = GTK_WIDGET (iter->data);
2086
2087 /* Skip tearoff items, they have no counterpart in val. */
2088 if (GTK_IS_TEAROFF_MENU_ITEM (w))
2089 {
2090 has_tearoff_p = 1;
2091 iter = g_list_next (iter);
2092 if (iter) w = GTK_WIDGET (iter->data);
2093 else break;
2094 }
2095
2096 /* Remember first radio button in a group. If we get a mismatch in
2097 a radio group we must rebuild the whole group so that the connections
2098 in GTK becomes correct. */
2099 if (cur->button_type == BUTTON_TYPE_RADIO && ! first_radio)
2100 first_radio = iter;
2101 else if (cur->button_type != BUTTON_TYPE_RADIO
2102 && ! GTK_IS_RADIO_MENU_ITEM (w))
2103 first_radio = 0;
2104
2105 if (GTK_IS_SEPARATOR_MENU_ITEM (w))
2106 {
2107 if (! xg_separator_p (cur->name))
2108 break;
2109 }
2110 else if (GTK_IS_CHECK_MENU_ITEM (w))
2111 {
2112 if (cur->button_type != BUTTON_TYPE_TOGGLE)
2113 break;
2114 xg_update_toggle_item (cur, w);
2115 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2116 }
2117 else if (GTK_IS_RADIO_MENU_ITEM (w))
2118 {
2119 if (cur->button_type != BUTTON_TYPE_RADIO)
2120 break;
2121 xg_update_radio_item (cur, w);
2122 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2123 }
2124 else if (GTK_IS_MENU_ITEM (w))
2125 {
2126 GtkMenuItem *witem = GTK_MENU_ITEM (w);
2127 GtkWidget *sub;
2128
2129 if (cur->button_type != BUTTON_TYPE_NONE ||
2130 xg_separator_p (cur->name))
2131 break;
2132
2133 xg_update_menu_item (cur, w, select_cb, highlight_cb, cl_data);
2134
2135 sub = gtk_menu_item_get_submenu (witem);
2136 if (sub && ! cur->contents)
2137 {
2138 /* Not a submenu anymore. */
2139 gtk_widget_ref (sub);
2140 gtk_menu_item_remove_submenu (witem);
2141 gtk_widget_destroy (sub);
2142 }
2143 else if (cur->contents)
2144 {
2145 GtkWidget *nsub;
2146
2147 nsub = xg_update_submenu (sub, f, cur->contents,
2148 select_cb, deactivate_cb,
2149 highlight_cb, cl_data);
2150
2151 /* If this item just became a submenu, we must set it. */
2152 if (nsub != sub)
2153 gtk_menu_item_set_submenu (witem, nsub);
2154 }
2155 }
2156 else
2157 {
2158 /* Structural difference. Remove everything from here and down
2159 in SUBMENU. */
2160 break;
2161 }
2162 }
2163
2164 /* Remove widgets from first structual change. */
2165 if (iter)
2166 {
2167 /* If we are adding new menu items below, we must remove from
2168 first radio button so that radio groups become correct. */
2169 if (cur && first_radio) remove_from_container (submenu, first_radio);
2170 else remove_from_container (submenu, iter);
2171 }
177c0ea7 2172
f392e843
JD
2173 if (cur)
2174 {
2175 /* More items added. Create them. */
2176 newsub = create_menus (cur,
2177 f,
2178 select_cb,
2179 deactivate_cb,
2180 highlight_cb,
2181 0,
2182 0,
2183 ! has_tearoff_p,
2184 submenu,
2185 cl_data,
2186 0);
2187 }
177c0ea7 2188
49853a4d
JD
2189 if (list) g_list_free (list);
2190
f392e843
JD
2191 return newsub;
2192}
2193
2194/* Update the MENUBAR.
2195 F is the frame the menu bar belongs to.
2196 VAL describes the contents of the menu bar.
2197 If DEEP_P is non-zero, rebuild all but the top level menu names in
2198 the MENUBAR. If DEEP_P is zero, just rebuild the names in the menubar.
2199 SELECT_CB is the callback to use when a menu item is selected.
2200 DEACTIVATE_CB is the callback to use when a sub menu is not shown anymore.
2201 HIGHLIGHT_CB is the callback to call when entering/leaving menu items. */
2202void
2203xg_modify_menubar_widgets (menubar, f, val, deep_p,
2204 select_cb, deactivate_cb, highlight_cb)
2205 GtkWidget *menubar;
2206 FRAME_PTR f;
2207 widget_value *val;
2208 int deep_p;
2209 GCallback select_cb;
2210 GCallback deactivate_cb;
2211 GCallback highlight_cb;
2212{
2213 xg_menu_cb_data *cl_data;
2214 GList *list = gtk_container_get_children (GTK_CONTAINER (menubar));
f392e843
JD
2215
2216 if (! list) return;
177c0ea7 2217
f392e843
JD
2218 cl_data = (xg_menu_cb_data*) g_object_get_data (G_OBJECT (menubar),
2219 XG_FRAME_DATA);
2220
da18b5ac
JD
2221 xg_update_menubar (menubar, f, &list, list, 0, val->contents,
2222 select_cb, highlight_cb, cl_data);
2223
2224 if (deep_p);
f392e843
JD
2225 {
2226 widget_value *cur;
2227
2228 /* Update all sub menus.
da18b5ac 2229 We must keep the submenus (GTK menu item widgets) since the
f392e843
JD
2230 X Window in the XEvent that activates the menu are those widgets. */
2231
2232 /* Update cl_data, menu_item things in F may have changed. */
2233 update_cl_data (cl_data, f, highlight_cb);
2234
2235 for (cur = val->contents; cur; cur = cur->next)
2236 {
49853a4d 2237 GList *iter;
f392e843
JD
2238 GtkWidget *sub = 0;
2239 GtkWidget *newsub;
2240 GtkMenuItem *witem;
2241
2242 /* Find sub menu that corresponds to val and update it. */
2243 for (iter = list ; iter; iter = g_list_next (iter))
2244 {
2245 witem = GTK_MENU_ITEM (iter->data);
2246 if (xg_item_label_same_p (witem, cur->name))
2247 {
2248 sub = gtk_menu_item_get_submenu (witem);
2249 break;
2250 }
2251 }
177c0ea7 2252
f392e843
JD
2253 newsub = xg_update_submenu (sub,
2254 f,
2255 cur->contents,
2256 select_cb,
2257 deactivate_cb,
2258 highlight_cb,
2259 cl_data);
2260 /* sub may still be NULL. If we just updated non deep and added
2261 a new menu bar item, it has no sub menu yet. So we set the
2262 newly created sub menu under witem. */
2263 if (newsub != sub)
2264 gtk_menu_item_set_submenu (witem, newsub);
f392e843
JD
2265 }
2266 }
2267
49853a4d 2268 g_list_free (list);
f392e843
JD
2269 gtk_widget_show_all (menubar);
2270}
2271
2272/* Recompute all the widgets of frame F, when the menu bar has been
2273 changed. Value is non-zero if widgets were updated. */
2274
2275int
2276xg_update_frame_menubar (f)
2277 FRAME_PTR f;
2278{
2279 struct x_output *x = f->output_data.x;
2280 GtkRequisition req;
177c0ea7 2281
f392e843
JD
2282 if (!x->menubar_widget || GTK_WIDGET_MAPPED (x->menubar_widget))
2283 return 0;
2284
2285 BLOCK_INPUT;
2286
2287 gtk_box_pack_start (GTK_BOX (x->vbox_widget), x->menubar_widget,
2288 FALSE, FALSE, 0);
2289 gtk_box_reorder_child (GTK_BOX (x->vbox_widget), x->menubar_widget, 0);
2290
2291 gtk_widget_show_all (x->menubar_widget);
2292 gtk_widget_size_request (x->menubar_widget, &req);
2293
2294 FRAME_MENUBAR_HEIGHT (f) = req.height;
2295
2296 /* The height has changed, resize outer widget and set columns
2297 rows to what we had before adding the menu bar. */
be786000 2298 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
177c0ea7 2299
f392e843
JD
2300 SET_FRAME_GARBAGED (f);
2301 UNBLOCK_INPUT;
a8303f92
AS
2302
2303 return 1;
f392e843
JD
2304}
2305
2306/* Get rid of the menu bar of frame F, and free its storage.
2307 This is used when deleting a frame, and when turning off the menu bar. */
2308
2309void
2310free_frame_menubar (f)
2311 FRAME_PTR f;
2312{
2313 struct x_output *x = f->output_data.x;
2314
2315 if (x->menubar_widget)
2316 {
2317 BLOCK_INPUT;
2318
2319 gtk_container_remove (GTK_CONTAINER (x->vbox_widget), x->menubar_widget);
2320 /* The menubar and its children shall be deleted when removed from
2321 the container. */
2322 x->menubar_widget = 0;
2323 FRAME_MENUBAR_HEIGHT (f) = 0;
2324
2325 /* The height has changed, resize outer widget and set columns
2326 rows to what we had before removing the menu bar. */
be786000 2327 xg_resize_outer_widget (f, FRAME_COLS (f), FRAME_LINES (f));
f392e843
JD
2328
2329 SET_FRAME_GARBAGED (f);
2330 UNBLOCK_INPUT;
2331 }
2332}
2333
2334
2335\f
2336/***********************************************************************
2337 Scroll bar functions
2338 ***********************************************************************/
2339
2340
2341/* Setting scroll bar values invokes the callback. Use this variable
2342 to indicate that callback should do nothing. */
2343int xg_ignore_gtk_scrollbar;
2344
f392e843
JD
2345/* SET_SCROLL_BAR_X_WINDOW assumes the second argument fits in
2346 32 bits. But we want to store pointers, and they may be larger
2347 than 32 bits. Keep a mapping from integer index to widget pointers
2348 to get around the 32 bit limitation. */
2349static struct
2350{
2351 GtkWidget **widgets;
2352 int max_size;
2353 int used;
81e302ef 2354} id_to_widget;
f392e843
JD
2355
2356/* Grow this much every time we need to allocate more */
2357#define ID_TO_WIDGET_INCR 32
2358
2359/* Store the widget pointer W in id_to_widget and return the integer index. */
2360static int
2361xg_store_widget_in_map (w)
2362 GtkWidget *w;
2363{
2364 int i;
2365
2366 if (id_to_widget.max_size == id_to_widget.used)
2367 {
2368 int new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR;
2369
2370 id_to_widget.widgets = xrealloc (id_to_widget.widgets,
2371 sizeof (GtkWidget *)*new_size);
2372
2373 for (i = id_to_widget.max_size; i < new_size; ++i)
2374 id_to_widget.widgets[i] = 0;
2375 id_to_widget.max_size = new_size;
2376 }
2377
2378 /* Just loop over the array and find a free place. After all,
2379 how many scroll bars are we creating? Should be a small number.
2380 The check above guarantees we will find a free place. */
2381 for (i = 0; i < id_to_widget.max_size; ++i)
2382 {
2383 if (! id_to_widget.widgets[i])
2384 {
2385 id_to_widget.widgets[i] = w;
2386 ++id_to_widget.used;
2387
2388 return i;
2389 }
2390 }
2391
2392 /* Should never end up here */
2393 abort ();
2394}
2395
2396/* Remove pointer at IDX from id_to_widget.
2397 Called when scroll bar is destroyed. */
2398static void
2399xg_remove_widget_from_map (idx)
2400 int idx;
2401{
2402 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2403 {
2404 id_to_widget.widgets[idx] = 0;
2405 --id_to_widget.used;
2406 }
2407}
2408
2409/* Get the widget pointer at IDX from id_to_widget. */
2410static GtkWidget *
2411xg_get_widget_from_map (idx)
2412 int idx;
2413{
2414 if (idx < id_to_widget.max_size && id_to_widget.widgets[idx] != 0)
2415 return id_to_widget.widgets[idx];
2416
2417 return 0;
2418}
2419
3a8a22fc
JD
2420/* Return the scrollbar id for X Window WID.
2421 Return -1 if WID not in id_to_widget. */
2422int
2423xg_get_scroll_id_for_window (wid)
2424 Window wid;
2425{
2426 int idx;
2427 GtkWidget *w;
2428
2429 w = xg_win_to_widget (wid);
2430
2431 if (w)
2432 {
2433 for (idx = 0; idx < id_to_widget.max_size; ++idx)
2434 if (id_to_widget.widgets[idx] == w)
2435 return idx;
2436 }
2437
2438 return -1;
2439}
2440
f392e843
JD
2441/* Callback invoked when scroll bar WIDGET is destroyed.
2442 DATA is the index into id_to_widget for WIDGET.
cea9be54 2443 We free pointer to last scroll bar values here and remove the index. */
f392e843
JD
2444static void
2445xg_gtk_scroll_destroy (widget, data)
2446 GtkWidget *widget;
2447 gpointer data;
2448{
2449 gpointer p;
2450 int id = (int)data;
177c0ea7 2451
f392e843
JD
2452 p = g_object_get_data (G_OBJECT (widget), XG_LAST_SB_DATA);
2453 if (p) xfree (p);
2454 xg_remove_widget_from_map (id);
2455}
2456
2457/* Callback for button press/release events. Used to start timer so that
2458 the scroll bar repetition timer in GTK gets handeled.
17097258 2459 Also, sets bar->dragging to Qnil when dragging (button release) is done.
f392e843
JD
2460 WIDGET is the scroll bar widget the event is for (not used).
2461 EVENT contains the event.
17097258 2462 USER_DATA points to the struct scrollbar structure.
f392e843
JD
2463
2464 Returns FALSE to tell GTK that it shall continue propagate the event
2465 to widgets. */
2466static gboolean
2467scroll_bar_button_cb (widget, event, user_data)
2468 GtkWidget *widget;
2469 GdkEventButton *event;
2470 gpointer user_data;
2471{
2472 if (event->type == GDK_BUTTON_PRESS && ! xg_timer)
2473 xg_start_timer ();
17097258
JD
2474 else if (event->type == GDK_BUTTON_RELEASE)
2475 {
2476 struct scroll_bar *bar = (struct scroll_bar *) user_data;
2477 if (xg_timer) xg_stop_timer ();
2478 bar->dragging = Qnil;
2479 }
2480
f392e843
JD
2481 return FALSE;
2482}
2483
2484/* Create a scroll bar widget for frame F. Store the scroll bar
2485 in BAR.
2486 SCROLL_CALLBACK is the callback to invoke when the value of the
2487 bar changes.
2488 SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used
2489 to set resources for the widget. */
2490void
2491xg_create_scroll_bar (f, bar, scroll_callback, scroll_bar_name)
2492 FRAME_PTR f;
2493 struct scroll_bar *bar;
2494 GCallback scroll_callback;
2495 char *scroll_bar_name;
2496{
2497 GtkWidget *wscroll;
2498 GtkObject *vadj;
2499 int scroll_id;
177c0ea7 2500
f392e843
JD
2501 /* Page, step increment values are not so important here, they
2502 will be corrected in x_set_toolkit_scroll_bar_thumb. */
2503 vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
2504 0.1, 0.1, 0.1);
2505
2506 wscroll = gtk_vscrollbar_new (GTK_ADJUSTMENT (vadj));
2507 gtk_widget_set_name (wscroll, scroll_bar_name);
2508 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
177c0ea7 2509
f392e843 2510 scroll_id = xg_store_widget_in_map (wscroll);
177c0ea7 2511
f979dc05 2512 g_signal_connect (G_OBJECT (wscroll),
f392e843
JD
2513 "value-changed",
2514 scroll_callback,
f979dc05 2515 (gpointer) bar);
f392e843
JD
2516 g_signal_connect (G_OBJECT (wscroll),
2517 "destroy",
2518 G_CALLBACK (xg_gtk_scroll_destroy),
f979dc05 2519 (gpointer) scroll_id);
f392e843
JD
2520
2521 /* Connect to button press and button release to detect if any scroll bar
2522 has the pointer. */
2523 g_signal_connect (G_OBJECT (wscroll),
2524 "button-press-event",
2525 G_CALLBACK (scroll_bar_button_cb),
f979dc05 2526 (gpointer) bar);
f392e843
JD
2527 g_signal_connect (G_OBJECT (wscroll),
2528 "button-release-event",
2529 G_CALLBACK (scroll_bar_button_cb),
f979dc05 2530 (gpointer) bar);
177c0ea7 2531
f392e843 2532 gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget),
17097258 2533 wscroll, -1, -1);
f392e843
JD
2534
2535 /* Set the cursor to an arrow. */
2536 xg_set_cursor (wscroll, &xg_left_ptr_cursor);
2537
2538 SET_SCROLL_BAR_X_WINDOW (bar, scroll_id);
2539}
2540
2541/* Make the scroll bar represented by SCROLLBAR_ID visible. */
2542void
2543xg_show_scroll_bar (scrollbar_id)
2544 int scrollbar_id;
2545{
2546 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2547 if (w)
2548 gtk_widget_show (w);
2549}
2550
2551/* Remove the scroll bar represented by SCROLLBAR_ID from the frame F. */
2552void
2553xg_remove_scroll_bar (f, scrollbar_id)
2554 FRAME_PTR f;
2555 int scrollbar_id;
2556{
2557 GtkWidget *w = xg_get_widget_from_map (scrollbar_id);
2558 if (w)
2559 {
2560 gtk_widget_destroy (w);
2561 SET_FRAME_GARBAGED (f);
2562 }
2563}
2564
17097258
JD
2565/* Find left/top for widget W in GtkFixed widget WFIXED. */
2566static void
2567xg_find_top_left_in_fixed (w, wfixed, left, top)
2568 GtkWidget *w, *wfixed;
2569 int *left, *top;
2570{
2571 GList *iter;
2572
2573 for (iter = GTK_FIXED (wfixed)->children; iter; iter = g_list_next (iter))
2574 {
2575 GtkFixedChild *child = (GtkFixedChild *) iter->data;
2576
2577 if (child->widget == w)
2578 {
2579 *left = child->x;
2580 *top = child->y;
2581 return;
2582 }
2583 }
2584
2585 /* Shall never end up here. */
2586 abort ();
2587}
f392e843
JD
2588
2589/* Update the position of the vertical scroll bar represented by SCROLLBAR_ID
2590 in frame F.
2591 TOP/LEFT are the new pixel positions where the bar shall appear.
2592 WIDTH, HEIGHT is the size in pixels the bar shall have. */
2593void
7863d625
JD
2594xg_update_scrollbar_pos (f, scrollbar_id, top, left, width, height,
2595 real_left, canon_width)
f392e843
JD
2596 FRAME_PTR f;
2597 int scrollbar_id;
2598 int top;
2599 int left;
2600 int width;
2601 int height;
2602{
f392e843 2603
49853a4d 2604 GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
7863d625 2605
49853a4d
JD
2606 if (wscroll)
2607 {
cea9be54 2608 GtkWidget *wfixed = f->output_data.x->edit_widget;
17097258 2609 int winextra = canon_width > width ? (canon_width - width) / 2 : 0;
5fd6f727 2610 int bottom = top + height;
f392e843 2611
17097258
JD
2612 gint slider_width;
2613 int oldtop, oldleft, oldbottom;
2614 GtkRequisition req;
7863d625 2615
17097258
JD
2616 /* Get old values. */
2617 xg_find_top_left_in_fixed (wscroll, wfixed, &oldleft, &oldtop);
2618 gtk_widget_size_request (wscroll, &req);
2619 oldbottom = oldtop + req.height;
2620
2621 /* Scroll bars in GTK has a fixed width, so if we say width 16, it
2622 will only be its fixed width (14 is default) anyway, the rest is
2623 blank. We are drawing the mode line across scroll bars when
2624 the frame is split:
2625 |bar| |fringe|
2626 ----------------
2627 mode line
2628 ----------------
2629 |bar| |fringe|
2630
2631 When we "unsplit" the frame:
2632
2633 |bar| |fringe|
2634 -| |-| |
2635