emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/pgtk 04bfe70 048/100: implement popup/context menu


From: Yuuki Harano
Subject: feature/pgtk 04bfe70 048/100: implement popup/context menu
Date: Tue, 24 Nov 2020 08:02:35 -0500 (EST)

branch: feature/pgtk
commit 04bfe701381ebf292bfc123177c48efd22613d41
Author: Yuuki Harano <masm+github@masm11.me>
Commit: Jeff Walsh <jeff.walsh@drtusers-MacBook-Pro.local>

    implement popup/context menu
    
        * ../src/pgtkterm.c (x_draw_glyph_string_background)
        (x_draw_glyph_string_foreground)
        (x_draw_composite_glyph_string_foreground)
        (x_draw_glyphless_glyph_string_foreground, x_draw_relief_rect)
        (x_draw_box_rect, x_draw_glyph_string_box, x_draw_image_relief)
        (x_draw_image_foreground, x_draw_image_glyph_string)
        (button_event):
    
        * src/pgtkterm.h (struct pgtk_display_info):
    
        * src/pgtkmenu.c (pgtk_menu_set_in_use)
        (pgtk_menu_wait_for_event, Fx_menu_bar_open_internal)
        (popup_widget_loop, pgtk_activate_menubar)
        (popup_deactivate_callback, show_help_event): new functions
        (menu_highlight_callback): remove
        (update_frame_menubar, popup_selection_callback, pop_down_menu)
        (create_and_show_popup_menu, cleanup_widget_value_tree)
        (pgtk_menu_show, dialog_selection_callback): new
        (create_and_show_dialog, pgtk_dialog_show, pgtk_popup_dialog)
        (popup_activated, Fmenu_or_popup_active_p, syms_of_pgtkmenu):
---
 src/gtkutil.c  |   6 +
 src/pgtkmenu.c | 814 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 src/pgtkterm.c |  71 ++---
 src/pgtkterm.h |   5 +-
 4 files changed, 794 insertions(+), 102 deletions(-)

diff --git a/src/gtkutil.c b/src/gtkutil.c
index 6d6e4b4..16fac6c 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -2968,9 +2968,15 @@ create_menus (widget_value *data,
       if (name)
         gtk_widget_set_name (wmenu, name);
 
+#ifndef HAVE_PGTK
       if (deactivate_cb)
         g_signal_connect (G_OBJECT (wmenu),
                           "selection-done", deactivate_cb, 0);
+#else
+      if (deactivate_cb)
+        g_signal_connect (G_OBJECT (wmenu),
+                          "deactivate", deactivate_cb, 0);
+#endif
     }
 
   for (item = data; item; item = item->next)
diff --git a/src/pgtkmenu.c b/src/pgtkmenu.c
index 9148504..b946220 100644
--- a/src/pgtkmenu.c
+++ b/src/pgtkmenu.c
@@ -41,14 +41,162 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "gtkutil.h"
 #include <gtk/gtk.h>
 
+/* Flag which when set indicates a dialog or menu has been posted by
+   Xt on behalf of one of the widget sets.  */
+static int popup_activated_flag;
 
-Lisp_Object
-pgtk_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+/* Set menu_items_inuse so no other popup menu or dialog is created.  */
+
+void
+pgtk_menu_set_in_use (bool in_use)
+{
+  Lisp_Object frames, frame;
+
+  menu_items_inuse = in_use;
+  popup_activated_flag = in_use;
+
+  /* Don't let frames in `above' z-group obscure popups.  */
+  FOR_EACH_FRAME (frames, frame)
+    {
+      struct frame *f = XFRAME (frame);
+
+      if (in_use && FRAME_Z_GROUP_ABOVE (f))
+       x_set_z_group (f, Qabove_suspended, Qabove);
+      else if (!in_use && FRAME_Z_GROUP_ABOVE_SUSPENDED (f))
+       x_set_z_group (f, Qabove, Qabove_suspended);
+    }
+}
+
+/* Wait for an X event to arrive or for a timer to expire.  */
+
+void
+pgtk_menu_wait_for_event (void *data)
+{
+  struct timespec next_time = timer_check (), *ntp;
+
+  if (! timespec_valid_p (next_time))
+    ntp = 0;
+  else
+    ntp = &next_time;
+
+  /* Gtk3 have arrows on menus when they don't fit.  When the
+     pointer is over an arrow, a timeout scrolls it a bit.  Use
+     xg_select so that timeout gets triggered.  */
+  pgtk_select (0, NULL, NULL, NULL, ntp, NULL);
+}
+
+DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, 
Sx_menu_bar_open_internal, 0, 1, "i",
+       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.  */)
+  (Lisp_Object frame)
 {
+  GtkWidget *menubar;
+  struct frame *f;
+
+  block_input ();
+  f = decode_window_system_frame (frame);
+
+  if (FRAME_EXTERNAL_MENU_BAR (f))
+    set_frame_menubar (f, false, true);
+
+  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+  if (menubar)
+    {
+      /* Activate the first menu.  */
+      GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
+
+      if (children)
+        {
+          g_signal_emit_by_name (children->data, "activate_item");
+          g_list_free (children);
+        }
+    }
+  unblock_input ();
+
   return Qnil;
 }
 
