Add new infrastructure for notifications of failed keyboard navigation and

2006-11-16  Michael Natterer  <mitch@imendio.com>

	Add new infrastructure for notifications of failed keyboard
	navigation and navigation with restricted set of keys.

	The patch handles configurable beeping, navigating the GUI with
	cursor keys only (as in phone environments), and configurable
	wrap-around. Fixes bugs #322640, #70986, #318827, #334726, #334742
	and #309291.

	* gtk/gtksettings.c: added properties gtk-keynav-cursor-only,
	gtk-keynav-wrap-around and gtk-error-bell.

	* gtk/gtkwidget.[ch]: added new signal "keynav-failed" and public
	API to emit it. Added New function gtk_widget_error_bell() which
	looks at the gtk-error-bell setting and calls gdk_window_beep()
	accordingly.

	* gtk/gtk.symbols: add the new widget symbols.

	* gtk/gtkcellrendereraccel.c
	* gtk/gtkimcontextsimple.c
	* gtk/gtkmenu.c
	* gtk/gtknotebook.c: use gtk_widget_error_bell() or look at the
	gtk-error-bell setting instead of calling gdk_display_beep()
	unconditionally.

	* gtk/gtkcombobox.c
	* gtk/gtkentry.c
	* gtk/gtkiconview.c
	* gtk/gtklabel.c
	* gtk/gtkmenushell.c
	* gtk/gtkspinbutton.c
	* gtk/gtktextview.c
	* gtk/gtktreeview.c: call gtk_widget_error_bell() on failed keynav.

	* gtk/gtkentry.c
	* gtk/gtklabel.c
	* gtk/gtkrange.c
	* gtk/gtktextview.c: consult gtk_widget_keynav_failed() on failed
	cursor navigation and leave the widget if it returns FALSE.

	* gtk/gtkmenushell.c
	* gtk/gtknotebook.c: only wrap around if gtk-keynav-wrap-around
	is TRUE.

	* gtk/gtkradiobutton.c: ask gtk_widget_keynav_failed() to decide
	whether to to wrap-around, and don't select active items on cursor
	navigation if gtk-keynav-cursor-only is TRUE. Should look at
	gtk-keynav-wrap-around too, will look into that.
This commit is contained in:
Michael Natterer 2006-11-16 12:56:30 +00:00 committed by Michael Natterer
parent af6b361d6b
commit 7f374a74ba
19 changed files with 743 additions and 132 deletions

View File

@ -1,3 +1,54 @@
2006-11-16 Michael Natterer <mitch@imendio.com>
Add new infrastructure for notifications of failed keyboard
navigation and navigation with restricted set of keys.
The patch handles configurable beeping, navigating the GUI with
cursor keys only (as in phone environments), and configurable
wrap-around. Fixes bugs #322640, #70986, #318827, #334726, #334742
and #309291.
* gtk/gtksettings.c: added properties gtk-keynav-cursor-only,
gtk-keynav-wrap-around and gtk-error-bell.
* gtk/gtkwidget.[ch]: added new signal "keynav-failed" and public
API to emit it. Added New function gtk_widget_error_bell() which
looks at the gtk-error-bell setting and calls gdk_window_beep()
accordingly.
* gtk/gtk.symbols: add the new widget symbols.
* gtk/gtkcellrendereraccel.c
* gtk/gtkimcontextsimple.c
* gtk/gtkmenu.c
* gtk/gtknotebook.c: use gtk_widget_error_bell() or look at the
gtk-error-bell setting instead of calling gdk_display_beep()
unconditionally.
* gtk/gtkcombobox.c
* gtk/gtkentry.c
* gtk/gtkiconview.c
* gtk/gtklabel.c
* gtk/gtkmenushell.c
* gtk/gtkspinbutton.c
* gtk/gtktextview.c
* gtk/gtktreeview.c: call gtk_widget_error_bell() on failed keynav.
* gtk/gtkentry.c
* gtk/gtklabel.c
* gtk/gtkrange.c
* gtk/gtktextview.c: consult gtk_widget_keynav_failed() on failed
cursor navigation and leave the widget if it returns FALSE.
* gtk/gtkmenushell.c
* gtk/gtknotebook.c: only wrap around if gtk-keynav-wrap-around
is TRUE.
* gtk/gtkradiobutton.c: ask gtk_widget_keynav_failed() to decide
whether to to wrap-around, and don't select active items on cursor
navigation if gtk-keynav-cursor-only is TRUE. Should look at
gtk-keynav-wrap-around too, will look into that.
2006-11-16 Emmanuele Bassi <ebassi@gnome.org>
* gtk/gtkrecentmanager.c:

View File

@ -4470,6 +4470,7 @@ gtk_widget_create_pango_layout
gtk_widget_destroy
gtk_widget_destroyed
gtk_widget_ensure_style
gtk_widget_error_bell
gtk_widget_event
gtk_widget_freeze_child_notify
gtk_widget_get_accessible
@ -4511,6 +4512,7 @@ gtk_widget_hide_on_delete
gtk_widget_intersect
gtk_widget_is_ancestor
gtk_widget_is_focus
gtk_widget_keynav_failed
gtk_widget_list_accel_closures
gtk_widget_list_mnemonic_labels
gtk_widget_map

View File

