popup menu keybinding



Hi,

I went ahead and added Shift+F10 and the Menu key as keybindings for
popping up widget menus. Implies a popup_menu RUN_ACTION signal on
GtkWidget, and implementations of said signal on GtkEntry and
GtkTextView.

Patch appended, pretty straightforward. Includes a couple random fixes
too.

Havoc

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.1830
diff -u -u -r1.1830 ChangeLog
--- ChangeLog	2001/03/19 22:40:31	1.1830
+++ ChangeLog	2001/03/20 00:59:20
@@ -1,5 +1,25 @@
 2001-03-19  Havoc Pennington  <hp redhat com>
 
+	* gtk/gtkimcontextsimple.c (check_hex): do better validation of 
+	inserted unicode from Ctrl-Shift-hex input method
+
+	* gtk/gtktextbtree.c (_gtk_text_btree_insert): remove utf8
+	validatation here, already done at GtkTextBuffer level.
+
+	* gtk/gtkwidget.c (gtk_widget_class_init): add binding set, add
+	popup_menu run action signal and Shift+F10 and Menu keybindings.
+
+	* gtk/gtkentry.c: implement a default handler for popup_menu
+
+	* gtk/gtktextview.c: implement a default handler for popup_menu
+
+	* gtk/gtkmenu.c (gtk_menu_popup): select first item if popup is
+	from a key event
+	
+	* gtk/gtklabel.c: remove "trailer" cruft
+
+2001-03-19  Havoc Pennington  <hp redhat com>
+
 	* gtk/gtkiconfactory.c: Make GtkIconSource an opaque datatype, and
 	add a bunch of accessor functions. This is because we have
 	reasonable expectations of extending what fields it contains in
Index: gtk/gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.115
diff -u -u -r1.115 gtkentry.c
--- gtk/gtkentry.c	2001/03/16 01:44:54	1.115
+++ gtk/gtkentry.c	2001/03/20 00:59:20
@@ -207,6 +207,7 @@
 static void gtk_entry_paste_clipboard    (GtkEntry        *entry);
 static void gtk_entry_toggle_overwrite   (GtkEntry        *entry);
 static void gtk_entry_real_activate      (GtkEntry        *entry);
+static void gtk_entry_popup_menu         (GtkWidget      *widget);
 
 /* IM Context Callbacks
  */
@@ -248,7 +249,7 @@
 static void         gtk_entry_paste                    (GtkEntry       *entry,
 							GdkAtom         selection);
 static void         gtk_entry_update_primary_selection (GtkEntry       *entry);
-static void         gtk_entry_popup_menu               (GtkEntry       *entry,
+static void         gtk_entry_do_popup                 (GtkEntry       *entry,
 							GdkEventButton *event);
 
 static GtkWidgetClass *parent_class = NULL;
@@ -348,6 +349,8 @@
   widget_class->drag_data_get = gtk_entry_drag_data_get;
   widget_class->drag_data_delete = gtk_entry_drag_data_delete;
 
+  widget_class->popup_menu = gtk_entry_popup_menu;
+  
   class->insert_text = gtk_entry_real_insert_text;
   class->delete_text = gtk_entry_real_delete_text;
   class->move_cursor = gtk_entry_move_cursor;
@@ -1309,7 +1312,7 @@
     }
   else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
     {
-      gtk_entry_popup_menu (entry, event);
+      gtk_entry_do_popup (entry, event);
       entry->button = 0;	/* Don't wait for release, since the menu will gtk_grab_add */
 
       return TRUE;
@@ -3157,9 +3160,36 @@
 }
 
 static void
-gtk_entry_popup_menu (GtkEntry       *entry,
-		      GdkEventButton *event)
+popup_position_func (GtkMenu   *menu,
+                     gint      *x,
+                     gint      *y,
+                     gboolean  *push_in,
+                     gpointer	user_data)
 {
+  GtkEntry *entry;
+  GtkWidget *widget;
+  GtkRequisition req;
+  
+  entry = GTK_ENTRY (user_data);  
+  widget = GTK_WIDGET (entry);
+  
+  g_return_if_fail (GTK_WIDGET_REALIZED (entry));
+
+  gdk_window_get_origin (widget->window, x, y);      
+
+  gtk_widget_size_request (entry->popup_menu, &req);
+  
+  *x += widget->allocation.width / 2;
+  *y += widget->allocation.height;
+
+  *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
+  *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
+}
+
+static void
+gtk_entry_do_popup (GtkEntry       *entry,
+                    GdkEventButton *event)
+{
   if (!entry->popup_menu)
     {
       GtkWidget *menuitem;
@@ -3182,9 +3212,20 @@
 					    GTK_MENU_SHELL (entry->popup_menu));
     }
 
