xmenu.c 72 KB
Newer Older
Jim Blandy's avatar
Jim Blandy committed
1
/* X Communication module for terminals which understand the X protocol.
2 3 4

Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2011
  Free Software Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
5 6 7

This file is part of GNU Emacs.

8
GNU Emacs is free software: you can redistribute it and/or modify
Jim Blandy's avatar
Jim Blandy committed
9
it under the terms of the GNU General Public License as published by
10 11
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jim Blandy's avatar
Jim Blandy committed
12 13 14 15 16 17 18

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
19
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Jim Blandy's avatar
Jim Blandy committed
20

21
/* X pop-up deck-of-cards menu facility for GNU Emacs.
Jim Blandy's avatar
Jim Blandy committed
22 23 24 25 26 27
 *
 * Written by Jon Arnold and Roman Budzianowski
 * Mods and rewrite by Robert Krawitz
 *
 */

28 29 30
/* Modified by Fred Pierresteguy on December 93
   to make the popup menus and menubar use the Xt.  */

31 32
/* Rewritten for clarity and GC protection by rms in Feb 94.  */

33 34
#include <config.h>

Richard M. Stallman's avatar
Richard M. Stallman committed
35
#if 0  /* Why was this included?  And without syssignal.h?  */
Jim Blandy's avatar
Jim Blandy committed
36 37
/* On 4.3 this loses if it comes after xterm.h.  */
#include <signal.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
38
#endif
39 40

#include <stdio.h>
41
#include <setjmp.h>
42

Jim Blandy's avatar
Jim Blandy committed
43
#include "lisp.h"
44
#include "keyboard.h"
45
#include "keymap.h"
Jim Blandy's avatar
Jim Blandy committed
46
#include "frame.h"
47
#include "termhooks.h"
Jim Blandy's avatar
Jim Blandy committed
48
#include "window.h"
49
#include "blockinput.h"
Richard M. Stallman's avatar
Richard M. Stallman committed
50
#include "buffer.h"
51 52
#include "charset.h"
#include "coding.h"
53
#include "sysselect.h"
Jim Blandy's avatar
Jim Blandy committed
54

55 56 57 58
#ifdef MSDOS
#include "msdos.h"
#endif

Morten Welinder's avatar
Morten Welinder committed
59
#ifdef HAVE_X_WINDOWS
Jim Blandy's avatar
Jim Blandy committed
60 61 62
/* This may include sys/types.h, and that somehow loses
   if this is not done before the other system files.  */
#include "xterm.h"
Morten Welinder's avatar
Morten Welinder committed
63
#endif
Jim Blandy's avatar
Jim Blandy committed
64 65 66 67 68 69 70 71 72

/* Load sys/types.h if not already loaded.
   In some systems loading it twice is suicidal.  */
#ifndef makedev
#include <sys/types.h>
#endif

#include "dispextern.h"

Morten Welinder's avatar
Morten Welinder committed
73
#ifdef HAVE_X_WINDOWS
Dave Love's avatar
Dave Love committed
74 75
/*  Defining HAVE_MULTILINGUAL_MENU would mean that the toolkit menu
    code accepts the Emacs internal encoding.  */
76
#undef HAVE_MULTILINGUAL_MENU
77
#ifdef USE_X_TOOLKIT
78
#include "widget.h"
79 80 81 82
#include <X11/Xlib.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <X11/StringDefs.h>
83
#include <X11/Shell.h>
84
#ifdef USE_LUCID
85 86
#include "xsettings.h"
#include "../lwlib/xlwmenu.h"
Chong Yidong's avatar
Chong Yidong committed
87 88 89
#ifdef HAVE_XAW3D
#include <X11/Xaw3d/Paned.h>
#else /* !HAVE_XAW3D */
90
#include <X11/Xaw/Paned.h>
Chong Yidong's avatar
Chong Yidong committed
91
#endif /* HAVE_XAW3D */
92
#endif /* USE_LUCID */
93
#ifdef USE_MOTIF
Jan D's avatar
Jan D committed
94
#include "../lwlib/lwlib.h"
95
#endif
96
#else /* not USE_X_TOOLKIT */
Jan Djärv's avatar
Jan Djärv committed
97
#ifndef USE_GTK
98
#include "../oldXMenu/XMenu.h"
Jan Djärv's avatar
Jan Djärv committed
99
#endif
100 101
#endif /* not USE_X_TOOLKIT */
#endif /* HAVE_X_WINDOWS */
102