+/* Loop util popup_activated_flag is set to zero in a callback.
+   Used for popup menus and dialogs. */
 
+static void
+popup_widget_loop (bool do_timers, GtkWidget *widget)
+{
+  ++popup_activated_flag;
+
+  /* Process events in the Gtk event loop until done.  */
+  while (popup_activated_flag)
+    {
+      if (do_timers) pgtk_menu_wait_for_event (0);
+      gtk_main_iteration ();
+    }
+}
+
+void pgtk_activate_menubar (struct frame *f)
+{
+  set_frame_menubar(f, false, true);
+
+  popup_activated_flag = 1;
+
+  /* f->output_data.pgtk->menubar_active = 1; */
+}
+
+/* This callback is invoked when a dialog or menu is finished being
+   used and has been unposted.  */
+
+static void
+popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
+{
+  popup_activated_flag = 0;
+}
+
+/* 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
+show_help_event (struct frame *f, GtkWidget *widget, Lisp_Object help)
+{
+  Lisp_Object frame;
+
+  if (f)
+    {
+      XSETFRAME (frame, f);
+      kbd_buffer_store_help_event (frame, help);
+    }
+  else
+    show_help_echo (help, Qnil, Qnil, Qnil);
+}
+
+/* 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.  */
+
+static void
+menu_highlight_callback (GtkWidget *widget, gpointer call_data)
+{
+  xg_menu_item_cb_data *cb_data;
+  Lisp_Object help;
+
+  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 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 handled.  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);
+}
 
 /* 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
@@ -97,30 +245,15 @@ menubar_selection_callback (GtkWidget *widget, gpointer 
client_data)
                                 cb_data->call_data);
 }
 
-static void
-menu_highlight_callback (GtkWidget *widget, gpointer call_data)
-{
-  xg_menu_item_cb_data *cb_data;
-  Lisp_Object help;
-
-  cb_data = g_object_get_data (G_OBJECT (widget), XG_ITEM_DATA);
-  if (! cb_data) return;
-
-  help = call_data ? cb_data->help : Qnil;
-}
-
-
-/* This callback is invoked when a dialog or menu is finished being
-   used and has been unposted.  */
+/* Recompute all the widgets of frame F, when the menu bar has been
+   changed.  */
 
 static void
-popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
+update_frame_menubar (struct frame *f)
 {
+  xg_update_frame_menubar (f);
 }
 
-
-
-
 /* Set the contents of the menubar widgets of frame F.
    The argument FIRST_TIME is currently ignored;
    it is set the first time this is called, from initialize_frame_menubar.  */
@@ -357,15 +490,13 @@ set_frame_menubar (struct frame *f, bool first_time, bool 
deep_p)
     }
 
   free_menubar_widget_value_tree (first_wv);
-  xg_update_frame_menubar (f);
+  update_frame_menubar (f);
 
   xg_crazy_callback_abort = false;
 
   unblock_input ();
 }
 
-
-
 /* Called from Fx_create_frame to create the initial menubar of a frame
    before it is mapped, so that the window is mapped with the menubar already
    there instead of us tacking it on later and thrashing the window after it
@@ -381,95 +512,644 @@ initialize_frame_menubar (struct frame *f)
 }
 
 
-void pgtk_activate_menubar (struct frame *f)
+/* x_menu_show actually displays a menu using the panes and items in menu_items
+   and returns the value selected from it.
+   There are two versions of x_menu_show, one for Xt and one for Xlib.
+   Both assume input is blocked by the caller.  */
+
+/* F is the frame the menu is for.
+   X and Y are the frame-relative specified position,
+   relative to the inside upper left corner of the frame F.
+   Bitfield MENUFLAGS bits are:
+   MENU_FOR_CLICK is set if this menu was invoked for a mouse click.
+   MENU_KEYMAPS is set if this menu was specified with keymaps;
+    in that case, we return a list containing the chosen item's value
+    and perhaps also the pane's prefix.
+   TITLE is the specified menu title.
+   ERROR is a place to store an error message string in case of failure.
+   (We return nil on failure, but the value doesn't actually matter.)  */
+
+/* The item selected in the popup menu.  */
+static Lisp_Object *volatile menu_item_selection;
+
+static void
+popup_selection_callback (GtkWidget *widget, gpointer client_data)
 {
-  set_frame_menubar(f, false, false);
+  xg_menu_item_cb_data *cb_data = client_data;
 
-  /* f->output_data.pgtk->menubar_active = 1; */
+  if (xg_crazy_callback_abort) return;
+  if (cb_data) menu_item_selection = cb_data->call_data;
+}
+
+static void
+pop_down_menu (void *arg)
+{
+  popup_activated_flag = 0;
+  block_input ();
+  gtk_widget_destroy (GTK_WIDGET (arg));
+  unblock_input ();
+}
+
+/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
+   menu pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
+                           int x, int y, bool for_click)
+{
+  GtkWidget *menu;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  eassert (FRAME_PGTK_P (f));
+
+  xg_crazy_callback_abort = true;
+  menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
+                           G_CALLBACK (popup_selection_callback),
+                           G_CALLBACK (popup_deactivate_callback),
+                           G_CALLBACK (menu_highlight_callback));
+  xg_crazy_callback_abort = false;
+
+  /* Display the menu.  */
+  gtk_widget_show_all (menu);
+
+  if (for_click)
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), 
FRAME_DISPLAY_INFO(f)->last_click_event);
+  else {
+    GdkRectangle rect;
+    rect.x = x;
+    rect.y = y;
+    rect.width = 1;
+    rect.height = 1;
+    gtk_menu_popup_at_rect (GTK_MENU (menu),
+                           gtk_widget_get_window(FRAME_GTK_WIDGET(f)),
+                           &rect,
+                           GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST,
+                           FRAME_DISPLAY_INFO(f)->last_click_event);
+  }
+
+  record_unwind_protect_ptr (pop_down_menu, menu);
+
+  if (gtk_widget_get_mapped (menu))
+    {
+      /* Set this to one.  popup_widget_loop increases it by one, so it becomes
+         two.  show_help_echo uses this to detect popup menus.  */
+      popup_activated_flag = 1;
+      /* Process events that apply to the menu.  */
+      popup_widget_loop (true, menu);
+    }
+
+  unbind_to (specpdl_count, Qnil);
+
+  /* Must reset this manually because the button release event is not passed
+     to Emacs event loop. */
+  FRAME_DISPLAY_INFO (f)->grabbed = 0;
 }
 