@ -456,7 +456,7 @@ grab_key_callback (GtkWidget *widget,
{
if (!gtk_accelerator_valid (accel_key, accel_mods))
{
gdk_display_beep (display);
gtk_widget_error_bell (widget);
return TRUE;
}

View File

@ -4978,7 +4978,10 @@ gtk_combo_box_real_move_active (GtkComboBox *combo_box,
gboolean found;
if (!combo_box->priv->model)
return;
{
gtk_widget_error_bell (GTK_WIDGET (combo_box));
return;
}
active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
@ -5024,28 +5027,28 @@ gtk_combo_box_real_move_active (GtkComboBox *combo_box,
return;
}
if (found && active_iter)
{
GtkTreePath *old_path;
GtkTreePath *new_path;
old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
if (gtk_tree_path_compare (old_path, new_path) == 0)
found = FALSE;
gtk_tree_path_free (old_path);
gtk_tree_path_free (new_path);
}
if (found)
{
if (active_iter)
{
GtkTreePath *old_path;
GtkTreePath *new_path;
old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
if (gtk_tree_path_compare (old_path, new_path) == 0)
found = FALSE;
gtk_tree_path_free (old_path);
gtk_tree_path_free (new_path);
}
if (found)
{
gtk_combo_box_set_active_iter (combo_box, &new_iter);
return;
}
gtk_combo_box_set_active_iter (combo_box, &new_iter);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (combo_box));
}
}

View File

@ -1709,12 +1709,18 @@ gtk_entry_button_press (GtkWidget *widget,
return TRUE;
}
else if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
else if (event->button == 2 && event->type == GDK_BUTTON_PRESS)
{
priv->insert_pos = tmp_pos;
gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
return TRUE;
if (entry->editable)
{
priv->insert_pos = tmp_pos;
gtk_entry_paste (entry, GDK_SELECTION_PRIMARY);
return TRUE;
}
else
{
gtk_widget_error_bell (widget);
}
}
else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
{
@ -1976,6 +1982,9 @@ gtk_entry_key_press (GtkWidget *widget,
*/
return TRUE;
if (!entry->editable && event->length)
gtk_widget_error_bell (widget);
return FALSE;
}
@ -2329,7 +2338,7 @@ gtk_entry_real_insert_text (GtkEditable *editable,
n_chars = g_utf8_strlen (new_text, new_text_length);
if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
{
gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (entry)));
gtk_widget_error_bell (GTK_WIDGET (entry));
n_chars = entry->text_max_length - entry->text_length;
new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
}
@ -2535,7 +2544,6 @@ gtk_entry_move_cursor (GtkEntry *entry,
new_pos = current_x < bound_x ? entry->current_pos : entry->selection_bound;
else
new_pos = current_x > bound_x ? entry->current_pos : entry->selection_bound;
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
@ -2566,6 +2574,27 @@ gtk_entry_move_cursor (GtkEntry *entry,
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos = gtk_entry_move_visually (entry, new_pos, count);
if (entry->current_pos == new_pos)
{
if (!extend_selection)
{
if (!gtk_widget_keynav_failed (GTK_WIDGET (entry),
count > 0 ?
GTK_DIR_RIGHT : GTK_DIR_LEFT))
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
if (toplevel)
gtk_widget_child_focus (toplevel,
count > 0 ?
GTK_DIR_RIGHT : GTK_DIR_LEFT);
}
}
else
{
gtk_widget_error_bell (GTK_WIDGET (entry));
}
}
break;
case GTK_MOVEMENT_WORDS:
while (count > 0)
@ -2578,11 +2607,15 @@ gtk_entry_move_cursor (GtkEntry *entry,
new_pos = gtk_entry_move_backward_word (entry, new_pos, FALSE);
count++;
}
if (entry->current_pos == new_pos)
gtk_widget_error_bell (GTK_WIDGET (entry));
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
new_pos = count < 0 ? 0 : entry->text_length;
if (entry->current_pos == new_pos)
gtk_widget_error_bell (GTK_WIDGET (entry));
break;
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:
@ -2624,11 +2657,15 @@ gtk_entry_delete_from_cursor (GtkEntry *entry,
GtkEditable *editable = GTK_EDITABLE (entry);
gint start_pos = entry->current_pos;
gint end_pos = entry->current_pos;
gint old_n_bytes = entry->n_bytes;
_gtk_entry_reset_im_context (entry);
if (!entry->editable)
return;
{
gtk_widget_error_bell (GTK_WIDGET (entry));
return;
}
if (entry->selection_bound != entry->current_pos)
{
@ -2685,7 +2722,10 @@ gtk_entry_delete_from_cursor (GtkEntry *entry,
gtk_entry_delete_whitespace (entry);
break;
}
if (entry->n_bytes == old_n_bytes)
gtk_widget_error_bell (GTK_WIDGET (entry));
gtk_entry_pend_cursor_blink (entry);
}
@ -2698,7 +2738,10 @@ gtk_entry_backspace (GtkEntry *entry)
_gtk_entry_reset_im_context (entry);
if (!entry->editable || !entry->text)
return;
{
gtk_widget_error_bell (GTK_WIDGET (entry));
return;
}
if (entry->selection_bound != entry->current_pos)
{
@ -2751,6 +2794,10 @@ gtk_entry_backspace (GtkEntry *entry)
g_free (log_attrs);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (entry));
}
gtk_entry_pend_cursor_blink (entry);
}
@ -2784,6 +2831,10 @@ gtk_entry_cut_clipboard (GtkEntry *entry)
if (gtk_editable_get_selection_bounds (editable, &start, &end))
gtk_editable_delete_text (editable, start, end);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (entry));
}
}
static void
@ -2791,6 +2842,8 @@ gtk_entry_paste_clipboard (GtkEntry *entry)
{
if (entry->editable)
gtk_entry_paste (entry, GDK_NONE);
else
gtk_widget_error_bell (GTK_WIDGET (entry));
}
static void

View File

