diff --git a/ChangeLog b/ChangeLog index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 51700f8c45..1d47c86b5a 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,40 @@ +Wed Mar 28 19:20:43 2001 Owen Taylor + + * gtk/testtext.c (create_buffer): Add missing NULL on + valist. + + * gtk/gtkcheckmenuitem.c (gtk_check_menu_item_class_init): Fix + c-n-p problem with INCONSISTENT property. + + [ Patch from Havoc Pennington ] + + * gtk/gtkentry.h (struct _GtkEntryClass): add same populate_popup + hook. + + * gtk/gtktextview.h (struct _GtkTextViewClass): add populate_popup + signal as a hook for extending the default popup menu + + * gtk/gtkimmulticontext.c (gtk_im_multicontext_append_menuitems): + use radio menu items for the input method menuitems + + * 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 + Wed Mar 28 17:27:12 2001 Jonathan Blandford * gtk/gtkrbtree.c (_gtk_rbtree_reorder): new function to diff --git a/gtk/gtkcheckmenuitem.c b/gtk/gtkcheckmenuitem.c index d76bbe256f..efeb6fcf07 100644 --- a/gtk/gtkcheckmenuitem.c +++ b/gtk/gtkcheckmenuitem.c @@ -117,7 +117,7 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass) G_PARAM_READABLE | G_PARAM_WRITABLE)); g_object_class_install_property (G_OBJECT_CLASS (object_class), - PROP_ACTIVE, + PROP_INCONSISTENT, g_param_spec_boolean ("inconsistent", _("Inconsistent"), _("Whether to display an \"inconsistent\" state."), diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 046980492a..9597e85255 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -58,6 +58,7 @@ enum { DELETE_TEXT, CHANGED, ACTIVATE, + POPULATE_POPUP, MOVE_CURSOR, INSERT_AT_CURSOR, DELETE_FROM_CURSOR, @@ -207,6 +208,7 @@ static void gtk_entry_copy_clipboard (GtkEntry *entry); 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 +250,7 @@ static char * gtk_entry_get_public_chars (GtkEntry *entry, 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 gboolean gtk_entry_activate_mnemonic (GtkWidget *widget, gboolean group_cycling); @@ -351,6 +353,8 @@ gtk_entry_class_init (GtkEntryClass *class) 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; @@ -456,6 +460,14 @@ gtk_entry_class_init (GtkEntryClass *class) gtk_marshal_VOID__VOID, GTK_TYPE_NONE, 0); + signals[POPULATE_POPUP] = + gtk_signal_new ("populate_popup", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkEntryClass, populate_popup), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_MENU); + /* Action signals */ signals[ACTIVATE] = @@ -1312,7 +1324,7 @@ gtk_entry_button_press (GtkWidget *widget, } 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; @@ -3149,7 +3161,8 @@ static void append_action_signal (GtkEntry *entry, GtkWidget *menu, const gchar *label, - const gchar *signal) + const gchar *signal, + gboolean sensitive) { GtkWidget *menuitem = gtk_menu_item_new_with_label (label); @@ -3157,6 +3170,8 @@ append_action_signal (GtkEntry *entry, gtk_signal_connect (GTK_OBJECT (menuitem), "activate", GTK_SIGNAL_FUNC (activate_cb), entry); + gtk_widget_set_sensitive (menuitem, sensitive); + gtk_widget_show (menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); } @@ -3169,34 +3184,91 @@ popup_menu_detach (GtkWidget *attach_widget, } 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) { - if (!entry->popup_menu) - { - GtkWidget *menuitem; + 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) +{ + + GtkWidget *menuitem; + GtkWidget *submenu; + gboolean have_selection; + + if (entry->popup_menu) + gtk_widget_destroy (entry->popup_menu); + + entry->popup_menu = gtk_menu_new (); + + gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu), + GTK_WIDGET (entry), + popup_menu_detach); + + have_selection = entry->current_pos != entry->selection_bound; + + append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard", + have_selection); + append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard", + have_selection); + append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard", + TRUE); + + menuitem = gtk_separator_menu_item_new (); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem); - entry->popup_menu = gtk_menu_new (); + menuitem = gtk_menu_item_new_with_label (_("Input Methods")); + gtk_widget_show (menuitem); + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); - gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu), - GTK_WIDGET (entry), - popup_menu_detach); + gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem); + + gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context), + GTK_MENU_SHELL (submenu)); - append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard"); - append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard"); - append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard"); + gtk_signal_emit (GTK_OBJECT (entry), + signals[POPULATE_POPUP], + entry->popup_menu); + + 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 ()); +} - menuitem = gtk_separator_menu_item_new (); - gtk_widget_show (menuitem); - gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem); - - gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context), - GTK_MENU_SHELL (entry->popup_menu)); - } - - gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL, - NULL, NULL, - event->button, event->time); +static void +gtk_entry_popup_menu (GtkWidget *widget) +{ + gtk_entry_do_popup (GTK_ENTRY (widget), NULL); } static void diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h index 5ba3df752f..f94072b98e 100644 --- a/gtk/gtkentry.h +++ b/gtk/gtkentry.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef __cplusplus @@ -118,6 +119,10 @@ struct _GtkEntryClass gint start_pos, gint end_pos); + /* Hook to customize right-click popup */ + void (* populate_popup) (GtkEntry *entry, + GtkMenu *menu); + /* Action signals */ void (* activate) (GtkEntry *entry); diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index 36da2c96a9..d1c5f543d0 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -827,7 +827,9 @@ gtk_im_context_simple_commit_char (GtkIMContext *context, 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 @@ check_hex (GtkIMContextSimple *context_simple, } 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; diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c index 80e75f0db5..341b56a02d 100644 --- a/gtk/gtkimmulticontext.c +++ b/gtk/gtkimmulticontext.c @@ -28,7 +28,7 @@ #include "gtksignal.h" #include "gtkimmulticontext.h" #include "gtkimmodule.h" -#include "gtkmenuitem.h" +#include "gtkradiomenuitem.h" static void gtk_im_multicontext_class_init (GtkIMMulticontextClass *class); static void gtk_im_multicontext_init (GtkIMMulticontext *im_multicontext); @@ -341,6 +341,7 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context, { const GtkIMContextInfo **contexts; gint n_contexts, i; + GSList *group = NULL; _gtk_im_module_list (&contexts, &n_contexts); @@ -348,8 +349,17 @@ gtk_im_multicontext_append_menuitems (GtkIMMulticontext *context, { GtkWidget *menuitem; - menuitem = gtk_menu_item_new_with_label (contexts[i]->context_name); - + menuitem = gtk_radio_menu_item_new_with_label (group, + contexts[i]->context_name); + + if ((global_context_id == NULL && group == NULL) || + (global_context_id && + strcmp (contexts[i]->context_id, global_context_id) == 0)) + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), + TRUE); + + group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menuitem)); + gtk_object_set_data (GTK_OBJECT (menuitem), "gtk-context-id", (char *)contexts[i]->context_id); gtk_signal_connect (GTK_OBJECT (menuitem), "activate", diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 4d26c93c68..3de7d6d7d3 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -518,7 +518,6 @@ gtk_menu_popup (GtkMenu *menu, if ((current_event->type != GDK_BUTTON_PRESS) && (current_event->type != GDK_ENTER_NOTIFY)) menu_shell->ignore_enter = TRUE; - gdk_event_free (current_event); } if (menu->torn_off) @@ -542,6 +541,24 @@ gtk_menu_popup (GtkMenu *menu, gtk_widget_show (GTK_WIDGET (menu)); gtk_widget_show (menu->toplevel); + if (current_event) + { + /* 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); + } + + gtk_menu_scroll_to (menu, menu->scroll_offset); + /* Find the last viewable ancestor, and make an X grab on it */ parent = GTK_WIDGET (menu); @@ -576,15 +593,13 @@ gtk_menu_popup (GtkMenu *menu, 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); } } - gtk_menu_scroll_to (menu, menu->scroll_offset); - gtk_grab_add (GTK_WIDGET (menu)); } diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c index 601ab8a942..58e0d0c79e 100644 --- a/gtk/gtktextbtree.c +++ b/gtk/gtktextbtree.c @@ -959,8 +959,6 @@ _gtk_text_btree_insert (GtkTextIter *iter, /* 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 diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 292e63ddc9..1074ca0f9a 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -477,7 +477,7 @@ gtk_text_buffer_emit_insert (GtkTextBuffer *buffer, 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) { diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 3ee94dd8d7..b60250b739 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -99,6 +99,8 @@ struct _GtkTextPendingScroll enum { + SET_SCROLL_ADJUSTMENTS, + POPULATE_POPUP, MOVE_CURSOR, SET_ANCHOR, INSERT_AT_CURSOR, @@ -107,7 +109,6 @@ enum COPY_CLIPBOARD, PASTE_CLIPBOARD, TOGGLE_OVERWRITE, - SET_SCROLL_ADJUSTMENTS, LAST_SIGNAL }; @@ -207,6 +208,7 @@ static void gtk_text_view_drag_data_received (GtkWidget *widget, static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view, 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, @@ -270,7 +272,7 @@ static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view, 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 @@ gtk_text_view_class_init (GtkTextViewClass *klass) 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; @@ -592,6 +596,14 @@ gtk_text_view_class_init (GtkTextViewClass *klass) GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); widget_class->set_scroll_adjustments_signal = signals[SET_SCROLL_ADJUSTMENTS]; + signals[POPULATE_POPUP] = + gtk_signal_new ("populate_popup", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GtkTextViewClass, populate_popup), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_MENU); + /* * Key bindings */ @@ -3044,7 +3056,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } 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 || @@ -4866,7 +4878,8 @@ static void append_action_signal (GtkTextView *text_view, GtkWidget *menu, const gchar *label, - const gchar *signal) + const gchar *signal, + gboolean sensitive) { GtkWidget *menuitem = gtk_menu_item_new_with_label (label); @@ -4874,6 +4887,8 @@ append_action_signal (GtkTextView *text_view, gtk_signal_connect (GTK_OBJECT (menuitem), "activate", GTK_SIGNAL_FUNC (activate_cb), text_view); + gtk_widget_set_sensitive (menuitem, sensitive); + gtk_widget_show (menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); } @@ -4886,37 +4901,127 @@ popup_menu_detach (GtkWidget *attach_widget, } 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) { - if (!text_view->popup_menu) + 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 { - GtkWidget *menuitem; - - text_view->popup_menu = gtk_menu_new (); - - gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu), - GTK_WIDGET (text_view), - popup_menu_detach); - - append_action_signal (text_view, text_view->popup_menu, _("Cut"), "cut_clipboard"); - append_action_signal (text_view, text_view->popup_menu, _("Copy"), "copy_clipboard"); - append_action_signal (text_view, text_view->popup_menu, _("Paste"), "paste_clipboard"); - - menuitem = gtk_separator_menu_item_new (); - gtk_widget_show (menuitem); - gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem); - - gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context), - GTK_MENU_SHELL (text_view->popup_menu)); + /* 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); } - gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL, - NULL, NULL, - event->button, event->time); + /* 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) +{ + GtkWidget *menuitem; + GtkWidget *submenu; + gboolean have_selection; + + if (text_view->popup_menu) + gtk_widget_destroy (text_view->popup_menu); + + text_view->popup_menu = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu), + GTK_WIDGET (text_view), + popup_menu_detach); + + have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), + NULL, NULL); + + append_action_signal (text_view, text_view->popup_menu, _("Cut"), "cut_clipboard", + have_selection); + append_action_signal (text_view, text_view->popup_menu, _("Copy"), "copy_clipboard", + have_selection); + append_action_signal (text_view, text_view->popup_menu, _("Paste"), "paste_clipboard", + TRUE); + + menuitem = gtk_separator_menu_item_new (); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem); + + menuitem = gtk_menu_item_new_with_label (_("Input Methods")); + gtk_widget_show (menuitem); + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); + gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem); + + gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context), + GTK_MENU_SHELL (submenu)); + + gtk_signal_emit (GTK_OBJECT (text_view), + signals[POPULATE_POPUP], + text_view->popup_menu); + + 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 */ diff --git a/gtk/gtktextview.h b/gtk/gtktextview.h index b60ccd9719..9224657ca3 100644 --- a/gtk/gtktextview.h +++ b/gtk/gtktextview.h @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -148,6 +149,13 @@ struct _GtkTextViewClass { GtkContainerClass parent_class; + void (* set_scroll_adjustments) (GtkTextView *text_view, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + void (* populate_popup) (GtkTextView *text_view, + GtkMenu *menu); + /* These are all RUN_ACTION signals for keybindings */ /* move insertion point */ @@ -171,9 +179,6 @@ struct _GtkTextViewClass void (* paste_clipboard) (GtkTextView *text_view); /* overwrite */ void (* toggle_overwrite) (GtkTextView *text_view); - void (* set_scroll_adjustments) (GtkTextView *text_view, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); }; GtkType gtk_text_view_get_type (void) G_GNUC_CONST; diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 831e089dc2..7274923db4 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -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) @@ -101,6 +102,7 @@ enum { NO_EXPOSE_EVENT, VISIBILITY_NOTIFY_EVENT, WINDOW_STATE_EVENT, + POPUP_MENU, LAST_SIGNAL }; @@ -281,6 +283,7 @@ gtk_widget_class_init (GtkWidgetClass *klass) { 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); @@ -811,6 +814,21 @@ gtk_widget_class_init (GtkWidgetClass *klass) 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 diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 3343045d24..9e37df24f9 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -377,8 +377,11 @@ struct _GtkWidgetClass 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; diff --git a/gtk/testtext.c b/gtk/testtext.c index ee732ff902..fe1a009fdb 100644 --- a/gtk/testtext.c +++ b/gtk/testtext.c @@ -390,7 +390,7 @@ setup_tag (GtkTextTag *tag) { g_signal_connect_data (G_OBJECT (tag), "event", - tag_event_handler, + G_CALLBACK (tag_event_handler), NULL, NULL, FALSE, FALSE); } @@ -1419,7 +1419,7 @@ create_buffer (void) } buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, - "invisible", TRUE); + "invisible", TRUE, NULL); buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, diff --git a/tests/testtext.c b/tests/testtext.c index ee732ff902..fe1a009fdb 100644 --- a/tests/testtext.c +++ b/tests/testtext.c @@ -390,7 +390,7 @@ setup_tag (GtkTextTag *tag) { g_signal_connect_data (G_OBJECT (tag), "event", - tag_event_handler, + G_CALLBACK (tag_event_handler), NULL, NULL, FALSE, FALSE); } @@ -1419,7 +1419,7 @@ create_buffer (void) } buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, - "invisible", TRUE); + "invisible", TRUE, NULL); buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,