+static void
+cleanup_widget_value_tree (void *arg)
+{
+  free_menubar_widget_value_tree (arg);
+}
 
 Lisp_Object
 pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
             Lisp_Object title, const char **error_name)
 {
-  Lisp_Object tem;
+  int i;
+  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+  widget_value **submenu_stack
+    = alloca (menu_items_used * sizeof *submenu_stack);
+  Lisp_Object *subprefix_stack
+    = alloca (menu_items_used * sizeof *subprefix_stack);
+  int submenu_depth = 0;
+
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  eassert (FRAME_PGTK_P (f));
+
+  *error_name = NULL;
+
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error_name = "Empty menu";
+      return Qnil;
+    }
 
   block_input ();
 
+  /* Create a tree of widget_value objects
+     representing the panes and their items.  */
+  wv = make_widget_value ("menu", NULL, true, Qnil);
+  wv->button_type = BUTTON_TYPE_NONE;
+  first_wv = wv;
+  bool first_pane = true;
 
-  unblock_input ();
+  /* Loop over all panes and items, filling in the tree.  */
+  i = 0;
+  while (i < menu_items_used)
+    {
+      if (NILP (AREF (menu_items, i)))
+       {
+         submenu_stack[submenu_depth++] = save_wv;
+         save_wv = prev_wv;
+         prev_wv = 0;
+         first_pane = true;
+         i++;
+       }
+      else if (EQ (AREF (menu_items, i), Qlambda))
+       {
+         prev_wv = save_wv;
+         save_wv = submenu_stack[--submenu_depth];
+         first_pane = false;
+         i++;
+       }
+      else if (EQ (AREF (menu_items, i), Qt)
+              && submenu_depth != 0)
+       i += MENU_ITEMS_PANE_LENGTH;
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (AREF (menu_items, i), Qquote))
+       i += 1;
+      else if (EQ (AREF (menu_items, i), Qt))
+       {
+         /* Create a new pane.  */
+         Lisp_Object pane_name, prefix;
+         const char *pane_string;
+
+         pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+         prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+           {
+             pane_name = ENCODE_MENU_STRING (pane_name);
+             ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
+           }
+#endif
+         pane_string = (NILP (pane_name)
+                        ? "" : SSDATA (pane_name));
+         /* If there is just one top-level pane, put all its items directly
+            under the top-level menu.  */
+         if (menu_items_n_panes == 1)
+           pane_string = "";
+
+         /* If the pane has a meaningful name,
+            make the pane a top-level menu item
+            with its items as a submenu beneath it.  */
+         if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, ""))
+           {
+             wv = make_widget_value (pane_string, NULL, true, Qnil);
+             if (save_wv)
+               save_wv->next = wv;
+             else
+               first_wv->contents = wv;
+             if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+               wv->name++;
+             wv->button_type = BUTTON_TYPE_NONE;
+             save_wv = wv;
+             prev_wv = 0;
+           }
+         else if (first_pane)
+           {
+             save_wv = wv;
+             prev_wv = 0;
+           }
+         first_pane = false;
+         i += MENU_ITEMS_PANE_LENGTH;
+       }
+      else
+       {
+         /* Create a new item within current pane.  */
+         Lisp_Object item_name, enable, descrip, def, type, selected, help;
+         item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+         enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+         descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+         def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+         type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+         selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+          if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+           {
+             item_name = ENCODE_MENU_STRING (item_name);
+             ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
+           }
+
+          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+           {
+             descrip = ENCODE_MENU_STRING (descrip);
+             ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
+           }
+#endif /* not HAVE_MULTILINGUAL_MENU */
+
+         wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
+                                 STRINGP (help) ? help : Qnil);
+         if (prev_wv)
+           prev_wv->next = wv;
+         else
+           save_wv->contents = wv;
+         if (!NILP (descrip))
+           wv->key = SSDATA (descrip);
+         /* If this item has a null value,
+            make the call_data null so that it won't display a box
+            when the mouse is on it.  */
+         wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
+
+         if (NILP (type))
+           wv->button_type = BUTTON_TYPE_NONE;
+         else if (EQ (type, QCtoggle))
+           wv->button_type = BUTTON_TYPE_TOGGLE;
+         else if (EQ (type, QCradio))
+           wv->button_type = BUTTON_TYPE_RADIO;
+         else
+           emacs_abort ();
 