@ -3796,7 +3796,10 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view,
}
if (!item)
return;
{
gtk_widget_error_bell (GTK_WIDGET (icon_view));
return;
}
if (icon_view->priv->ctrl_pressed ||
!icon_view->priv->shift_pressed ||
@ -3847,6 +3850,9 @@ gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view,
icon_view->priv->cursor_item,
count);
if (item == icon_view->priv->cursor_item)
gtk_widget_error_bell (GTK_WIDGET (icon_view));
if (!item)
return;
@ -3915,7 +3921,10 @@ gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view,
}
if (!item)
return;
{
gtk_widget_error_bell (GTK_WIDGET (icon_view));
return;
}
if (icon_view->priv->ctrl_pressed ||
!icon_view->priv->shift_pressed ||
@ -3958,6 +3967,9 @@ gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view,
item = list ? list->data : NULL;
if (item == icon_view->priv->cursor_item)
gtk_widget_error_bell (GTK_WIDGET (icon_view));
if (!item)
return;

View File

@ -23,6 +23,8 @@
#include <gdk/gdkkeysyms.h>
#include "gtkaccelgroup.h"
#include "gtkimcontextsimple.h"
#include "gtksettings.h"
#include "gtkwidget.h"
#include "gtkintl.h"
#include "gtkalias.h"
@ -1175,6 +1177,31 @@ check_hex (GtkIMContextSimple *context_simple,
return TRUE;
}
static void
beep_window (GdkWindow *window)
{
GtkWidget *widget;
gdk_window_get_user_data (window, &widget);
if (GTK_IS_WIDGET (widget))
{
gtk_widget_error_bell (widget);
}
else
{
GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
gboolean beep;
g_object_get (gtk_settings_get_for_screen (screen),
"gtk-error-bell", &beep,
NULL);
if (beep)
gdk_window_beep (window);
}
}
static gboolean
no_sequence_matches (GtkIMContextSimple *context_simple,
gint n_compose,
@ -1212,7 +1239,7 @@ no_sequence_matches (GtkIMContextSimple *context_simple,
context_simple->compose_buffer[0] = 0;
if (n_compose > 1) /* Invalid sequence */
{
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
return TRUE;
}
@ -1317,7 +1344,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
else
{
/* invalid hex sequence */
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
@ -1403,7 +1430,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
{
/* invalid hex sequence */
if (n_compose > 0)
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
@ -1438,7 +1465,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
else if (!is_hex_end)
{
/* non-hex character in hex sequence */
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
return TRUE;
}
@ -1465,7 +1492,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
else
{
/* invalid hex sequence */
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
context_simple->tentative_match = 0;
context_simple->in_hex_sequence = FALSE;
@ -1473,7 +1500,7 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
}
}
else if (!check_hex (context_simple, n_compose))
gdk_display_beep (gdk_drawable_get_display (event->window));
beep_window (event->window);
g_signal_emit_by_name (context_simple, "preedit_changed");

View File

@ -891,8 +891,8 @@ gtk_label_mnemonic_activate (GtkWidget *widget,
/* barf if there was nothing to activate */
g_warning ("Couldn't find a target for a mnemonic activation.");
gdk_display_beep (gtk_widget_get_display (widget));
gtk_widget_error_bell (widget);
return FALSE;
}
@ -3871,12 +3871,13 @@ gtk_label_move_cursor (GtkLabel *label,
gint count,
gboolean extend_selection)
{
gint old_pos;
gint new_pos;
if (label->select_info == NULL)
return;
new_pos = label->select_info->selection_end;
old_pos = new_pos = label->select_info->selection_end;
if (label->select_info->selection_end != label->select_info->selection_anchor &&
!extend_selection)
@ -3901,7 +3902,6 @@ gtk_label_move_cursor (GtkLabel *label,
new_pos = end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
else
new_pos = !end_is_left ? label->select_info->selection_end : label->select_info->selection_anchor;
break;
}
case GTK_MOVEMENT_LOGICAL_POSITIONS:
@ -3933,6 +3933,27 @@ gtk_label_move_cursor (GtkLabel *label,
break;
case GTK_MOVEMENT_VISUAL_POSITIONS:
new_pos = gtk_label_move_visually (label, new_pos, count);
if (new_pos == old_pos)
{
if (!extend_selection)
{
if (!gtk_widget_keynav_failed (GTK_WIDGET (label),
count > 0 ?
GTK_DIR_RIGHT : GTK_DIR_LEFT))
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label));
if (toplevel)
gtk_widget_child_focus (toplevel,
count > 0 ?
GTK_DIR_RIGHT : GTK_DIR_LEFT);
}
}
else
{
gtk_widget_error_bell (GTK_WIDGET (label));
}
}
break;
case GTK_MOVEMENT_WORDS:
while (count > 0)
@ -3945,12 +3966,16 @@ gtk_label_move_cursor (GtkLabel *label,
new_pos = gtk_label_move_backward_word (label, new_pos);
count++;
}
if (new_pos == old_pos)
gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
case GTK_MOVEMENT_PARAGRAPH_ENDS:
case GTK_MOVEMENT_BUFFER_ENDS:
/* FIXME: Can do better here */
new_pos = count < 0 ? 0 : strlen (label->text);
if (new_pos == old_pos)
gtk_widget_error_bell (GTK_WIDGET (label));
break;
case GTK_MOVEMENT_DISPLAY_LINES:
case GTK_MOVEMENT_PARAGRAPHS:

View File

@ -2756,7 +2756,7 @@ gtk_menu_key_press (GtkWidget *widget,
* (basically, those items are accelerator-locked).
*/
/* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
gdk_display_beep (display);
gtk_widget_error_bell (widget);
}
else
{
@ -2785,7 +2785,7 @@ gtk_menu_key_press (GtkWidget *widget,
* locked already
*/
/* g_print("failed to change\n"); */
gdk_display_beep (display);
gtk_widget_error_bell (widget);
}
}
}

View File

@ -1044,17 +1044,27 @@ gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
GList *node = g_list_find (menu_shell->children,
menu_shell->active_menu_item);
GList *start_node = node;
gboolean wrap_around;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
"gtk-keynav-wrap-around", &wrap_around,
NULL);
if (distance > 0)
{
node = node->next;
while (node != start_node &&
(!node || !_gtk_menu_item_is_selectable (node->data)))
{
if (!node)
node = menu_shell->children;
else
if (node)
node = node->next;
else if (wrap_around)
node = menu_shell->children;
else
{
gtk_widget_error_bell (GTK_WIDGET (menu_shell));
break;
}
}
}
else
@ -1063,10 +1073,15 @@ gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
while (node != start_node &&
(!node || !_gtk_menu_item_is_selectable (node->data)))
{
if (!node)
node = g_list_last (menu_shell->children);
else
if (node)
node = node->prev;
else if (wrap_around)
node = g_list_last (menu_shell->children);
else
{
gtk_widget_error_bell (GTK_WIDGET (menu_shell));
break;
}
}
}