-  gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
-		  NULL, NULL,
-		  event->button, event->time);
+  if (event)
+    gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+                    NULL, NULL,
+                    event->button, event->time);
+  else
+    gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+                    popup_position_func, entry,
+                    0, gtk_get_current_event_time ());
+}
+
+static void
+gtk_entry_popup_menu (GtkWidget *widget)
+{
+  gtk_entry_do_popup (GTK_ENTRY (widget), NULL);
 }
 
 static void
Index: gtk/gtkimcontextsimple.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkimcontextsimple.c,v
retrieving revision 1.12
diff -u -u -r1.12 gtkimcontextsimple.c
--- gtk/gtkimcontextsimple.c	2000/12/11 15:51:19	1.12
+++ gtk/gtkimcontextsimple.c	2001/03/20 00:59:20
@@ -827,7 +827,9 @@
   gint len;
 
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
-      
+
+  g_return_if_fail (g_unichar_validate (ch));
+  
   len = g_unichar_to_utf8 (ch, buf);
   buf[len] = '\0';
 
@@ -981,12 +983,10 @@
     }
   else
     g_string_free (str, TRUE);
-  
-  if (n > 0xFFFF)
-    return FALSE; /* too many digits */
 
-  if (n == 0)
-    return FALSE; /* don't insert nul bytes */
+  /* don't allow invalid Unicode or nul bytes */
+  if (n == 0 || !g_unichar_validate (n))
+    return FALSE;
   
   context_simple->tentative_match = n;
   context_simple->tentative_match_len = n_compose;
Index: gtk/gtklabel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.c,v
retrieving revision 1.79
diff -u -u -r1.79 gtklabel.c
--- gtk/gtklabel.c	2001/03/09 16:43:15	1.79
+++ gtk/gtklabel.c	2001/03/20 00:59:20
@@ -57,13 +57,11 @@
 static void gtk_label_set_property      (GObject          *object,
 					 guint             prop_id,
 					 const GValue     *value,
-					 GParamSpec       *pspec,
-					 const gchar      *trailer);
+					 GParamSpec       *pspec);
 static void gtk_label_get_property      (GObject          *object,
 					 guint             prop_id,
 					 GValue           *value,
-					 GParamSpec       *pspec,
-					 const gchar      *trailer);
+					 GParamSpec       *pspec);
 static void gtk_label_finalize          (GObject          *object);
 static void gtk_label_size_request      (GtkWidget        *widget,
 					 GtkRequisition   *requisition);
@@ -175,7 +173,7 @@
   widget_class->button_press_event = gtk_label_button_press;
   widget_class->button_release_event = gtk_label_button_release;
   widget_class->motion_notify_event = gtk_label_motion;
-
+  
   g_object_class_install_property (G_OBJECT_CLASS(object_class),
                                    PROP_LABEL,
                                    g_param_spec_string ("label",
@@ -251,8 +249,7 @@
 gtk_label_set_property (GObject      *object,
 			guint         prop_id,
 			const GValue *value,
-			GParamSpec   *pspec,
-			const gchar  *trailer)
+			GParamSpec   *pspec)
 {
   GtkLabel *label;
   
@@ -298,8 +295,7 @@
 gtk_label_get_property (GObject     *object,
 			guint        prop_id,
 			GValue      *value,
-			GParamSpec  *pspec,
-			const gchar *trailer)
+			GParamSpec  *pspec)
 {
   GtkLabel *label;
   
Index: gtk/gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.59
diff -u -u -r1.59 gtkmenu.c
--- gtk/gtkmenu.c	2001/03/09 13:28:25	1.59
+++ gtk/gtkmenu.c	2001/03/20 00:59:20
@@ -518,6 +518,18 @@
       if ((current_event->type != GDK_BUTTON_PRESS) &&
 	  (current_event->type != GDK_ENTER_NOTIFY))
 	menu_shell->ignore_enter = TRUE;
+
+      /* Also, if we're popping up from a key event, select the first
+       * item in the menu. Bad hack, but no better way to do it
+       * in current menu framework.
+       */
+      if (current_event->type == GDK_KEY_PRESS &&
+          GTK_MENU_SHELL (menu)->children)
+        {
+          gtk_menu_shell_select_item (GTK_MENU_SHELL (menu),
+                                      GTK_MENU_SHELL (menu)->children->data);
+        }
+      
       gdk_event_free (current_event);
     }
 
@@ -576,7 +588,7 @@
 			     NULL, NULL, activate_time) == 0))
 	{
 	  if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
-			      activate_time) == 0)
+                                 activate_time) == 0)
 	    GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
 	  else
 	    gdk_pointer_ungrab (activate_time);