-  // not implemented.
-  return Qnil;
-}
+         wv->selected = !NILP (selected);
 
-DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, 
Sx_menu_bar_open_internal, 0, 1, "i",
-       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.
+         prev_wv = wv;
 
-If FRAME is nil or not given, use the selected frame.  */)
-  (Lisp_Object frame)
-{
-  GtkWidget *menubar;
-  struct frame *f;
+         i += MENU_ITEMS_ITEM_LENGTH;
+       }
+    }
 
-  block_input ();
-  f = decode_window_system_frame (frame);
+  /* Deal with the title, if it is non-nil.  */
+  if (!NILP (title))
+    {
+      widget_value *wv_title;
+      widget_value *wv_sep1 = make_widget_value ("--", NULL, false, Qnil);
+      widget_value *wv_sep2 = make_widget_value ("--", NULL, false, Qnil);
+
+      wv_sep2->next = first_wv->contents;
+      wv_sep1->next = wv_sep2;
+
+#ifndef HAVE_MULTILINGUAL_MENU
+      if (STRING_MULTIBYTE (title))
+       title = ENCODE_MENU_STRING (title);
+#endif
+
+      wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil);
+      wv_title->button_type = BUTTON_TYPE_NONE;
+      wv_title->next = wv_sep1;
+      first_wv->contents = wv_title;
+    }
 
-  if (FRAME_EXTERNAL_MENU_BAR (f))
-    set_frame_menubar (f, false, true);
+  /* No selection has been chosen yet.  */
+  menu_item_selection = 0;
 
-  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
-  if (menubar)
+  /* Make sure to free the widget_value objects we used to specify the
+     contents even with longjmp.  */
+  record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
+
+  /* Actually create and show the menu until popped down.  */
+  create_and_show_popup_menu (f, first_wv, x, y,
+                             menuflags & MENU_FOR_CLICK);
+
+  unbind_to (specpdl_count, Qnil);
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+  if (menu_item_selection != 0)
     {
-      /* Activate the first menu.  */
-      GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
+      Lisp_Object prefix, entry;
 
-      if (children)
-        {
-          g_signal_emit_by_name (children->data, "activate_item");
-          g_list_free (children);
-        }
+      prefix = entry = Qnil;
+      i = 0;
+      while (i < menu_items_used)
+       {
+         if (NILP (AREF (menu_items, i)))
+           {
+             subprefix_stack[submenu_depth++] = prefix;
+             prefix = entry;
+             i++;
+           }
+         else if (EQ (AREF (menu_items, i), Qlambda))
+           {
+             prefix = subprefix_stack[--submenu_depth];
+             i++;
+           }
+         else if (EQ (AREF (menu_items, i), Qt))
+           {
+             prefix
+               = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+             i += MENU_ITEMS_PANE_LENGTH;
+           }
+         /* Ignore a nil in the item list.
+            It's meaningful only for dialog boxes.  */
+         else if (EQ (AREF (menu_items, i), Qquote))
+           i += 1;
+         else
+           {
+             entry
+               = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+             if (menu_item_selection == aref_addr (menu_items, i))
+               {
+                 if (menuflags & MENU_KEYMAPS)
+                   {
+                     int j;
+
+                     entry = list1 (entry);
+                     if (!NILP (prefix))
+                       entry = Fcons (prefix, entry);
+                     for (j = submenu_depth - 1; j >= 0; j--)
+                       if (!NILP (subprefix_stack[j]))
+                         entry = Fcons (subprefix_stack[j], entry);
+                   }
+                 unblock_input ();
+                 return entry;
+               }
+             i += MENU_ITEMS_ITEM_LENGTH;
+           }
+       }
+    }
+  else if (!(menuflags & MENU_FOR_CLICK))
+    {
+      unblock_input ();
+      /* Make "Cancel" equivalent to C-g.  */
+      quit ();
     }
-  unblock_input ();
 
+  unblock_input ();
   return Qnil;
 }
 
