popup menu keybinding
- From: Havoc Pennington <hp redhat com>
- To: gtk-devel-list gnome org
- Subject: popup menu keybinding
- Date: 19 Mar 2001 20:04:29 -0500
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]