View File

@ -1066,14 +1066,33 @@ gtk_notebook_change_current_page (GtkNotebook *notebook,
while (offset != 0)
{
current = gtk_notebook_search_page (notebook, current, offset < 0 ? STEP_PREV : STEP_NEXT, TRUE);
current = gtk_notebook_search_page (notebook, current,
offset < 0 ? STEP_PREV : STEP_NEXT,
TRUE);
if (!current)
{
gboolean wrap_around;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
"gtk-keynav-wrap-around", &wrap_around,
NULL);
if (wrap_around)
current = gtk_notebook_search_page (notebook, NULL,
offset < 0 ? STEP_PREV : STEP_NEXT,
TRUE);
else
break;
}
offset += offset < 0 ? 1 : -1;
}
if (current)
gtk_notebook_switch_page (notebook, current->data, -1);
else
gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
gtk_widget_error_bell (GTK_WIDGET (notebook));
}
static GtkDirectionType
@ -3589,11 +3608,24 @@ focus_tabs_move (GtkNotebook *notebook,
new_page = gtk_notebook_search_page (notebook, notebook->focus_tab,
search_direction, TRUE);
if (!new_page)
{
gboolean wrap_around;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
"gtk-keynav-wrap-around", &wrap_around,
NULL);
if (wrap_around)
new_page = gtk_notebook_search_page (notebook, NULL,
search_direction, TRUE);
}
if (new_page)
gtk_notebook_switch_focus_tab (notebook, new_page);
else
gdk_display_beep (gtk_widget_get_display (GTK_WIDGET (notebook)));
gtk_widget_error_bell (GTK_WIDGET (notebook));
return TRUE;
}

View File