+static void
+dialog_selection_callback (GtkWidget *widget, gpointer client_data)
+{
+  /* Treat the pointer as an integer.  There's no problem
+     as long as pointers have enough bits to hold small integers.  */
+  if ((intptr_t) client_data != -1)
+    menu_item_selection = client_data;
+
+  popup_activated_flag = 0;
+}
+
+/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
+   dialog pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_dialog (struct frame *f, widget_value *first_wv)
+{
+  GtkWidget *menu;
+
+  eassert (FRAME_PGTK_P (f));
+
+  menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
+                           G_CALLBACK (dialog_selection_callback),
+                           G_CALLBACK (popup_deactivate_callback),
+                           0);
+
+  if (menu)
+    {
+      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+      record_unwind_protect_ptr (pop_down_menu, menu);
+
+      /* Display the menu.  */
+      gtk_widget_show_all (menu);
+
+      /* Process events that apply to the menu.  */
+      popup_widget_loop (true, menu);
+
+      unbind_to (specpdl_count, Qnil);
+    }
+}
 
 static const char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
 
-extern Lisp_Object
+Lisp_Object
 pgtk_dialog_show (struct frame *f, Lisp_Object title,
-                Lisp_Object header, char **error)
+                Lisp_Object header, const char **error_name)
 {
+  int i, nb_buttons=0;
+  char dialog_name[6];
+
+  widget_value *wv, *first_wv = 0, *prev_wv = 0;
+
+  /* Number of elements seen so far, before boundary.  */
+  int left_count = 0;
+  /* Whether we've seen the boundary between left-hand elts and right-hand.  */
+  bool boundary_seen = false;
+
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  eassert (FRAME_PGTK_P (f));
+
+  *error_name = NULL;
+
+  if (menu_items_n_panes > 1)
+    {
+      *error_name = "Multiple panes in dialog box";
+      return Qnil;
+    }
+
+  /* Create a tree of widget_value objects
+     representing the text label and buttons.  */
+  {
+    Lisp_Object pane_name;
+    const char *pane_string;
+    pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
+    pane_string = (NILP (pane_name)
+                  ? "" : SSDATA (pane_name));
+    prev_wv = make_widget_value ("message", (char *) pane_string, true, Qnil);
+    first_wv = prev_wv;
+
+    /* Loop over all panes and items, filling in the tree.  */
+    i = MENU_ITEMS_PANE_LENGTH;
+    while (i < menu_items_used)
+      {
+
+       /* Create a new item within current pane.  */
+       Lisp_Object item_name, enable, descrip;
+       item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+       enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+       descrip
+         = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+
+       if (NILP (item_name))
+         {
+           free_menubar_widget_value_tree (first_wv);
+           *error_name = "Submenu in dialog items";
+           return Qnil;
+         }
+       if (EQ (item_name, Qquote))
+         {
+           /* This is the boundary between left-side elts
+              and right-side elts.  Stop incrementing right_count.  */
+           boundary_seen = true;
+           i++;
+           continue;
+         }
+       if (nb_buttons >= 9)
+         {
+           free_menubar_widget_value_tree (first_wv);
+           *error_name = "Too many dialog items";
+           return Qnil;
+         }
+
+       wv = make_widget_value (button_names[nb_buttons],
+                               SSDATA (item_name),
+                               !NILP (enable), Qnil);
+       prev_wv->next = wv;
+       if (!NILP (descrip))
+         wv->key = SSDATA (descrip);
+       wv->call_data = aref_addr (menu_items, i);
+       prev_wv = wv;
+
+       if (! boundary_seen)
+         left_count++;
+
+       nb_buttons++;
+       i += MENU_ITEMS_ITEM_LENGTH;
+      }
+
+    /* If the boundary was not specified,
+       by default put half on the left and half on the right.  */
+    if (! boundary_seen)
+      left_count = nb_buttons - nb_buttons / 2;
+
+    wv = make_widget_value (dialog_name, NULL, false, Qnil);
+
+    /*  Frame title: 'Q' = Question, 'I' = Information.
+        Can also have 'E' = Error if, one day, we want
+        a popup for errors. */
+    if (NILP (header))
+      dialog_name[0] = 'Q';
+    else
+      dialog_name[0] = 'I';
+
+    /* Dialog boxes use a really stupid name encoding
+       which specifies how many buttons to use
+       and how many buttons are on the right. */
+    dialog_name[1] = '0' + nb_buttons;
+    dialog_name[2] = 'B';
+    dialog_name[3] = 'R';
+    /* Number of buttons to put on the right.  */
+    dialog_name[4] = '0' + nb_buttons - left_count;
+    dialog_name[5] = 0;
+    wv->contents = first_wv;
+    first_wv = wv;
+  }
+
+  /* No selection has been chosen yet.  */
+  menu_item_selection = 0;
+
+  /* Make sure to free the widget_value objects we used to specify the
+     contents even with longjmp.  */
+  record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
+
+  /* Actually create and show the dialog.  */
+  create_and_show_dialog (f, first_wv);
+
+  unbind_to (specpdl_count, Qnil);
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+  if (menu_item_selection != 0)
+    {
+      i = 0;
+      while (i < menu_items_used)
+       {
+         Lisp_Object entry;
+
+         if (EQ (AREF (menu_items, i), Qt))
+           i += MENU_ITEMS_PANE_LENGTH;
+         else if (EQ (AREF (menu_items, i), Qquote))
+           {
+             /* This is the boundary between left-side elts and
+                right-side elts.  */
+             ++i;
+           }
+         else
+           {
+             entry
+               = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+             if (menu_item_selection == aref_addr (menu_items, i))
+               return entry;
+             i += MENU_ITEMS_ITEM_LENGTH;
+           }
+       }
+    }
+  else
+    /* Make "Cancel" equivalent to C-g.  */
+    quit ();
+
   return Qnil;
 }
 
