xmenu.c 71.8 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 111
#ifndef TRUE
#define TRUE 1
#define FALSE 0
112
#endif /* no TRUE */
Jim Blandy's avatar
Jim Blandy committed
113

114 115
Lisp_Object Qdebug_on_next_call;

116
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
117
static Lisp_Object xdialog_show (FRAME_PTR, int, Lisp_Object, Lisp_Object,
118
                                 const char **);
Gerd Moellmann's avatar
Ditto.  
Gerd Moellmann committed
119 120
#endif

121
static int update_frame_menubar (struct frame *);
122

123
/* Flag which when set indicates a dialog or menu has been posted by
124
   Xt on behalf of one of the widget sets.  */
Jan Djärv's avatar
Jan Djärv committed
125
static int popup_activated_flag;
126

Richard M. Stallman's avatar
Richard M. Stallman committed
127
static int next_menubar_widget_id;
128

129

Richard M. Stallman's avatar
Richard M. Stallman committed
130
#ifdef USE_X_TOOLKIT
131

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

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

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

#endif
155

156 157 158 159 160 161 162 163
#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.  */
164
void
165
mouse_position_for_popup (FRAME_PTR f, int *x, int *y)
166 167 168 169
{
  Window root, dummy_window;
  int dummy;

170 171 172
  if (! FRAME_X_P (f))
    abort ();

173
  BLOCK_INPUT;
174

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
  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.  */
198 199
  *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
  *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
200 201 202 203
}

#endif /* HAVE_X_WINDOWS */

204 205
#ifdef HAVE_MENUS

206
DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
Pavel Janík's avatar
Pavel Janík committed
207
       doc: /* Pop up a dialog box and return user's selection.
Gerd Moellmann's avatar
Gerd Moellmann committed
208 209 210 211 212 213
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.
214
It is a list of the form (DIALOG ITEM1 ITEM2...).
Gerd Moellmann's avatar
Gerd Moellmann committed
215 216 217 218 219 220
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.
221 222
\(By default, approximately half appear on each side.)

223 224 225
If HEADER is non-nil, the frame title for the box is "Information",
otherwise it is "Question".

226 227 228
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
229
  (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
230
{
231
  FRAME_PTR f = NULL;
232
  Lisp_Object window;
233 234 235

  check_x ();

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

249
      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
250

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

275
  /* Decode where to put the menu.  */
276

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

289 290
  if (! FRAME_X_P (f) && ! FRAME_MSDOS_P (f))
    error ("Can not put X dialog on this terminal");
291

292 293 294 295 296 297 298 299 300 301 302
  /* 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
303
#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
304 305 306 307
  /* Display a menu with these alternatives
     in the middle of frame F.  */
  {
    Lisp_Object x, y, frame, newpos;
308 309 310
    XSETFRAME (frame, f);
    XSETINT (x, x_pixel_width (f) / 2);
    XSETINT (y, x_pixel_height (f) / 2);
311 312 313 314 315 316 317 318
    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;
319
    const char *error_name;
320
    Lisp_Object selection;
Stefan Monnier's avatar
Stefan Monnier committed
321
    int specpdl_count = SPECPDL_INDEX ();
322

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

Kenichi Handa's avatar
Kenichi Handa committed
328 329 330 331 332
    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
333

334
    list_of_panes (Fcons (contents, Qnil));
335

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

Stefan Monnier's avatar
Stefan Monnier committed
341
    unbind_to (specpdl_count, Qnil);
342 343 344 345 346
    discard_menu_items ();

    if (error_name) error (error_name);
    return selection;
  }
347
#endif
348
}
349 350 351 352


#ifndef MSDOS

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

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

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

368
void
369 370 371 372 373 374 375 376 377
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
378
         ! XtAppPending (Xt_app_con)
379 380 381 382 383 384 385
#elif defined USE_GTK
         ! gtk_events_pending ()
#else
         ! XPending ((Display*) data)
#endif
         )
    {
386
      EMACS_TIME next_time = timer_check (1), *ntp;
387 388 389 390 391 392 393 394 395 396 397 398
      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
399
          XFlush (dpyinfo->display);
400 401
        }

402 403 404 405
      if (secs < 0 && usecs < 0)
        ntp = 0;
      else
        ntp = &next_time;
406

407
      select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ntp);
408 409 410 411
    }
}
#endif /* ! MSDOS */

412

Jan Djärv's avatar
Jan Djärv committed
413
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
414

Jan Djärv's avatar
Jan Djärv committed
415 416
#ifdef USE_X_TOOLKIT

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

421
   NOTE: All calls to popup_get_selection should be protected
422
   with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
423