@ -491,6 +491,12 @@ gtk_radio_button_focus (GtkWidget *widget,
if (!new_focus)
{
if (!gtk_widget_keynav_failed (widget, direction))
{
g_slist_free (focus_list);
return FALSE;
}
tmp_list = focus_list;
while (tmp_list)
@ -511,8 +517,17 @@ gtk_radio_button_focus (GtkWidget *widget,
if (new_focus)
{
GtkSettings *settings = gtk_widget_get_settings (widget);
gboolean cursor_only;
g_object_get (settings,
"gtk-keynav-cursor-only", &cursor_only,
NULL);
gtk_widget_grab_focus (new_focus);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
if (!cursor_only)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
}
return TRUE;

View File

@ -2457,6 +2457,42 @@ static void
gtk_range_move_slider (GtkRange *range,
GtkScrollType scroll)
{
gboolean cursor_only;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
"gtk-keynav-cursor-only", &cursor_only,
NULL);
if (cursor_only)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (scroll == GTK_SCROLL_STEP_UP ||
scroll == GTK_SCROLL_STEP_DOWN)
{
if (toplevel)
gtk_widget_child_focus (toplevel,
scroll == GTK_SCROLL_STEP_UP ?
GTK_DIR_UP : GTK_DIR_DOWN);
return;
}
}
else
{
if (scroll == GTK_SCROLL_STEP_LEFT ||
scroll == GTK_SCROLL_STEP_RIGHT)
{
if (toplevel)
gtk_widget_child_focus (toplevel,
scroll == GTK_SCROLL_STEP_LEFT ?
GTK_DIR_LEFT : GTK_DIR_RIGHT);
return;
}
}
}
gtk_range_scroll (range, scroll);
/* Policy DELAYED makes sense with key events,

View File

@ -93,6 +93,9 @@ enum {
PROP_COLOR_SCHEME,
PROP_ENABLE_ANIMATIONS,
PROP_TOUCHSCREEN_MODE,
PROP_KEYNAV_CURSOR_ONLY,
PROP_KEYNAV_WRAP_AROUND,
PROP_ERROR_BELL,
PROP_COLOR_HASH
};
@ -507,7 +510,7 @@ gtk_settings_class_init (GtkSettingsClass *class)
/**
* GtkSettings:gtk-touchscreen-mode:
*
* When TRUE, there are no motion notify events delivered on this screen,
* When %TRUE, there are no motion notify events delivered on this screen,
* and widgets can't use the pointer hovering them for any essential
* functionality.
*
@ -523,6 +526,64 @@ gtk_settings_class_init (GtkSettingsClass *class)
g_assert (result == PROP_TOUCHSCREEN_MODE);
/**
* GtkSettings:gtk-keynav-cursor-only:
*
* When %TRUE, keyboard navigation should be able to reach all widgets
* by using the cursor keys only. Tab, Shift etc. keys can't be expected
* to be present on the used input device.
*
* Since: 2.12
*/
result = settings_install_property_parser (class,
g_param_spec_boolean ("gtk-keynav-cursor-only",
P_("Keynav Cursor Only"),
P_("When TRUE, there are only cursor keys available to navigate widgets"),
FALSE,
GTK_PARAM_READWRITE),
NULL);
g_assert (result == PROP_KEYNAV_CURSOR_ONLY);
/**
* GtkSettings:gtk-keynav-wrap-around:
*
* When %TRUE, some widgets will wrap around when doing keyboard
* navigation, such as menus, menubars and notebooks.
*
* Since: 2.12
*/
result = settings_install_property_parser (class,
g_param_spec_boolean ("gtk-keynav-wrap-around",
P_("Keynav Wrap Around"),
P_("Whether to wrap around when keyboard-navigating widgets"),
TRUE,
GTK_PARAM_READWRITE),
NULL);
g_assert (result == PROP_KEYNAV_WRAP_AROUND);
/**
* GtkSettings:gtk-error-bell:
*
* When %TRUE, keyboard navigation and other input-related errors
* will cause a beep. Since the error bell is implemented using
* gdk_window_beep(), the windowing system may offer ways to
* configure the error bell in many ways, such as flashing the
* window or similar visual effects.
*
* Since: 2.12
*/
result = settings_install_property_parser (class,
g_param_spec_boolean ("gtk-error-bell",
P_("Error Bell"),
P_("When TRUE, keyboard navigation and other errors will cause a beep"),
TRUE,
GTK_PARAM_READWRITE),
NULL);
g_assert (result == PROP_ERROR_BELL);
/**
* GtkSettings:color-hash:
*

View File

@ -1243,6 +1243,8 @@ static void
gtk_spin_button_real_change_value (GtkSpinButton *spin,
GtkScrollType scroll)
{
gdouble old_value = spin->adjustment->value;
/* We don't test whether the entry is editable, since
* this key binding conceptually corresponds to changing
* the value with the buttons using the mouse, which
@ -1320,6 +1322,9 @@ gtk_spin_button_real_change_value (GtkSpinButton *spin,
}
gtk_spin_button_update (spin);
if (spin->adjustment->value == old_value)
gtk_widget_error_bell (GTK_WIDGET (spin));
}
static gint

View File

@ -260,10 +260,10 @@ static void gtk_text_view_move_viewport (GtkTextView *text_view,
GtkScrollStep step,
gint count);
static void gtk_text_view_set_anchor (GtkTextView *text_view);
static void gtk_text_view_scroll_pages (GtkTextView *text_view,
static gboolean gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection);
static void gtk_text_view_scroll_hpages (GtkTextView *text_view,
static gboolean gtk_text_view_scroll_hpages(GtkTextView *text_view,
gint count,
gboolean extend_selection);
static void gtk_text_view_insert_at_cursor (GtkTextView *text_view,
@ -3922,6 +3922,9 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
gtk_text_view_reset_blink_time (text_view);
gtk_text_view_pend_cursor_blink (text_view);
if (!retval && event->length)
gtk_widget_error_bell (widget);
return retval;
}
@ -4688,8 +4691,8 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view,
{
GtkTextIter insert;
GtkTextIter newplace;
gint cursor_x_pos = 0;
GtkDirectionType leave_direction = -1;
if (!text_view->cursor_visible)
{
@ -4733,14 +4736,18 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view,
if (step == GTK_MOVEMENT_PAGES)
{
gtk_text_view_scroll_pages (text_view, count, extend_selection);
if (!gtk_text_view_scroll_pages (text_view, count, extend_selection))
gtk_widget_error_bell (GTK_WIDGET (text_view));
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
}
else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
{
gtk_text_view_scroll_hpages (text_view, count, extend_selection);
if (!gtk_text_view_scroll_hpages (text_view, count, extend_selection))
gtk_widget_error_bell (GTK_WIDGET (text_view));
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
return;
@ -4777,19 +4784,23 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view,
case GTK_MOVEMENT_DISPLAY_LINES:
if (count < 0)
{
if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
else
gtk_text_iter_set_line_offset (&newplace, 0);
}
{
leave_direction = GTK_DIR_UP;
if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
else
gtk_text_iter_set_line_offset (&newplace, 0);
}
if (count > 0)
{
if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
else
gtk_text_iter_forward_to_line_end (&newplace);
}
{
leave_direction = GTK_DIR_DOWN;
if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
else
gtk_text_iter_forward_to_line_end (&newplace);
}
break;
case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
@ -4860,6 +4871,18 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view,
if (step == GTK_MOVEMENT_DISPLAY_LINES)
gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
}
else if (leave_direction != -1)
{
if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view),
leave_direction))
{
gtk_text_view_move_focus (text_view, leave_direction);
}
}
else
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
gtk_text_view_check_cursor_blink (text_view);
gtk_text_view_pend_cursor_blink (text_view);
@ -4943,7 +4966,7 @@ gtk_text_view_set_anchor (GtkTextView *text_view)
gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
}
static void
static gboolean
gtk_text_view_scroll_pages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
@ -4952,11 +4975,12 @@ gtk_text_view_scroll_pages (GtkTextView *text_view,
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
GtkTextIter old_insert;
GtkTextIter new_insert;
GtkTextIter anchor;
gint y0, y1;
g_return_if_fail (text_view->vadjustment != NULL);
g_return_val_if_fail (text_view->vadjustment != NULL, FALSE);
adj = text_view->vadjustment;
@ -4970,9 +4994,13 @@ gtk_text_view_scroll_pages (GtkTextView *text_view,
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
/* Validate the region that will be brought into view by the cursor motion
/* Validate the region that will be brought into view by the cursor motion
*/
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
&old_insert,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
if (count < 0)
{
gtk_text_view_get_first_para_iter (text_view, &anchor);
@ -4989,6 +5017,8 @@ gtk_text_view_scroll_pages (GtkTextView *text_view,
gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
/* FIXME do we need to update the adjustment ranges here? */
new_insert = old_insert;
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at top, just be sure we are at offset 0 */
@ -5005,9 +5035,9 @@ gtk_text_view_scroll_pages (GtkTextView *text_view,
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
newval = adj->value;
oldval = adj->value;
newval = adj->value;
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
@ -5027,9 +5057,11 @@ gtk_text_view_scroll_pages (GtkTextView *text_view,
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
return !gtk_text_iter_equal (&old_insert, &new_insert);
}
static void
static gboolean
gtk_text_view_scroll_hpages (GtkTextView *text_view,
gint count,
gboolean extend_selection)
@ -5038,10 +5070,11 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view,
gdouble oldval;
GtkAdjustment *adj;
gint cursor_x_pos, cursor_y_pos;
GtkTextIter old_insert;
GtkTextIter new_insert;
gint y, height;
g_return_if_fail (text_view->hadjustment != NULL);
g_return_val_if_fail (text_view->hadjustment != NULL, FALSE);
adj = text_view->hadjustment;
@ -5055,16 +5088,18 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view,
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
/* Validate the line that we're moving within.
*/
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
&new_insert,
&old_insert,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height);
gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height);
/* FIXME do we need to update the adjustment ranges here? */
new_insert = old_insert;
if (count < 0 && adj->value <= (adj->lower + 1e-12))
{
/* already at far left, just be sure we are at offset 0 */
@ -5082,9 +5117,9 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view,
{
gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
newval = adj->value;
oldval = adj->value;
newval = adj->value;
newval += count * adj->page_increment;
set_adjustment_clamped (adj, newval);
@ -5110,6 +5145,8 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view,
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view),
"insert"));
return !gtk_text_iter_equal (&old_insert, &new_insert);
}
static gboolean
@ -5143,8 +5180,11 @@ static void
gtk_text_view_insert_at_cursor (GtkTextView *text_view,
const gchar *str)
{
gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
text_view->editable);
if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
text_view->editable))
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
static void
@ -5261,6 +5301,10 @@ gtk_text_view_delete_from_cursor (GtkTextView *text_view,
" ", 1,
text_view->editable);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
gtk_text_buffer_end_user_action (get_buffer (text_view));
gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
@ -5269,6 +5313,10 @@ gtk_text_view_delete_from_cursor (GtkTextView *text_view,
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_mark (get_buffer (text_view), "insert"));
}
else
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
static void
@ -5295,6 +5343,10 @@ gtk_text_view_backspace (GtkTextView *text_view)
gtk_text_view_scroll_mark_onscreen (text_view,
gtk_text_buffer_get_insert (get_buffer (text_view)));
}
else
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
static void
@ -6337,9 +6389,13 @@ insert_text_data (GtkTextView *text_view,
if (str)
{
gtk_text_buffer_insert_interactive (get_buffer (text_view),
drop_point, (gchar *) str, -1,
text_view->editable);
if (!gtk_text_buffer_insert_interactive (get_buffer (text_view),
drop_point, (gchar *) str, -1,
text_view->editable))
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
g_free (str);
}
}
@ -6804,8 +6860,11 @@ gtk_text_view_commit_text (GtkTextView *text_view,
if (!strcmp (str, "\n"))
{
gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
text_view->editable);
if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
text_view->editable))
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
else
{
@ -6820,8 +6879,12 @@ gtk_text_view_commit_text (GtkTextView *text_view,
if (!gtk_text_iter_ends_line (&insert))
gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
}
gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
text_view->editable);
if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
text_view->editable))
{
gtk_widget_error_bell (GTK_WIDGET (text_view));
}
}
gtk_text_buffer_end_user_action (get_buffer (text_view));