+Lisp_Object
+pgtk_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+{
+  Lisp_Object title;
+  const char *error_name;
+  Lisp_Object selection;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  check_window_system (f);
+
+  /* Decode the dialog items from what was specified.  */
+  title = Fcar (contents);
+  CHECK_STRING (title);
+  record_unwind_protect_void (unuse_menu_items);
+
+  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 = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+  list_of_panes (list1 (contents));
+
+  /* Display them in a dialog box.  */
+  block_input ();
+  selection = pgtk_dialog_show (f, title, header, &error_name);
+  unblock_input ();
+
+  unbind_to (specpdl_count, Qnil);
+  discard_menu_items ();
+
+  if (error_name) error ("%s", error_name);
+  return selection;
+}
+
+/* Detect if a dialog or menu has been posted.  MSDOS has its own
+   implementation on msdos.c.  */
+
+int
+popup_activated (void)
+{
+  return popup_activated_flag;
+}
+
 /* The following is used by delayed window autoselection.  */
 
 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, 
Smenu_or_popup_active_p, 0, 0, 0,
-       doc: /* SKIP: real doc in xmenu.c.  */)
+       doc: /* Return t if a menu or popup dialog is active.
+\(On MS Windows, this refers to the selected frame.)  */)
   (void)
 {
-  /* struct frame *f = SELECTED_FRAME (); */
-  /* return (f->output_data.pgtk->menubar_active > 0) ? Qt : Qnil; */
-  return Qnil;
+  return (popup_activated ()) ? Qt : Qnil;
 }
 