Andreas Schwab's avatar
Andreas Schwab committed
103 104 105 106 107 108
#ifdef USE_GTK
#include "gtkutil.h"
#endif

#include "menu.h"

Jim Blandy's avatar
Jim Blandy committed
109 110
#ifndef TRUE
#define TRUE 1
111
#endif /* no TRUE */
Jim Blandy's avatar
Jim Blandy committed
112

113
static Lisp_Object Qdebug_on_next_call;
114

115
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
116
static Lisp_Object xdialog_show (FRAME_PTR, int, Lisp_Object, Lisp_Object,
117
                                 const char **);
Gerd Moellmann's avatar
Ditto.  
Gerd Moellmann committed
118
#endif
119

120
/* Flag which when set indicates a dialog or menu has been posted by
121
   Xt on behalf of one of the widget sets.  */
Jan Djärv's avatar
Jan Djärv committed
122
static int popup_activated_flag;
123

124

Richard M. Stallman's avatar
Richard M. Stallman committed
125
#ifdef USE_X_TOOLKIT
126

127 128
static int next_menubar_widget_id;

129
/* Return the frame whose ->output_data.x->id equals ID, or 0 if none.  */
130

Richard M. Stallman's avatar
Richard M. Stallman committed
131
static struct frame *
132
menubar_id_to_frame (LWLIB_ID id)
Richard M. Stallman's avatar
Richard M. Stallman committed
133 134 135
{
  Lisp_Object tail, frame;
  FRAME_PTR f;
136

137
  for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
138
    {
139
      frame = XCAR (tail);
140
      if (!FRAMEP (frame))
Richard M. Stallman's avatar
Richard M. Stallman committed
141 142
        continue;
      f = XFRAME (frame);
143
      if (!FRAME_WINDOW_P (f))
Richard M. Stallman's avatar
Richard M. Stallman committed
144
	continue;
145
      if (f->output_data.x->id == id)
Richard M. Stallman's avatar
Richard M. Stallman committed
146
	return f;
147
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
148
  return 0;
149
}
Richard M. Stallman's avatar
Richard M. Stallman committed
150 151

#endif
152

153 154 155 156 157 158 159 160
#ifdef HAVE_X_WINDOWS
/* Return the mouse position in *X and *Y.  The coordinates are window
   relative for the edit window in frame F.
   This is for Fx_popup_menu.  The mouse_position_hook can not
   be used for X, as it returns window relative coordinates
   for the window where the mouse is in.  This could be the menu bar,
   the scroll bar or the edit window.  Fx_popup_menu needs to be
   sure it is the edit window.  */
161
void
162
mouse_position_for_popup (FRAME_PTR f, int *x, int *y)
163 164 165 166
{
  Window root, dummy_window;
  int dummy;

167 168 169
  if (! FRAME_X_P (f))
    abort ();

170
  BLOCK_INPUT;
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
  XQueryPointer (FRAME_X_DISPLAY (f),
                 DefaultRootWindow (FRAME_X_DISPLAY (f)),

                 /* The root window which contains the pointer.  */
                 &root,

                 /* Window pointer is on, not used  */
                 &dummy_window,

                 /* The position on that root window.  */
                 x, y,

                 /* x/y in dummy_window coordinates, not used.  */
                 &dummy, &dummy,

                 /* Modifier keys and pointer buttons, about which
                    we don't care.  */
                 (unsigned int *) &dummy);

  UNBLOCK_INPUT;

  /* xmenu_show expects window coordinates, not root window
     coordinates.  Translate.  */
195 196
  *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
  *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
197 198 199 200
}

#endif /* HAVE_X_WINDOWS */

201 202
#ifdef HAVE_MENUS

Paul Eggert's avatar
Paul Eggert committed
203
DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
Pavel Janík's avatar
Pavel Janík committed
204
       doc: /* Pop up a dialog box and return user's selection.
Gerd Moellmann's avatar
Gerd Moellmann committed
205 206 207 208 209 210
POSITION specifies which frame to use.
This is normally a mouse button event or a window or frame.
If POSITION is t, it means to use the frame the mouse is on.
The dialog box appears in the middle of the specified frame.

CONTENTS specifies the alternatives to display in the dialog box.
211
It is a list of the form (DIALOG ITEM1 ITEM2...).
Gerd Moellmann's avatar
Gerd Moellmann committed
212 213 214 215 216 217
Each ITEM is a cons cell (STRING . VALUE).
The return value is VALUE from the chosen item.

An ITEM may also be just a string--that makes a nonselectable item.
An ITEM may also be nil--that means to put all preceding items
on the left of the dialog box and all following items on the right.
218 219
\(By default, approximately half appear on each side.)

220 221 222
If HEADER is non-nil, the frame title for the box is "Information",
otherwise it is "Question".

223 224 225
If the user gets rid of the dialog box without making a valid choice,
for instance using the window manager, then this produces a quit and
`x-popup-dialog' does not return.  */)
Dan Nicolaescu's avatar
Dan Nicolaescu committed
226
  (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
227
{
228
  FRAME_PTR f = NULL;
229
  Lisp_Object window;
230 231 232

  check_x ();

233
  /* Decode the first argument: find the window or frame to use.  */
234
  if (EQ (position, Qt)
235 236
      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
			       || EQ (XCAR (position), Qtool_bar))))