424
static void
425
popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, LWLIB_ID id, int do_timers)
426
{
427
  XEvent event;
428

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

442
      /* Make sure we don't consider buttons grabbed after menu goes.
443 444 445 446
         And make sure to deactivate for any ButtonRelease,
         even if XtDispatchEvent doesn't do that.  */
      if (event.type == ButtonRelease
          && dpyinfo->display == event.xbutton.display)
447 448
        {
          dpyinfo->grabbed &= ~(1 << event.xbutton.button);
449
#ifdef USE_MOTIF /* Pretending that the event came from a
450 451 452 453 454 455 456 457
                    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;
458
#endif
459
        }
460
      /* Pop down on C-g and Escape.  */
461
      else if (event.type == KeyPress
462
               && dpyinfo->display == event.xbutton.display)
463 464
        {
          KeySym keysym = XLookupKeysym (&event.xkey, 0);
465

466 467
          if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
              || keysym == XK_Escape) /* Any escape, ignore modifiers.  */
468
            popup_activated_flag = 0;
469
        }
470

471
      x_dispatch_event (&event, event.xany.display);
472
    }
473 474
}

Karoly Lorentey's avatar
Karoly Lorentey committed
475
DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
476 477 478 479 480 481
       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
482
  (Lisp_Object frame)
483 484 485 486 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
{
  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;
546 547

  return Qnil;
548
}
Jan Djärv's avatar
Jan Djärv committed
549 550
#endif /* USE_X_TOOLKIT */

551

Jan Djärv's avatar
Jan Djärv committed
552
#ifdef USE_GTK
Karoly Lorentey's avatar
Karoly Lorentey committed
553
DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
554 555 556 557 558 559
       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
560
  (Lisp_Object frame)
561 562
{
  GtkWidget *menubar;
563 564
  FRAME_PTR f;

Chong Yidong's avatar
Chong Yidong committed
565 566 567
  /* gcc 2.95 doesn't accept the FRAME_PTR declaration after
     BLOCK_INPUT.  */

568
  BLOCK_INPUT;
569
  f = check_x_frame (frame);
570 571 572 573 574 575 576 577 578 579

  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));

580 581 582 583 584 585
      if (children)
        {
          g_signal_emit_by_name (children->data, "activate_item");
          popup_activated_flag = 1;
          g_list_free (children);
        }
586 587 588 589 590 591
    }
  UNBLOCK_INPUT;

  return Qnil;
}

Jan Djärv's avatar
Jan Djärv committed
592 593
/* 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
594

Jan Djärv's avatar
Jan Djärv committed
595
static void
596
popup_widget_loop (int do_timers, GtkWidget *widget)
Jan Djärv's avatar
Jan Djärv committed
597 598 599 600 601 602
{
  ++popup_activated_flag;

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

Richard M. Stallman's avatar
Richard M. Stallman committed
609 610
/* 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
611
   MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
Richard M. Stallman's avatar
Richard M. Stallman committed
612 613

   To activate the menu bar, we use the X button-press event
614
   that was saved in saved_menu_event.
Richard M. Stallman's avatar
Richard M. Stallman committed
615 616 617 618 619 620 621
   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.  */
622

Andreas Schwab's avatar
Andreas Schwab committed
623
void
624
x_activate_menubar (FRAME_PTR f)
Richard M. Stallman's avatar
Richard M. Stallman committed
625
{
626 627 628
  if (! FRAME_X_P (f))
    abort ();

629
  if (!f->output_data.x->saved_menu_event->type)
Richard M. Stallman's avatar
Richard M. Stallman committed
630 631
    return;

632
#ifdef USE_GTK
Kenichi Handa's avatar
Kenichi Handa committed
633 634
  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
635 636
    return;
#endif
637

638
  set_frame_menubar (f, 0, 1);
Richard M. Stallman's avatar
Richard M. Stallman committed
639
  BLOCK_INPUT;
640
  popup_activated_flag = 1;
Jan Djärv's avatar
Jan Djärv committed
641 642 643 644
#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
645
  XtDispatchEvent (f->output_data.x->saved_menu_event);
Jan Djärv's avatar
Jan Djärv committed
646
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
647
  UNBLOCK_INPUT;
648

Richard M. Stallman's avatar
Richard M. Stallman committed
649
  /* Ignore this if we get it a second time.  */
650
  f->output_data.x->saved_menu_event->type = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
651 652
}

653 654
/* This callback is invoked when the user selects a menubar cascade
   pushbutton, but before the pulldown menu is posted.  */
655

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