+static void syms_of_pgtkmenu_for_pdumper (void);
+
 void
 syms_of_pgtkmenu (void)
 {
-  // current_popup_menu = NULL;
-  // PDUMPER_IGNORE (current_popup_menu);
-
   DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
-  DEFSYM (Qunsupported__w32_dialog, "unsupported--w32-dialog");
-
   defsubr (&Smenu_or_popup_active_p);
+
+  DEFSYM (Qframe_monitor_workarea, "frame-monitor-workarea");
+
+  defsubr (&Sx_menu_bar_open_internal);
+  Ffset (intern_c_string ("accelerate-menu"),
+        intern_c_string (Sx_menu_bar_open_internal.s.symbol_name));
+
+  pdumper_do_now_and_after_load (syms_of_pgtkmenu_for_pdumper);
+}
+
+static void
+syms_of_pgtkmenu_for_pdumper (void)
+{
 }
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 0ab86b7..e5f89cf 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -1138,7 +1138,7 @@ x_draw_glyph_string_background (struct glyph_string *s, 
bool force_p)
   if (!s->background_filled_p)
     {
       PGTK_TRACE("x_draw_glyph_string_background: 1.");
-      int box_line_width = max (s->face->box_line_width, 0);
+      int box_line_width = max (s->face->box_horizontal_line_width, 0);
 
       PGTK_TRACE("x_draw_glyph_string_background: 2. %d, %d.",
                   FONT_HEIGHT (s->font), s->height - 2 * box_line_width);
@@ -1201,7 +1201,7 @@ x_draw_glyph_string_foreground (struct glyph_string *s)
      of S to the right of that box line.  */
   if (s->face->box != FACE_NO_BOX
       && s->first_glyph->left_box_line_p)
-    x = s->x + eabs (s->face->box_line_width);
+    x = s->x + max (s->face->box_vertical_line_width, 0);
   else
     x = s->x;
 
@@ -1250,7 +1250,7 @@ x_draw_composite_glyph_string_foreground (struct 
glyph_string *s)
      of S to the right of that box line.  */
   if (s->face && s->face->box != FACE_NO_BOX
       && s->first_glyph->left_box_line_p)
-    x = s->x + eabs (s->face->box_line_width);
+    x = s->x + max (s->face->box_vertical_line_width, 0);
   else
     x = s->x;
 
@@ -1342,7 +1342,7 @@ x_draw_glyphless_glyph_string_foreground (struct 
glyph_string *s)
      of S to the right of that box line.  */
   if (s->face && s->face->box != FACE_NO_BOX
       && s->first_glyph->left_box_line_p)
-    x = s->x + eabs (s->face->box_line_width);
+    x = s->x + max (s->face->box_vertical_line_width, 0);
   else
     x = s->x;
 
@@ -1637,7 +1637,7 @@ x_set_clip_rectangles (struct frame *f, cairo_t *cr, 
XRectangle *rectangles, int
 static void
 x_draw_relief_rect (struct frame *f,
                    int left_x, int top_y, int right_x, int bottom_y,
-                   int width, bool raised_p, bool top_p, bool bot_p,
+                   int hwidth, int vwidth, bool raised_p, bool top_p, bool 
bot_p,
                    bool left_p, bool right_p,
                    XRectangle *clip_rect)
 {
@@ -1662,7 +1662,7 @@ x_draw_relief_rect (struct frame *f,
   if (left_p)
     {
       pgtk_fill_rectangle (f, top_left_color, left_x, top_y,
-                            width, bottom_y + 1 - top_y);
+                            vwidth, bottom_y + 1 - top_y);
       if (top_p)
        corners |= 1 << CORNER_TOP_LEFT;
       if (bot_p)
@@ -1670,8 +1670,8 @@ x_draw_relief_rect (struct frame *f,
     }
   if (right_p)
     {
-      pgtk_fill_rectangle (f, bottom_right_color, right_x + 1 - width, top_y,
-                            width, bottom_y + 1 - top_y);
+      pgtk_fill_rectangle (f, bottom_right_color, right_x + 1 - vwidth, top_y,
+                            vwidth, bottom_y + 1 - top_y);
       if (top_p)
        corners |= 1 << CORNER_TOP_RIGHT;
       if (bot_p)
@@ -1681,25 +1681,25 @@ x_draw_relief_rect (struct frame *f,
     {
       if (!right_p)
        pgtk_fill_rectangle (f, top_left_color, left_x, top_y,
-                              right_x + 1 - left_x, width);
+                              right_x + 1 - left_x, hwidth);
       else
        x_fill_trapezoid_for_relief (f, top_left_color, left_x, top_y,
-                                    right_x + 1 - left_x, width, 1);
+                                    right_x + 1 - left_x, hwidth, 1);
     }
   if (bot_p)
     {
       if (!left_p)
-       pgtk_fill_rectangle (f, bottom_right_color, left_x, bottom_y + 1 - 
width,
-                              right_x + 1 - left_x, width);
+       pgtk_fill_rectangle (f, bottom_right_color, left_x, bottom_y + 1 - 
hwidth,
+                              right_x + 1 - left_x, hwidth);
       else
        x_fill_trapezoid_for_relief (f, bottom_right_color,
-                                    left_x, bottom_y + 1 - width,
-                                    right_x + 1 - left_x, width, 0);
+                                    left_x, bottom_y + 1 - hwidth,
+                                    right_x + 1 - left_x, hwidth, 0);
     }
-  if (left_p && width != 1)
+  if (left_p && vwidth > 1)
     pgtk_fill_rectangle (f, bottom_right_color, left_x, top_y,
                           1, bottom_y + 1 - top_y);
-  if (top_p && width != 1)
+  if (top_p && hwidth > 1)
     pgtk_fill_rectangle (f, bottom_right_color, left_x, top_y,
                           right_x + 1 - left_x, 1);
   if (corners)
@@ -1721,8 +1721,8 @@ x_draw_relief_rect (struct frame *f,
 
 static void
 x_draw_box_rect (struct glyph_string *s,
-                int left_x, int top_y, int right_x, int bottom_y, int width,
-                bool left_p, bool right_p, XRectangle *clip_rect)
+                int left_x, int top_y, int right_x, int bottom_y, int hwidth,
+                int vwidth, bool left_p, bool right_p, XRectangle *clip_rect)
 {
   unsigned long foreground_backup;
 
@@ -1735,21 +1735,21 @@ x_draw_box_rect (struct glyph_string *s,
 
   /* Top.  */
   pgtk_fill_rectangle (s->f, s->xgcv.foreground,
-                        left_x, top_y, right_x - left_x + 1, width);
+                        left_x, top_y, right_x - left_x + 1, hwidth);
 
   /* Left.  */
   if (left_p)
     pgtk_fill_rectangle (s->f, s->xgcv.foreground,
-                          left_x, top_y, width, bottom_y - top_y + 1);
+                          left_x, top_y, vwidth, bottom_y - top_y + 1);
 
   /* Bottom.  */
   pgtk_fill_rectangle (s->f, s->xgcv.foreground,
-                        left_x, bottom_y - width + 1, right_x - left_x + 1, 
width);
+                        left_x, bottom_y - hwidth + 1, right_x - left_x + 1, 
hwidth);
 
   /* Right.  */
   if (right_p)
     pgtk_fill_rectangle (s->f, s->xgcv.foreground,
-                          right_x - width + 1, top_y, width, bottom_y - top_y 
+ 1);
+                          right_x - vwidth + 1, top_y, vwidth, bottom_y - 
top_y + 1);
 
   s->xgcv.foreground = foreground_backup;
 
@@ -1762,7 +1762,7 @@ x_draw_box_rect (struct glyph_string *s,
 static void
 x_draw_glyph_string_box (struct glyph_string *s)
 {
-  int width, left_x, right_x, top_y, bottom_y, last_x;
+  int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
   bool raised_p, left_p, right_p;
   struct glyph *last_glyph;
   XRectangle clip_rect;
@@ -1776,7 +1776,8 @@ x_draw_glyph_string_box (struct glyph_string *s)
                ? s->first_glyph
                : s->first_glyph + s->nchars - 1);
 
-  width = eabs (s->face->box_line_width);
+  vwidth = eabs (s->face->box_vertical_line_width);
+  hwidth = eabs (s->face->box_horizontal_line_width);
   raised_p = s->face->box == FACE_RAISED_BOX;
   left_x = s->x;
   right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
@@ -1797,13 +1798,13 @@ x_draw_glyph_string_box (struct glyph_string *s)
   get_glyph_string_clip_rect (s, &clip_rect);
 
   if (s->face->box == FACE_SIMPLE_BOX)
-    x_draw_box_rect (s, left_x, top_y, right_x, bottom_y, width,
-                    left_p, right_p, &clip_rect);
+    x_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+                    vwidth, left_p, right_p, &clip_rect);
   else
     {
       x_setup_relief_colors (s);
-      x_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y,
-                         width, raised_p, true, true, left_p, right_p,
+      x_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y, hwidth,
+                         vwidth, raised_p, true, true, left_p, right_p,
                          &clip_rect);
     }
 }