237
    {
238
#if 0 /* Using the frame the mouse is on may not be right.  */
239
      /* Use the mouse's current position.  */
240
      FRAME_PTR new_f = SELECTED_FRAME ();
241
      Lisp_Object bar_window;
242
      enum scroll_bar_part part;
243
      Time time;
244
      Lisp_Object x, y;
245

246
      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
247

248
      if (new_f != 0)
249
	XSETFRAME (window, new_f);
250 251
      else
	window = selected_window;
252
#endif
253
      window = selected_window;
254 255 256 257 258
    }
  else if (CONSP (position))
    {
      Lisp_Object tem;
      tem = Fcar (position);
259
      if (CONSP (tem))
260
	window = Fcar (Fcdr (position));
261 262
      else
	{
263 264
	  tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
	  window = Fcar (tem);	     /* POSN_WINDOW (tem) */
265 266
	}
    }
267 268
  else if (WINDOWP (position) || FRAMEP (position))
    window = position;
269 270
  else
    window = Qnil;
271

272
  /* Decode where to put the menu.  */
273

274
  if (FRAMEP (window))
275
    f = XFRAME (window);
276
  else if (WINDOWP (window))
277
    {
278
      CHECK_LIVE_WINDOW (window);
279
      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
280
    }
281 282 283
  else
    /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
       but I don't want to make one now.  */
284
    CHECK_WINDOW (window);
285

286 287
  if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
    error ("Can not put X dialog on this terminal");
288

289 290 291 292 293 294 295 296 297 298 299
  /* Force a redisplay before showing the dialog.  If a frame is created
     just before showing the dialog, its contents may not have been fully
     drawn, as this depends on timing of events from the X server.  Redisplay
     is not done when a dialog is shown.  If redisplay could be done in the
     X event loop (i.e. the X event loop does not run in a signal handler)
     this would not be needed.

     Do this before creating the widget value that points to Lisp
     string contents, because Fredisplay may GC and relocate them.  */
  Fredisplay (Qt);

Jan Djärv's avatar
Jan Djärv committed
300
#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
301 302 303 304
  /* Display a menu with these alternatives
     in the middle of frame F.  */
  {
    Lisp_Object x, y, frame, newpos;
305 306 307
    XSETFRAME (frame, f);
    XSETINT (x, x_pixel_width (f) / 2);
    XSETINT (y, x_pixel_height (f) / 2);
308 309 310 311 312 313 314 315
    newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));

    return Fx_popup_menu (newpos,
			  Fcons (Fcar (contents), Fcons (contents, Qnil)));
  }
#else
  {
    Lisp_Object title;
316
    const char *error_name;
317
    Lisp_Object selection;
Stefan Monnier's avatar
Stefan Monnier committed
318
    int specpdl_count = SPECPDL_INDEX ();
319

320 321
    /* Decode the dialog items from what was specified.  */
    title = Fcar (contents);
322
    CHECK_STRING (title);
Stefan Monnier's avatar
Stefan Monnier committed
323
    record_unwind_protect (unuse_menu_items, Qnil);
324

Kenichi Handa's avatar
Kenichi Handa committed
325 326 327 328 329
    if (NILP (Fcar (Fcdr (contents))))
      /* No buttons specified, add an "Ok" button so users can pop down
         the dialog.  Also, the lesstif/motif version crashes if there are
         no buttons.  */
      contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
Kim F. Storm's avatar
Kim F. Storm committed
330

331
    list_of_panes (Fcons (contents, Qnil));
332

333 334
    /* Display them in a dialog box.  */
    BLOCK_INPUT;
335
    selection = xdialog_show (f, 0, title, header, &error_name);
336
    UNBLOCK_INPUT;
337

Stefan Monnier's avatar
Stefan Monnier committed
338
    unbind_to (specpdl_count, Qnil);
339 340
    discard_menu_items ();

341
    if (error_name) error ("%s", error_name);
342 343
    return selection;
  }