Index: gtk/gtktextbtree.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextbtree.c,v
retrieving revision 1.35
diff -u -u -r1.35 gtktextbtree.c
--- gtk/gtktextbtree.c	2001/02/19 23:27:26	1.35
+++ gtk/gtktextbtree.c	2001/03/20 00:59:20
@@ -959,8 +959,6 @@
   /* Invalidate all iterators */
   chars_changed (tree);
   segments_changed (tree);
-
-  g_assert (g_utf8_validate (text, len, NULL));
   
   /*
    * Chop the text up into lines and create a new segment for
Index: gtk/gtktextbuffer.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextbuffer.c,v
retrieving revision 1.44
diff -u -u -r1.44 gtktextbuffer.c
--- gtk/gtktextbuffer.c	2001/03/16 19:32:19	1.44
+++ gtk/gtktextbuffer.c	2001/03/20 00:59:20
@@ -477,7 +477,7 @@
   if (len < 0)
     len = strlen (text);
 
-  g_assert (g_utf8_validate (text, len, NULL));
+  g_return_if_fail (g_utf8_validate (text, len, NULL));
   
   if (len > 0)
     {
Index: gtk/gtktextview.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextview.c,v
retrieving revision 1.74
diff -u -u -r1.74 gtktextview.c
--- gtk/gtktextview.c	2001/03/19 21:06:37	1.74
+++ gtk/gtktextview.c	2001/03/20 00:59:20
@@ -208,6 +208,8 @@
                                                   GtkAdjustment *hadj,
                                                   GtkAdjustment *vadj);
 
+static void gtk_text_view_popup_menu             (GtkWidget     *widget);
+
 static void gtk_text_view_move_cursor      (GtkTextView           *text_view,
                                             GtkMovementStep        step,
                                             gint                   count,
@@ -270,7 +272,7 @@
 static GtkAdjustment* get_hadjustment            (GtkTextView       *text_view);
 static GtkAdjustment* get_vadjustment            (GtkTextView       *text_view);
 
-static void gtk_text_view_popup_menu             (GtkTextView       *text_view,
+static void gtk_text_view_do_popup               (GtkTextView       *text_view,
 						  GdkEventButton    *event);
 
 static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
@@ -473,6 +475,8 @@
   widget_class->drag_drop = gtk_text_view_drag_drop;
   widget_class->drag_data_received = gtk_text_view_drag_data_received;
 
+  widget_class->popup_menu = gtk_text_view_popup_menu;
+  
   container_class->add = gtk_text_view_add;
   container_class->remove = gtk_text_view_remove;
   container_class->forall = gtk_text_view_forall;
@@ -3046,7 +3050,7 @@
         }
       else if (event->button == 3)
         {
-	  gtk_text_view_popup_menu (text_view, event);
+	  gtk_text_view_do_popup (text_view, event);
         }
     }
   else if ((event->type == GDK_2BUTTON_PRESS ||
@@ -4888,9 +4892,72 @@
 }
 
 static void
-gtk_text_view_popup_menu (GtkTextView    *text_view,
-			  GdkEventButton *event)
+popup_position_func (GtkMenu   *menu,
+                     gint      *x,
+                     gint      *y,
+                     gboolean  *push_in,
+                     gpointer	user_data)
 {
+  GtkTextView *text_view;
+  GtkWidget *widget;
+  GdkRectangle cursor_rect;
+  GdkRectangle onscreen_rect;
+  gint root_x, root_y;
+  GtkTextIter iter;
+  GtkRequisition req;      
+  
+  text_view = GTK_TEXT_VIEW (user_data);
+  widget = GTK_WIDGET (text_view);
+  
+  g_return_if_fail (GTK_WIDGET_REALIZED (text_view));
+
+  gdk_window_get_origin (widget->window, &root_x, &root_y);
+
+  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+                                    &iter,
+                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
+
+  gtk_text_view_get_iter_location (text_view,
+                                   &iter,
+                                   &cursor_rect);
+
+  gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
+  
+  gtk_widget_size_request (text_view->popup_menu, &req);
+
+  /* can't use rectangle_intersect since cursor rect can have 0 width */
+  if (cursor_rect.x >= onscreen_rect.x &&
+      cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
+      cursor_rect.y >= onscreen_rect.y &&
+      cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
+    {    
+      gtk_text_view_buffer_to_window_coords (text_view,
+                                             GTK_TEXT_WINDOW_WIDGET,
+                                             cursor_rect.x, cursor_rect.y,
+                                             &cursor_rect.x, &cursor_rect.y);
+
+      *x = root_x + cursor_rect.x + cursor_rect.width;
+      *y = root_y + cursor_rect.y + cursor_rect.height;
+    }
+  else
+    {
+      /* Just center the menu, since cursor is offscreen. */      
+      *x = root_x + (widget->allocation.width / 2 - req.width / 2);
+      *y = root_y + (widget->allocation.height / 2 - req.height / 2);      
+    }
+
+  /* Ensure sanity */
+  *x = CLAMP (*x, root_x, (root_x + widget->allocation.width));
+  *y = CLAMP (*y, root_y, (root_y + widget->allocation.height));
+
+  *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
+  *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
+}
+
+static void
+gtk_text_view_do_popup (GtkTextView    *text_view,
+                        GdkEventButton *event)
+{
   if (!text_view->popup_menu)
     {
       GtkWidget *menuitem;
@@ -4913,12 +4980,21 @@
 					    GTK_MENU_SHELL (text_view->popup_menu));
     }
 