@@ -1896,7 +1897,7 @@ x_draw_image_relief (struct glyph_string *s)
   if (s->face->box != FACE_NO_BOX
       && s->first_glyph->left_box_line_p
       && s->slice.x == 0)
-    x += eabs (s->face->box_line_width);
+    x += max (s->face->box_vertical_line_width, 0);
 
   /* If there is a margin around the image, adjust x- and y-position
      by that margin.  */
@@ -1964,7 +1965,7 @@ x_draw_image_relief (struct glyph_string *s)
 
   x_setup_relief_colors (s);
   get_glyph_string_clip_rect (s, &r);
-  x_draw_relief_rect (s->f, x, y, x1, y1, thick, raised_p,
+  x_draw_relief_rect (s->f, x, y, x1, y1, thick, thick, raised_p,
                      top_p, bot_p, left_p, right_p, &r);
 }
 
@@ -1997,7 +1998,7 @@ x_draw_image_foreground (struct glyph_string *s)
   if (s->face->box != FACE_NO_BOX
       && s->first_glyph->left_box_line_p
       && s->slice.x == 0)
-    x += eabs (s->face->box_line_width);
+    x += max (s->face->box_vertical_line_width, 0);
 
   /* If there is a margin around the image, adjust x- and y-position
      by that margin.  */
@@ -2028,8 +2029,8 @@ x_draw_image_foreground (struct glyph_string *s)
 static void
 x_draw_image_glyph_string (struct glyph_string *s)
 {
-  int box_line_hwidth = eabs (s->face->box_line_width);
-  int box_line_vwidth = max (s->face->box_line_width, 0);
+  int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+  int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
   int height;
 
   height = s->height;
@@ -5902,6 +5903,10 @@ button_event(GtkWidget *widget, GdkEvent *event, 
gpointer *user_data)
     {
       dpyinfo->grabbed |= (1 << event->button.button);
       dpyinfo->last_mouse_frame = f;
+
+      if (dpyinfo->last_click_event != NULL)
+       gdk_event_free(dpyinfo->last_click_event);
+      dpyinfo->last_click_event = gdk_event_copy(event);
     }
   else
     dpyinfo->grabbed &= ~(1 << event->button.button);
diff --git a/src/pgtkterm.h b/src/pgtkterm.h
index bb9dca6..73e04b7 100644
--- a/src/pgtkterm.h
+++ b/src/pgtkterm.h
@@ -222,7 +222,8 @@ struct pgtk_display_info
   /* Modifier masks in gdk */
   int meta_mod_mask, alt_mod_mask;
 
-  /* whether to use Gtk's IM context. */
+  /* The last click event. */
+  GdkEvent *last_click_event;
 
   /* input method */
   struct {
@@ -572,7 +573,7 @@ extern void pgtk_cr_destroy_frame_context(struct frame *f);
 
 /* Defined in pgtkmenu.c */
 extern Lisp_Object pgtk_popup_dialog (struct frame *f, Lisp_Object header, 
Lisp_Object contents);
-extern Lisp_Object pgtk_dialog_show (struct frame *f, Lisp_Object title, 
Lisp_Object header, char **error);
+extern Lisp_Object pgtk_dialog_show (struct frame *f, Lisp_Object title, 
Lisp_Object header, const char **error_name);
 extern void initialize_frame_menubar (struct frame *);
 
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]