View File

@ -413,7 +413,7 @@ static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry
static gboolean gtk_tree_view_search_key_press_event (GtkWidget *entry,
GdkEventKey *event,
GtkTreeView *tree_view);
static void gtk_tree_view_search_move (GtkWidget *window,
static gboolean gtk_tree_view_search_move (GtkWidget *window,
GtkTreeView *tree_view,
gboolean up);
static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model,
@ -5115,11 +5115,16 @@ gtk_tree_view_key_press (GtkWidget *widget,
GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
if (!column->resizable)
return TRUE;
{
gtk_widget_error_bell (widget);
return TRUE;
}
if (event->keyval == (rtl ? GDK_Right : GDK_Left)
|| event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
{
gint old_width = column->resized_width;
column->resized_width = MAX (column->resized_width,
column->width);
column->resized_width -= 2;
@ -5138,11 +5143,17 @@ gtk_tree_view_key_press (GtkWidget *widget,
column->max_width);
column->use_resized_width = TRUE;
gtk_widget_queue_resize (widget);
if (column->resized_width != old_width)
gtk_widget_queue_resize (widget);
else
gtk_widget_error_bell (widget);
}
else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
{
gint old_width = column->resized_width;
column->resized_width = MAX (column->resized_width,
column->width);
column->resized_width += 2;
@ -5152,7 +5163,11 @@ gtk_tree_view_key_press (GtkWidget *widget,
column->max_width);
column->use_resized_width = TRUE;
gtk_widget_queue_resize (widget);
if (column->resized_width != old_width)
gtk_widget_queue_resize (widget);
else
gtk_widget_error_bell (widget);
}
return TRUE;
@ -5174,6 +5189,8 @@ gtk_tree_view_key_press (GtkWidget *widget,
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
else
gtk_widget_error_bell (widget);
}
else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
@ -5182,6 +5199,8 @@ gtk_tree_view_key_press (GtkWidget *widget,
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
else
gtk_widget_error_bell (widget);
}
else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
{
@ -5189,6 +5208,8 @@ gtk_tree_view_key_press (GtkWidget *widget,
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
else
gtk_widget_error_bell (widget);
}
else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
{
@ -5196,6 +5217,8 @@ gtk_tree_view_key_press (GtkWidget *widget,
col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
if (col != (GtkTreeViewColumn *)0x1)
gtk_tree_view_move_column_after (tree_view, column, col);
else
gtk_widget_error_bell (widget);
}
return TRUE;
@ -5206,8 +5229,7 @@ gtk_tree_view_key_press (GtkWidget *widget,
|| event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
{
if ((event->keyval == (rtl ? GDK_Right : GDK_Left)
|| event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
&& focus_column->prev)
|| event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)))
{
GList *tmp;
@ -5216,7 +5238,10 @@ gtk_tree_view_key_press (GtkWidget *widget,
break;
if (!tmp)
return FALSE;
{
gtk_widget_error_bell (widget);
return TRUE;
}
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp->data);
gtk_widget_grab_focus (tree_view->priv->focus_column->button);
@ -5227,8 +5252,7 @@ gtk_tree_view_key_press (GtkWidget *widget,
tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size));
}
else if ((event->keyval == (rtl ? GDK_Left : GDK_Right)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
&& focus_column->next)
|| event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)))
{
GList *tmp;
@ -5237,7 +5261,10 @@ gtk_tree_view_key_press (GtkWidget *widget,
break;
if (!tmp)
return FALSE;
{
gtk_widget_error_bell (widget);
return TRUE;
}
tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp->data);
@ -9558,6 +9585,7 @@ gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
else
{
gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
gtk_widget_grab_focus (GTK_WIDGET (tree_view));
@ -9569,6 +9597,7 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
{
GtkRBTree *cursor_tree = NULL;
GtkRBNode *cursor_node = NULL;
GtkTreePath *old_cursor_path = NULL;
GtkTreePath *cursor_path = NULL;
gint y;
gint window_y;
@ -9578,20 +9607,21 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
return;
if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
else
/* This is sorta weird. Focus in should give us a cursor */
return;
gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
_gtk_tree_view_find_node (tree_view, cursor_path,
_gtk_tree_view_find_node (tree_view, old_cursor_path,
&cursor_tree, &cursor_node);
gtk_tree_path_free (cursor_path);
if (cursor_tree == NULL)
/* FIXME: we lost the cursor. Should we try to get one? */
return;
{
/* FIXME: we lost the cursor. Should we try to get one? */
gtk_tree_path_free (old_cursor_path);
return;
}
g_return_if_fail (cursor_node != NULL);
y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
@ -9606,11 +9636,16 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
g_return_if_fail (cursor_path != NULL);
gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
gtk_tree_path_free (cursor_path);
y -= window_y;
gtk_tree_view_scroll_to_point (tree_view, -1, y);
_gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
gtk_widget_error_bell (GTK_WIDGET (tree_view));
gtk_tree_path_free (old_cursor_path);
gtk_tree_path_free (cursor_path);
}
static void
@ -9703,6 +9738,11 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
NULL);
g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
gtk_tree_view_clamp_column_visible (tree_view, tree_view->priv->focus_column);
}
@ -9713,23 +9753,25 @@ gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
GtkRBTree *cursor_tree;
GtkRBNode *cursor_node;
GtkTreePath *path;
GtkTreePath *old_path;
if (! GTK_WIDGET_HAS_FOCUS (tree_view))
return;
g_return_if_fail (tree_view->priv->tree != NULL);
gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
cursor_tree = tree_view->priv->tree;
cursor_node = cursor_tree->root;
if (count == -1)
{
cursor_tree = tree_view->priv->tree;
cursor_node = cursor_tree->root;
while (cursor_node && cursor_node->left != cursor_tree->nil)
cursor_node = cursor_node->left;
}
else
{
cursor_tree = tree_view->priv->tree;
cursor_node = cursor_tree->root;
do
{
while (cursor_node && cursor_node->right != cursor_tree->nil)
@ -9744,7 +9786,17 @@ gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
}
path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
if (gtk_tree_path_compare (old_path, path))
{
gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
}
else
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
gtk_tree_path_free (old_path);
gtk_tree_path_free (path);
}
@ -13888,28 +13940,36 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget,
/* select previous matching iter */
if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
{
gtk_tree_view_search_move (widget, tree_view, TRUE);
if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
gtk_widget_error_bell (widget);
retval = TRUE;
}
if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
&& (event->keyval == GDK_g || event->keyval == GDK_G))
{
gtk_tree_view_search_move (widget, tree_view, TRUE);
if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
gtk_widget_error_bell (widget);
retval = TRUE;
}
/* select next matching iter */
if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
{
gtk_tree_view_search_move (widget, tree_view, FALSE);
if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
gtk_widget_error_bell (widget);
retval = TRUE;
}
if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
&& (event->keyval == GDK_g || event->keyval == GDK_G))
{
gtk_tree_view_search_move (widget, tree_view, FALSE);
if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
gtk_widget_error_bell (widget);
retval = TRUE;
}
@ -13927,7 +13987,10 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget,
return retval;
}
static void
/* this function returns FALSE if there is a search string but
* nothing was found, and TRUE otherwise.
*/
static gboolean
gtk_tree_view_search_move (GtkWidget *window,
GtkTreeView *tree_view,
gboolean up)
@ -13942,15 +14005,17 @@ gtk_tree_view_search_move (GtkWidget *window,
text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
g_return_if_fail (text != NULL);
g_return_val_if_fail (text != NULL, FALSE);
len = strlen (text);
if (up && tree_view->priv->selected_iter == 1)
return;
return strlen (text) < 1;
len = strlen (text);
if (len < 1)
return;
return TRUE;
model = gtk_tree_view_get_model (tree_view);
selection = gtk_tree_view_get_selection (tree_view);
@ -13958,7 +14023,7 @@ gtk_tree_view_search_move (GtkWidget *window,
/* search */
gtk_tree_selection_unselect_all (selection);
if (!gtk_tree_model_get_iter_first (model, &iter))
return;
return TRUE;
ret = gtk_tree_view_search_iter (model, selection, &iter, text,
&count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
@ -13967,6 +14032,7 @@ gtk_tree_view_search_move (GtkWidget *window,
{
/* found */
tree_view->priv->selected_iter += up?(-1):(1);
return TRUE;
}
else
{
@ -13976,6 +14042,7 @@ gtk_tree_view_search_move (GtkWidget *window,
gtk_tree_view_search_iter (model, selection,
&iter, text,
&count, tree_view->priv->selected_iter);
return FALSE;
}
}

View File

@ -121,6 +121,7 @@ enum {
CAN_ACTIVATE_ACCEL,
GRAB_BROKEN,
COMPOSITED_CHANGED,
KEYNAV_FAILED,
LAST_SIGNAL
};
@ -204,6 +205,8 @@ static gboolean gtk_widget_real_focus_out_event (GtkWidget *widget,
GdkEventFocus *event);
static gboolean gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction);
static gboolean gtk_widget_real_keynav_failed (GtkWidget *widget,
GtkDirectionType direction);
static PangoContext* gtk_widget_peek_pango_context (GtkWidget *widget);
static void gtk_widget_update_pango_context (GtkWidget *widget);
static void gtk_widget_propagate_state (GtkWidget *widget,
@ -807,6 +810,29 @@ gtk_widget_class_init (GtkWidgetClass *klass)
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GtkWidget::keynav-failed:
* @widget: the object which received the signal.
* @direction: the direction of movement
*
* See gtk_widget_keynav_failed() for details.
*
* Returns: %TRUE if stopping keyboard navigation is fine, %FALSE
* if the emitting widget should try to handle the keyboard
* navigation attempt in its parent container(s).
*
* Since: 2.12
**/
widget_signals[KEYNAV_FAILED] =
_gtk_binding_signal_new (I_("keynav-failed"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_CALLBACK (gtk_widget_real_keynav_failed),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__ENUM,
G_TYPE_BOOLEAN, 1,
GTK_TYPE_DIRECTION_TYPE);
/**
* GtkWidget::delete-event:
* @widget: the object which received the signal.
@ -3642,7 +3668,7 @@ gtk_widget_real_mnemonic_activate (GtkWidget *widget,
{
g_warning ("widget `%s' isn't suitable for mnemonic activation",
G_OBJECT_TYPE_NAME (widget));
gdk_display_beep (gtk_widget_get_display (widget));
gtk_widget_error_bell (widget);
}
return TRUE;
}
@ -4326,6 +4352,35 @@ gtk_widget_real_focus (GtkWidget *widget,
return FALSE;
}
static gboolean
gtk_widget_real_keynav_failed (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean cursor_only;
switch (direction)
{
case GTK_DIR_TAB_FORWARD:
case GTK_DIR_TAB_BACKWARD:
return FALSE;
case GTK_DIR_UP:
case GTK_DIR_DOWN:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
g_object_get (gtk_widget_get_settings (widget),
"gtk-keynav-cursor-only", &cursor_only,
NULL);
if (cursor_only)
return FALSE;
break;
}
gtk_widget_error_bell (widget);
return TRUE;
}
/**
* gtk_widget_is_focus:
* @widget: a #GtkWidget
@ -5899,6 +5954,92 @@ gtk_widget_child_focus (GtkWidget *widget,
return return_val;
}
/**
* gtk_widget_keynav_failed:
* @widget: a #GtkWidget
* @direction: direction of focus movement
*
* This function should be called whenever keyboard navigation within
* a single widget hits a boundary. The function emits the
* "keynav-changed" signal on the widget and its return value should
* be interpreted in a way similar to the return value of
* gtk_widget_child_focus():
*
* When %TRUE is returned, stay in the widget, the failed keyboard
* navigation is Ok and/or there is nowhere we can/should move the
* focus to.
*
* When %FALSE is returned, the caller should continue with keyboard
* navigation outside the widget, e.g. by calling
* gtk_widget_child_focus() on the widget's toplevel.
*
* The default implementation for the "keynav-failed" signal is to
* return %TRUE for %GTK_DIR_TAB_FORWARD and
* %GTK_DIR_TAB_BACKWARD. For the other values of #GtkDirectionType,
* it looks at the "gtk-keynav-cursor-only" settings property and
* returns %FALSE if the setting is %TRUE. This way the entire GUI
* becomes cursor-navigatable on input devices such as mobile phones
* which only have cursor keys but no tab key.
*
* Whenever the default implementation returns %TRUE, it also calls
* gtk_widget_error_bell() to notify the user of the failed keyboard
* navigation.
*
* A use case for providing an own implementation of keynav-failed (by
* either connecting to it or by overriding it) would be a row of
* #GtkEntry widgets where the user should be able to navigate the
* entire row with the cursor keys, as e.g. known from GUIs that
* require entering license keys.
*
* Return value: %TRUE if stopping keyboard navigation is fine, %FALSE
* if the emitting widget should try to handle the keyboard
* navigation attempt in its parent container(s).
*
* Since: 2.12
**/
gboolean
gtk_widget_keynav_failed (GtkWidget *widget,
GtkDirectionType direction)
{
gboolean return_val;
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_signal_emit (widget, widget_signals[KEYNAV_FAILED], 0,
direction, &return_val);
return return_val;
}
/**
* gtk_widget_error_bell:
* @widget: a #GtkWidget
*
* Notifies the user about an input-related error on this widget. If
* the gtk-error-bell settings property is %TRUE, it calls
* gdk_window_beep(), otherwise it does nothing.
*
* Note that the effect of gdk_window_beep() can be configured in many
* ways, depending on the windowing backend and the desktop environment
* or window manager that is used.
*
* Since: 2.12
**/
void
gtk_widget_error_bell (GtkWidget *widget)
{
gboolean beep;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_object_get (gtk_widget_get_settings (widget),
"gtk-error-bell", &beep,
NULL);
if (beep && widget->window)
gdk_window_beep (widget->window);
}
/**
* gtk_widget_set_uposition:
* @widget: a #GtkWidget

View File

@ -408,7 +408,7 @@ struct _GtkWidgetClass
GdkEventGrabBroken *event);
void (* composited_changed) (GtkWidget *widget);
/* Padding for future expansion */
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
@ -563,6 +563,9 @@ GdkWindow *gtk_widget_get_parent_window (GtkWidget *widget);
gboolean gtk_widget_child_focus (GtkWidget *widget,
GtkDirectionType direction);
gboolean gtk_widget_keynav_failed (GtkWidget *widget,
GtkDirectionType direction);
void gtk_widget_error_bell (GtkWidget *widget);
void gtk_widget_set_size_request (GtkWidget *widget,
gint width,