-  gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
-		  NULL, NULL,
-		  event->button, event->time);
+  if (event)
+    gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
+                    NULL, NULL,
+                    event->button, event->time);
+  else
+    gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
+                    popup_position_func, text_view,
+                    0, gtk_get_current_event_time ());
 }
-
 
+static void
+gtk_text_view_popup_menu (GtkWidget *widget)
+{
+  gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);  
+}
 
 /* Child GdkWindows */
 
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.195
diff -u -u -r1.195 gtkwidget.c
--- gtk/gtkwidget.c	2001/03/18 04:50:34	1.195
+++ gtk/gtkwidget.c	2001/03/20 00:59:20
@@ -40,6 +40,7 @@
 #include "gdk/gdk.h"
 #include "gdk/gdkprivate.h" /* Used in gtk_reset_shapes_recurse to avoid copy */
 #include "gobject/gvaluecollector.h"
+#include "gdk/gdkkeysyms.h"
 
 
 #define WIDGET_CLASS(w)	 GTK_WIDGET_GET_CLASS (w)
@@ -100,6 +101,7 @@
   NO_EXPOSE_EVENT,
   VISIBILITY_NOTIFY_EVENT,
   WINDOW_STATE_EVENT,
+  POPUP_MENU,
   LAST_SIGNAL
 };
 
@@ -261,6 +263,7 @@
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+  GtkBindingSet *binding_set;
   
   parent_class = gtk_type_class (GTK_TYPE_OBJECT);
 
@@ -781,6 +784,21 @@
                     gtk_marshal_BOOLEAN__BOXED,
 		    GTK_TYPE_BOOL, 1,
 		    GTK_TYPE_GDK_EVENT);
+  widget_signals[POPUP_MENU] =
+    gtk_signal_new ("popup_menu",
+		    GTK_RUN_LAST | GTK_RUN_ACTION,
+		    GTK_CLASS_TYPE (object_class),
+		    GTK_SIGNAL_OFFSET (GtkWidgetClass, popup_menu),
+                    gtk_marshal_NONE__NONE,
+		    GTK_TYPE_NONE, 0);
+  
+  binding_set = gtk_binding_set_by_class (klass);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK,
+                                "popup_menu", 0);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Menu, 0,
+                                "popup_menu", 0);  
 }
 
 static void
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.98
diff -u -u -r1.98 gtkwidget.h
--- gtk/gtkwidget.h	2001/03/18 04:50:34	1.98
+++ gtk/gtkwidget.h	2001/03/20 00:59:20
@@ -370,8 +370,11 @@
 				    GtkSelectionData   *selection_data,
 				    guint               info,
 				    guint               time);
+
+  /* Signals used only for keybindings */
+  void (* popup_menu)              (GtkWidget          *widget);
   
-  /* Padding for future expandsion */
+  /* Padding for future expansion */
   GtkFunction pad1;
   GtkFunction pad2;
   GtkFunction pad3;




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