344
#endif
345
}
346 347 348 349


#ifndef MSDOS

350 351
#if defined USE_GTK || defined USE_MOTIF

352 353 354
/* Set menu_items_inuse so no other popup menu or dialog is created.  */

void
355
x_menu_set_in_use (int in_use)
356 357
{
  menu_items_inuse = in_use ? Qt : Qnil;
358
  popup_activated_flag = in_use;
359
#ifdef USE_X_TOOLKIT
360 361
  if (popup_activated_flag)
    x_activate_timeout_atimer ();
362
#endif
363 364
}

365 366
#endif

367 368
/* Wait for an X event to arrive or for a timer to expire.  */

369 370 371
#ifndef USE_MOTIF
static
#endif
372
void
373 374 375 376 377 378 379 380 381
x_menu_wait_for_event (void *data)
{
  /* Another way to do this is to register a timer callback, that can be
     done in GTK and Xt.  But we have to do it like this when using only X
     anyway, and with callbacks we would have three variants for timer handling
     instead of the small ifdefs below.  */

  while (
#ifdef USE_X_TOOLKIT
Jan Djärv's avatar
Jan Djärv committed
382
         ! XtAppPending (Xt_app_con)
383 384 385 386 387 388 389
#elif defined USE_GTK
         ! gtk_events_pending ()
#else
         ! XPending ((Display*) data)
#endif
         )
    {
390
      EMACS_TIME next_time = timer_check (), *ntp;
391 392 393 394 395 396 397 398 399 400 401 402
      long secs = EMACS_SECS (next_time);
      long usecs = EMACS_USECS (next_time);
      SELECT_TYPE read_fds;
      struct x_display_info *dpyinfo;
      int n = 0;

      FD_ZERO (&read_fds);
      for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
        {
          int fd = ConnectionNumber (dpyinfo->display);
          FD_SET (fd, &read_fds);
          if (fd > n) n = fd;
Jan Djärv's avatar
Jan Djärv committed
403
          XFlush (dpyinfo->display);
404 405
        }

406 407 408 409
      if (secs < 0 && usecs < 0)
        ntp = 0;
      else
        ntp = &next_time;
410

411
      select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp);
412 413 414 415
    }
}
#endif /* ! MSDOS */

416

Jan Djärv's avatar
Jan Djärv committed
417
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
418

Jan Djärv's avatar
Jan Djärv committed
419 420
#ifdef USE_X_TOOLKIT

421
/* Loop in Xt until the menu pulldown or dialog popup has been
Richard M. Stallman's avatar
Richard M. Stallman committed
422
   popped down (deactivated).  This is used for x-popup-menu
423 424
   and x-popup-dialog; it is not used for the menu bar.

425
   NOTE: All calls to popup_get_selection should be protected
426
   with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
427

428
static void
429
popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, LWLIB_ID id, int do_timers)
430
{
431
  XEvent event;
432

433 434
  while (popup_activated_flag)
    {
Richard M. Stallman's avatar
Richard M. Stallman committed
435
      if (initial_event)
436 437 438 439 440
        {
          event = *initial_event;
          initial_event = 0;
        }
      else
441 442 443 444
        {
          if (do_timers) x_menu_wait_for_event (0);
          XtAppNextEvent (Xt_app_con, &event);
        }
445

446
      /* Make sure we don't consider buttons grabbed after menu goes.
447 448 449 450
         And make sure to deactivate for any ButtonRelease,
         even if XtDispatchEvent doesn't do that.  */
      if (event.type == ButtonRelease
          && dpyinfo->display == event.xbutton.display)