/* 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
670 671
#ifdef USE_GTK
static void
672
popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
Jan Djärv's avatar
Jan Djärv committed
673 674 675 676
{
  popup_activated_flag = 0;
}
#else
677
static void
678
popup_deactivate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
679 680
{
  popup_activated_flag = 0;
681
}
Jan Djärv's avatar
Jan Djärv committed
682
#endif
683

684

Jan Djärv's avatar
Jan Djärv committed
685 686 687 688
/* 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
689
show_help_event (FRAME_PTR f, xt_or_gtk_widget widget, Lisp_Object help)
690
{
Jan Djärv's avatar
Jan Djärv committed
691
  Lisp_Object frame;
692 693

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

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

Jan Djärv's avatar
Jan Djärv committed
719 720 721 722 723
/* 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.  */
724

Jan Djärv's avatar
Jan Djärv committed
725 726
#ifdef USE_GTK
void
727
menu_highlight_callback (GtkWidget *widget, gpointer call_data)
Jan Djärv's avatar
Jan Djärv committed
728 729 730
{
  xg_menu_item_cb_data *cb_data;
  Lisp_Object help;
731

Jan Djärv's avatar
Jan Djärv committed
732 733 734 735 736 737 738 739 740 741 742 743 744 745
  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.
     Don't show help for them, they won't appear before the
     popup is popped down.  */
  if (popup_activated_flag <= 1)
    show_help_event (cb_data->cl_data->f, widget, help);
}
#else
void
746
menu_highlight_callback (Widget widget, LWLIB_ID id, void *call_data)
Jan Djärv's avatar
Jan Djärv committed
747 748 749 750 751 752 753
{
  struct frame *f;
  Lisp_Object help;

  widget_value *wv = (widget_value *) call_data;

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

Jan Djärv's avatar
Jan Djärv committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
  /* 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
774
menubar_selection_callback (GtkWidget *widget, gpointer client_data)
Jan Djärv's avatar
Jan Djärv committed
775 776 777 778 779 780 781 782 783
{
  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;

784 785 786 787 788 789 790 791 792
  /* 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;

793 794 795 796 797 798 799 800 801 802 803
  /* 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
804 805 806 807 808 809 810 811 812 813 814 815 816
  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
817
menubar_selection_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
Jan Djärv's avatar
Jan Djärv committed
818 819 820 821 822 823 824 825 826 827
{
  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
828 829 830 831 832

/* Recompute all the widgets of frame F, when the menu bar has been
   changed.  Value is non-zero if widgets were updated.  */

static int
833
update_frame_menubar (FRAME_PTR f)
834
{
Jan Djärv's avatar
Jan Djärv committed
835 836 837
#ifdef USE_GTK
  return xg_update_frame_menubar (f);
#else
838
  struct x_output *x;
839
  int columns, rows;
840

841 842 843 844 845
  if (! FRAME_X_P (f))
    abort ();

  x = f->output_data.x;

Gerd Moellmann's avatar
Gerd Moellmann committed
846 847
  if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
    return 0;
848 849

  BLOCK_INPUT;
Gerd Moellmann's avatar
Gerd Moellmann committed
850 851
  /* Save the size of the frame because the pane widget doesn't accept
     to resize itself. So force it.  */
852 853
  columns = FRAME_COLS (f);
  rows = FRAME_LINES (f);
854

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

Gerd Moellmann's avatar
Gerd Moellmann committed
859 860 861
  /* 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.  */
862 863
  XtUnmanageChild (x->edit_widget);

Gerd Moellmann's avatar
Gerd Moellmann committed
864 865 866 867 868
  /* 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);
869

870
  /* Re-manage the text-area widget, and then thrash the sizes.  */
871
  XtManageChild (x->edit_widget);
872
  lw_refigure_widget (x->column_widget, True);
873 874 875

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

881
#ifdef USE_LUCID
882
static void
883
apply_systemfont_to_dialog (Widget w)
884 885
{
  const char *fn = xsettings_get_system_normal_font ();
886
  if (fn)
887 888 889
    {
      XrmDatabase db = XtDatabase (XtDisplay (w));
      if (db)
890
        XrmPutStringResource (&db, "*dialog.font", fn);
891 892 893
    }
}

894
static void
895
apply_systemfont_to_menu (struct frame *f, Widget w)
896 897 898
{
  const char *fn = xsettings_get_system_normal_font ();

899
  if (fn)
900
    {
901 902 903 904 905 906
      XrmDatabase db = XtDatabase (XtDisplay (w));
      if (db)
        {
          XrmPutStringResource (&db, "*menubar*font", fn);
          XrmPutStringResource (&db, "*popup*font", fn);
        }
907 908
    }
}
909

910 911
#endif

912 913 914