451 452
        {
          dpyinfo->grabbed &= ~(1 << event.xbutton.button);
453
#ifdef USE_MOTIF /* Pretending that the event came from a
454 455 456 457 458 459 460 461
                    Btn1Down seems the only way to convince Motif to
                    activate its callbacks; setting the XmNmenuPost
                    isn't working. --marcus@sysc.pdx.edu.  */
          event.xbutton.button = 1;
          /*  Motif only pops down menus when no Ctrl, Alt or Mod
              key is pressed and the button is released.  So reset key state
              so Motif thinks this is the case.  */
          event.xbutton.state = 0;
462
#endif
463
        }
464
      /* Pop down on C-g and Escape.  */
465
      else if (event.type == KeyPress
466
               && dpyinfo->display == event.xbutton.display)
467 468
        {
          KeySym keysym = XLookupKeysym (&event.xkey, 0);
469

470 471
          if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
              || keysym == XK_Escape) /* Any escape, ignore modifiers.  */
472
            popup_activated_flag = 0;
473
        }
474

475
      x_dispatch_event (&event, event.xany.display);
476
    }
477 478
}

Karoly Lorentey's avatar
Karoly Lorentey committed
479
DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
480 481 482 483 484 485
       doc: /* Start key navigation of the menu bar in FRAME.
This initially opens the first menu bar item and you can then navigate with the
arrow keys, select a menu entry with the return key or cancel with the
escape key.  If FRAME has no menu bar this function does nothing.

If FRAME is nil or not given, use the selected frame.  */)
Dan Nicolaescu's avatar
Dan Nicolaescu committed
486
  (Lisp_Object frame)
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
{
  XEvent ev;
  FRAME_PTR f = check_x_frame (frame);
  Widget menubar;
  BLOCK_INPUT;

  if (FRAME_EXTERNAL_MENU_BAR (f))
    set_frame_menubar (f, 0, 1);

  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
  if (menubar)
    {
      Window child;
      int error_p = 0;

      x_catch_errors (FRAME_X_DISPLAY (f));
      memset (&ev, 0, sizeof ev);
      ev.xbutton.display = FRAME_X_DISPLAY (f);
      ev.xbutton.window = XtWindow (menubar);
      ev.xbutton.root = FRAME_X_DISPLAY_INFO (f)->root_window;
      ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
      ev.xbutton.button = Button1;
      ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
      ev.xbutton.same_screen = True;

#ifdef USE_MOTIF
      {
        Arg al[2];
        WidgetList list;
        Cardinal nr;
        XtSetArg (al[0], XtNchildren, &list);
        XtSetArg (al[1], XtNnumChildren, &nr);
        XtGetValues (menubar, al, 2);
        ev.xbutton.window = XtWindow (list[0]);
      }
#endif

      XTranslateCoordinates (FRAME_X_DISPLAY (f),
                             /* From-window, to-window.  */
                             ev.xbutton.window, ev.xbutton.root,

                             /* From-position, to-position.  */
                             ev.xbutton.x, ev.xbutton.y,
                             &ev.xbutton.x_root, &ev.xbutton.y_root,

                             /* Child of win.  */
                             &child);
      error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
      x_uncatch_errors ();

      if (! error_p)
        {
          ev.type = ButtonPress;
          ev.xbutton.state = 0;

          XtDispatchEvent (&ev);
          ev.xbutton.type = ButtonRelease;
          ev.xbutton.state = Button1Mask;
          XtDispatchEvent (&ev);
        }
    }

  UNBLOCK_INPUT;
550 551

  return Qnil;
552
}
Jan Djärv's avatar
Jan Djärv committed
553 554
#endif /* USE_X_TOOLKIT */

555

Jan Djärv's avatar
Jan Djärv committed
556
#ifdef USE_GTK
Karoly Lorentey's avatar
Karoly Lorentey committed
557
DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
558 559 560 561 562 563
       doc: /* Start key navigation of the menu bar in FRAME.
This initially opens the first menu bar item and you can then navigate with the
arrow keys, select a menu entry with the return key or cancel with the
escape key.  If FRAME has no menu bar this function does nothing.

If FRAME is nil or not given, use the selected frame.  */)
Dan Nicolaescu's avatar
Dan Nicolaescu committed
564
  (Lisp_Object frame)
565 566
{
  GtkWidget *menubar;
567 568
  FRAME_PTR f;

Chong Yidong's avatar
Chong Yidong committed
569 570 571
  /* gcc 2.95 doesn't accept the FRAME_PTR declaration after
     BLOCK_INPUT.  */

572
  BLOCK_INPUT;
573
  f = check_x_frame (frame);
574 575 576 577 578 579 580 581 582 583

  if (FRAME_EXTERNAL_MENU_BAR (f))
    set_frame_menubar (f, 0, 1);

  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
  if (menubar)
    {
      /* Activate the first menu.  */
      GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));

584 585 586 587 588 589
      if (children)
        {
          g_signal_emit_by_name (children->data, "activate_item");
          popup_activated_flag = 1;
          g_list_free (children);
        }
590 591 592 593 594 595
    }
  UNBLOCK_INPUT;

  return Qnil;
}

Jan Djärv's avatar
Jan Djärv committed
596 597
/* Loop util popup_activated_flag is set to zero in a callback.
   Used for popup menus and dialogs. */
Jan Djärv's avatar
Jan Djärv committed
598

Jan Djärv's avatar
Jan Djärv committed
599
static void
600
popup_widget_loop (int do_timers, GtkWidget *widget)
Jan Djärv's avatar
Jan Djärv committed
601 602 603 604 605 606
{
  ++popup_activated_flag;

  /* Process events in the Gtk event loop until done.  */
  while (popup_activated_flag)
    {
607
      if (do_timers) x_menu_wait_for_event (0);
Jan Djärv's avatar
Jan Djärv committed
608 609 610 611 612
      gtk_main_iteration ();
    }
}
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
613 614
/* Activate the menu bar of frame F.
   This is called from keyboard.c when it gets the
Pavel Janík's avatar
Pavel Janík committed
615
   MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
Richard M. Stallman's avatar
Richard M. Stallman committed
616 617

   To activate the menu bar, we use the X button-press event
618
   that was saved in saved_menu_event.
Richard M. Stallman's avatar
Richard M. Stallman committed
619 620 621 622 623 624 625
   That makes the toolkit do its thing.

   But first we recompute the menu bar contents (the whole tree).

   The reason for saving the button event until here, instead of
   passing it to the toolkit right away, is that we can safely
   execute Lisp code.  */
626

Andreas Schwab's avatar
Andreas Schwab committed
627
void
628
x_activate_menubar (FRAME_PTR f)
Richard M. Stallman's avatar
Richard M. Stallman committed
629
{
630 631 632
  if (! FRAME_X_P (f))
    abort ();

633
  if (!f->output_data.x->saved_menu_event->type)
Richard M. Stallman's avatar
Richard M. Stallman committed
634 635
    return;

636
#ifdef USE_GTK
Kenichi Handa's avatar
Kenichi Handa committed
637 638
  if (! xg_win_to_widget (FRAME_X_DISPLAY (f),
                          f->output_data.x->saved_menu_event->xany.window))
Jan Djärv's avatar
Jan Djärv committed
639 640
    return;
#endif
641

642
  set_frame_menubar (f, 0, 1);
Richard M. Stallman's avatar
Richard M. Stallman committed
643
  BLOCK_INPUT;
644
  popup_activated_flag = 1;
Jan Djärv's avatar
Jan Djärv committed
645 646 647 648
#ifdef USE_GTK
  XPutBackEvent (f->output_data.x->display_info->display,
                 f->output_data.x->saved_menu_event);
#else
Stefan Monnier's avatar
Stefan Monnier committed
649
  XtDispatchEvent (f->output_data.x->saved_menu_event);
Jan Djärv's avatar
Jan Djärv committed
650
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
651
  UNBLOCK_INPUT;
652

Richard M. Stallman's avatar
Richard M. Stallman committed
653
  /* Ignore this if we get it a second time.  */
654
  f->output_data.x->saved_menu_event->type = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
655 656
}

657 658
/* This callback is invoked when the user selects a menubar cascade
   pushbutton, but before the pulldown menu is posted.  */
659

Jan Djärv's avatar
Jan Djärv committed
660
#ifndef USE_GTK
661
static void
662
popup_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
663
{
664
  popup_activated_flag = 1;
665 666 667
#ifdef USE_X_TOOLKIT
  x_activate_timeout_atimer ();
#endif
668
}
Jan Djärv's avatar
Jan Djärv committed
669
#endif
670 671 672 673

/* This callback is invoked when a dialog or menu is finished being
   used and has been unposted.  */

Jan Djärv's avatar
Jan Djärv committed
674 675
#ifdef USE_GTK
static void
676
popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
Jan Djärv's avatar
Jan Djärv committed
677 678 679 680
{
  popup_activated_flag = 0;
}
#else
681
static void
682
popup_deactivate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
683 684
{
  popup_activated_flag = 0;
685
}
Jan Djärv's avatar
Jan Djärv committed
686
#endif
687

688

Jan Djärv's avatar
Jan Djärv committed
689 690 691 692
/* Function that finds the frame for WIDGET and shows the HELP text
   for that widget.
   F is the frame if known, or NULL if not known.  */
static void
693
show_help_event (FRAME_PTR f, xt_or_gtk_widget widget, Lisp_Object help)
694
{
Jan Djärv's avatar
Jan Djärv committed
695
  Lisp_Object frame;
696 697

  if (f)
698 699 700 701
    {
      XSETFRAME (frame, f);
      kbd_buffer_store_help_event (frame, help);
    }
702 703
  else
    {
Kenichi Handa's avatar
Kenichi Handa committed
704
#if 0  /* This code doesn't do anything useful.  ++kfs */
705
      /* WIDGET is the popup menu.  It's parent is the frame's
706
	 widget.  See which frame that is.  */
Jan Djärv's avatar
Jan Djärv committed
707
      xt_or_gtk_widget frame_widget = XtParent (widget);
708 709
      Lisp_Object tail;

710
      for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
711 712
	{
	  frame = XCAR (tail);
713
	  if (FRAMEP (frame)
714 715 716 717
	      && (f = XFRAME (frame),
		  FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
	    break;
	}
Kenichi Handa's avatar
Kenichi Handa committed
718
#endif
719
      show_help_echo (help, Qnil, Qnil, Qnil);
720
    }
721 722
}

Jan Djärv's avatar
Jan Djärv committed
723 724 725 726 727
/* Callback called when menu items are highlighted/unhighlighted
   while moving the mouse over them.  WIDGET is the menu bar or menu
   popup widget.  ID is its LWLIB_ID.  CALL_DATA contains a pointer to
   the data structure for the menu item, or null in case of
   unhighlighting.  */
728

Jan Djärv's avatar
Jan Djärv committed
729
#ifdef USE_GTK
730
static void
731
menu_highlight_callback (GtkWidget *widget, gpointer call_data)
Jan Djärv's avatar
Jan Djärv committed
732 733 734
{
  xg_menu_item_cb_data *cb_data;
  Lisp_Object help;
735

Jan Djärv's avatar
Jan Djärv committed
736 737 738 739 740 741 742
  cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (widget),
                                                       XG_ITEM_DATA);
  if (! cb_data) return;

  help = call_data ? cb_data->help : Qnil;

  /* If popup_activated_flag is greater than 1 we are in a popup menu.
743 744 745 746 747 748 749
     Don't pass the frame to show_help_event for those.
     Passing frame creates an Emacs event.  As we are looping in
     popup_widget_loop, it won't be handeled.  Passing NULL shows the tip
     directly without using an Emacs event.  This is what the Lucid code
     does below.  */
  show_help_event (popup_activated_flag <= 1 ? cb_data->cl_data->f : NULL,
                   widget, help);
Jan Djärv's avatar
Jan Djärv committed
750 751
}
#else
752
static void
753
menu_highlight_callback (Widget widget, LWLIB_ID id, void *call_data)
Jan Djärv's avatar
Jan Djärv committed
754 755 756 757 758 759 760
{
  struct frame *f;
  Lisp_Object help;

  widget_value *wv = (widget_value *) call_data;

  help = wv ? wv->help : Qnil;
761

Jan Djärv's avatar
Jan Djärv committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
  /* Determine the frame for the help event.  */
  f = menubar_id_to_frame (id);

  show_help_event (f, widget, help);
}
#endif

#ifdef USE_GTK
/* Gtk calls callbacks just because we tell it what item should be
   selected in a radio group.  If this variable is set to a non-zero
   value, we are creating menus and don't want callbacks right now.
*/
static int xg_crazy_callback_abort;

/* This callback is called from the menu bar pulldown menu
   when the user makes a selection.
   Figure out what the user chose
   and put the appropriate events into the keyboard buffer.  */
static void
781
menubar_selection_callback (GtkWidget *widget, gpointer client_data)
Jan Djärv's avatar
Jan Djärv committed
782 783 784 785 786 787 788 789 790
{
  xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;

  if (xg_crazy_callback_abort)
    return;

  if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
    return;

791 792 793 794 795 796 797 798 799
  /* For a group of radio buttons, GTK calls the selection callback first
     for the item that was active before the selection and then for the one that
     is active after the selection.  For C-h k this means we get the help on
     the deselected item and then the selected item is executed.  Prevent that
     by ignoring the non-active item.  */
  if (GTK_IS_RADIO_MENU_ITEM (widget)
      && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
    return;

800 801 802 803 804 805 806 807 808 809 810
  /* When a menu is popped down, X generates a focus event (i.e. focus
     goes back to the frame below the menu).  Since GTK buffers events,
     we force it out here before the menu selection event.  Otherwise
     sit-for will exit at once if the focus event follows the menu selection
     event.  */

  BLOCK_INPUT;
  while (gtk_events_pending ())
    gtk_main_iteration ();
  UNBLOCK_INPUT;

Jan Djärv's avatar
Jan Djärv committed
811 812 813 814 815 816 817 818 819 820 821 822 823
  find_and_call_menu_selection (cb_data->cl_data->f,
                                cb_data->cl_data->menu_bar_items_used,
                                cb_data->cl_data->menu_bar_vector,
                                cb_data->call_data);
}

#else /* not USE_GTK */

/* This callback is called from the menu bar pulldown menu
   when the user makes a selection.
   Figure out what the user chose
   and put the appropriate events into the keyboard buffer.  */
static void
824
menubar_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
Jan Djärv's avatar
Jan Djärv committed
825 826 827 828 829 830 831 832 833 834
{
  FRAME_PTR f;

  f = menubar_id_to_frame (id);
  if (!f)
    return;
  find_and_call_menu_selection (f, f->menu_bar_items_used,
                                f->menu_bar_vector, client_data);
}
#endif /* not USE_GTK */
Gerd Moellmann's avatar
Gerd Moellmann committed
835 836 837 838 839

/* Recompute all the widgets of frame F, when the menu bar has been
   changed.  Value is non-zero if widgets were updated.  */

static int
840
update_frame_menubar (FRAME_PTR f)
841
{
Jan Djärv's avatar
Jan Djärv committed
842 843 844
#ifdef USE_GTK
  return xg_update_frame_menubar (f);
#else
845
  struct x_output *x;
846
  int columns, rows;
847

848 849 850 851 852
  if (! FRAME_X_P (f))
    abort ();

  x = f->output_data.x;

Gerd Moellmann's avatar
Gerd Moellmann committed
853 854
  if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
    return 0;
855 856

  BLOCK_INPUT;
Gerd Moellmann's avatar
Gerd Moellmann committed
857 858
  /* Save the size of the frame because the pane widget doesn't accept
     to resize itself. So force it.  */
859 860
  columns = FRAME_COLS (f);
  rows = FRAME_LINES (f);
861

Gerd Moellmann's avatar
Gerd Moellmann committed
862 863
  /* Do the voodoo which means "I'm changing lots of things, don't try
     to refigure sizes until I'm done."  */
864
  lw_refigure_widget (x->column_widget, False);
865

Gerd Moellmann's avatar
Gerd Moellmann committed
866 867 868
  /* The order in which children are managed is the top to bottom
     order in which they are displayed in the paned window.  First,
     remove the text-area widget.  */
869 870
  XtUnmanageChild (x->edit_widget);

Gerd Moellmann's avatar
Gerd Moellmann committed
871 872 873 874 875
  /* Remove the menubar that is there now, and put up the menubar that
     should be there.  */
  XtManageChild (x->menubar_widget);
  XtMapWidget (x->menubar_widget);
  XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
876

877
  /* Re-manage the text-area widget, and then thrash the sizes.  */
878
  XtManageChild (x->edit_widget);
879
  lw_refigure_widget (x->column_widget, True);
880 881 882

  /* Force the pane widget to resize itself with the right values.  */
  EmacsFrameSetCharSize (x->edit_widget, columns, rows);
883
  UNBLOCK_INPUT;
Jan Djärv's avatar
Jan Djärv committed
884
#endif
Gerd Moellmann's avatar
Gerd Moellmann committed
885
  return 1;
886 887
}

888
#ifdef USE_LUCID
889
static void
890
apply_systemfont_to_dialog (Widget w)
891 892
{
  const char *fn = xsettings_get_system_normal_font ();
893
  if (fn)
894 895 896
    {
      XrmDatabase db = XtDatabase (XtDisplay (w));
      if (db)
897
        XrmPutStringResource (&db, "*dialog.font", fn);
898 899 900
    }
}

901
static void