diff options
-rw-r--r-- | gtk/gtktextview.c | 301 |
1 files changed, 282 insertions, 19 deletions
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index f8188483a9..9fd3c7d9a7 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -50,6 +50,7 @@ #include "gtkwindow.h" #include "gtkscrollable.h" #include "gtktypebuiltins.h" +#include "gtktexthandleprivate.h" #include "a11y/gtktextviewaccessible.h" @@ -132,6 +133,7 @@ struct _GtkTextViewPrivate GdkDevice *dnd_device; gulong selection_drag_handler; + GtkTextHandle *text_handle; GtkTextWindow *text_window; GtkTextWindow *left_window; @@ -229,6 +231,8 @@ struct _GtkTextViewPrivate * driving the scrollable adjustment values */ guint hscroll_policy : 1; guint vscroll_policy : 1; + guint cursor_handle_dragged : 1; + guint selection_handle_dragged : 1; }; struct _GtkTextPendingScroll @@ -454,6 +458,8 @@ static void gtk_text_view_target_list_notify (GtkTextBuffer *buffer, static void gtk_text_view_paste_done_handler (GtkTextBuffer *buffer, GtkClipboard *clipboard, gpointer data); +static void gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer, + gpointer data); static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view, GtkTextIter *cursor, gint *x, @@ -497,6 +503,15 @@ static void gtk_text_view_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); +/* GtkTextHandle handlers */ +static void gtk_text_view_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkTextView *text_view); +static void gtk_text_view_update_handles (GtkTextView *text_view, + GtkTextHandleMode mode); + /* FIXME probably need the focus methods. */ typedef struct _GtkTextViewChild GtkTextViewChild; @@ -1456,6 +1471,10 @@ gtk_text_view_init (GtkTextView *text_view) /* We handle all our own redrawing */ gtk_widget_set_redraw_on_allocate (widget, FALSE); + + priv->text_handle = _gtk_text_handle_new (widget); + g_signal_connect (priv->text_handle, "handle-dragged", + G_CALLBACK (gtk_text_view_handle_dragged), text_view); } /** @@ -1558,6 +1577,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_signal_handlers_disconnect_by_func (priv->buffer, gtk_text_view_paste_done_handler, text_view); + g_signal_handlers_disconnect_by_func (priv->buffer, + gtk_text_view_buffer_changed_handler, + text_view); if (gtk_widget_get_realized (GTK_WIDGET (text_view))) { @@ -1607,6 +1629,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_signal_connect (priv->buffer, "paste-done", G_CALLBACK (gtk_text_view_paste_done_handler), text_view); + g_signal_connect (priv->buffer, "changed", + G_CALLBACK (gtk_text_view_buffer_changed_handler), + text_view); gtk_text_view_target_list_notify (priv->buffer, NULL, text_view); @@ -1616,6 +1641,8 @@ gtk_text_view_set_buffer (GtkTextView *text_view, GDK_SELECTION_PRIMARY); gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard); } + + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE); } _gtk_text_view_accessible_set_buffer (text_view, old_buffer); @@ -3106,6 +3133,7 @@ gtk_text_view_finalize (GObject *object) if (priv->bottom_window) text_window_free (priv->bottom_window); + g_object_unref (priv->text_handle); g_object_unref (priv->im_context); g_free (priv->im_module); @@ -4107,6 +4135,8 @@ gtk_text_view_realize (GtkWidget *widget) /* Ensure updating the spot location. */ gtk_text_view_update_im_spot_location (text_view); + + _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_window->window); } static void @@ -4147,6 +4177,8 @@ gtk_text_view_unrealize (GtkWidget *widget) if (priv->bottom_window) text_window_unrealize (priv->bottom_window); + _gtk_text_handle_set_relative_to (priv->text_handle, NULL); + GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget); } @@ -4414,6 +4446,174 @@ emit_event_on_tags (GtkWidget *widget, return retval; } +static void +gtk_text_view_set_handle_position (GtkTextView *text_view, + GtkTextIter *iter, + GtkTextHandlePosition pos) +{ + GtkTextViewPrivate *priv; + GdkRectangle rect; + gint x, y; + + priv = text_view->priv; + gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL); + + x = rect.x - priv->xoffset; + y = rect.y - priv->yoffset; + + if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) && + (x < 0 || x > SCREEN_WIDTH (text_view) || + y < 0 || y > SCREEN_HEIGHT (text_view))) + { + /* Hide the handle if it's not being manipulated + * and fell outside of the visible text area. + */ + _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE); + } + else + { + _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE); + + rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view)); + rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view)); + _gtk_text_handle_set_position (priv->text_handle, pos, &rect); + } +} + +static void +gtk_text_view_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + GtkTextIter old_cursor, old_bound; + GtkTextIter cursor, bound, iter; + GtkTextIter *min, *max; + GtkTextHandleMode mode; + GtkTextBuffer *buffer; + GtkTextHandlePosition cursor_pos; + + priv = text_view->priv; + buffer = get_buffer (text_view); + mode = _gtk_text_handle_get_mode (handle); + + gtk_text_layout_get_iter_at_pixel (priv->layout, &iter, + x + priv->xoffset, + y + priv->yoffset); + gtk_text_buffer_get_iter_at_mark (buffer, &old_cursor, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_get_iter_at_mark (buffer, &old_bound, + gtk_text_buffer_get_selection_bound (buffer)); + cursor = old_cursor; + bound = old_bound; + + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR || + gtk_text_iter_compare (&cursor, &bound) >= 0) + { + cursor_pos = GTK_TEXT_HANDLE_POSITION_CURSOR; + max = &cursor; + min = &bound; + } + else + { + cursor_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START; + max = &bound; + min = &cursor; + } + + if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END) + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&iter, min) <= 0) + { + iter = *min; + gtk_text_iter_forward_char (&iter); + } + + *max = iter; + gtk_text_view_set_handle_position (text_view, &iter, pos); + } + else + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&iter, max) >= 0) + { + iter = *max; + gtk_text_iter_backward_char (&iter); + } + + *min = iter; + gtk_text_view_set_handle_position (text_view, &iter, pos); + } + + if (gtk_text_iter_compare (&old_cursor, &cursor) != 0 || + gtk_text_iter_compare (&old_bound, &bound) != 0) + { + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR) + gtk_text_buffer_place_cursor (buffer, &cursor); + else + gtk_text_buffer_select_range (buffer, &cursor, &bound); + + if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos)) + gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_buffer_get_insert (buffer)); + else + gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_buffer_get_selection_bound (buffer)); + } +} + +static void +gtk_text_view_update_handles (GtkTextView *text_view, + GtkTextHandleMode mode) +{ + GtkTextViewPrivate *priv = text_view->priv; + GtkTextIter cursor, bound, min, max; + GtkTextBuffer *buffer; + + buffer = get_buffer (text_view); + + gtk_text_buffer_get_iter_at_mark (buffer, &cursor, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_get_iter_at_mark (buffer, &bound, + gtk_text_buffer_get_selection_bound (buffer)); + + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&cursor, &bound) == 0) + { + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + } + + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR && + (!gtk_widget_is_sensitive (GTK_WIDGET (text_view)) || !priv->cursor_visible)) + { + mode = GTK_TEXT_HANDLE_MODE_NONE; + } + + _gtk_text_handle_set_mode (priv->text_handle, mode); + + if (gtk_text_iter_compare (&cursor, &bound) >= 0) + { + min = bound; + max = cursor; + } + else + { + min = cursor; + max = bound; + } + + if (mode != GTK_TEXT_HANDLE_MODE_NONE) + gtk_text_view_set_handle_position (text_view, &max, + GTK_TEXT_HANDLE_POSITION_SELECTION_END); + + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION) + gtk_text_view_set_handle_position (text_view, &min, + GTK_TEXT_HANDLE_POSITION_SELECTION_START); +} + static gint gtk_text_view_event (GtkWidget *widget, GdkEvent *event) { @@ -4545,6 +4745,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); + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + return retval; } @@ -4579,6 +4782,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) { GtkTextView *text_view; GtkTextViewPrivate *priv; + GdkDevice *device; + gboolean is_touchscreen; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4602,6 +4807,9 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout); #endif + device = gdk_event_get_source_device ((GdkEvent *) event); + is_touchscreen = gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN; + if (event->type == GDK_BUTTON_PRESS) { gtk_text_view_reset_im_context (text_view); @@ -4638,7 +4846,16 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } else { + GtkTextHandleMode mode; + gtk_text_view_start_selection_drag (text_view, &iter, event); + + if (gtk_widget_is_sensitive (widget) && is_touchscreen) + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); } return TRUE; @@ -4669,6 +4886,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) event->button == GDK_BUTTON_PRIMARY) { GtkTextIter iter; + GtkTextHandleMode mode; gtk_text_view_end_selection_drag (text_view); @@ -4676,11 +4894,18 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) &iter, event->x + priv->xoffset, event->y + priv->yoffset); - + gtk_text_view_start_selection_drag (text_view, &iter, event); + + if (gtk_widget_is_sensitive (widget) && is_touchscreen) + mode = GTK_TEXT_HANDLE_MODE_SELECTION; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); return TRUE; } - + return FALSE; } @@ -4708,6 +4933,8 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) return TRUE; else if (priv->pending_place_cursor_button == event->button) { + GtkTextHandleMode mode; + GdkDevice *device; GtkTextIter iter; /* Unselect everything; we clicked inside selection, but @@ -4721,9 +4948,18 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) gtk_text_buffer_place_cursor (get_buffer (text_view), &iter); gtk_text_view_check_cursor_blink (text_view); - + + device = gdk_event_get_source_device ((GdkEvent *) event); + + if (gtk_widget_is_sensitive (widget) && + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN) + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); priv->pending_place_cursor_button = 0; - + return FALSE; } } @@ -4797,6 +5033,8 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event) g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), keymap_direction_changed, text_view); + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); if (priv->editable) { @@ -6102,6 +6340,14 @@ gtk_text_view_paste_done_handler (GtkTextBuffer *buffer, } static void +gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer, + gpointer data) +{ + GtkTextView *text_view = data; + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE); +} + +static void gtk_text_view_toggle_overwrite (GtkTextView *text_view) { GtkTextViewPrivate *priv = text_view->priv; @@ -6256,24 +6502,25 @@ get_iter_at_pointer (GtkTextView *text_view, } static void -move_mark_to_pointer_and_scroll (GtkTextView *text_view, - const gchar *mark_name, - GdkDevice *device) +move_mark_to_pointer_and_scroll (GtkTextView *text_view, + const gchar *mark_name, + GdkDevice *device, + GdkInputSource source) { GtkTextIter newplace; + GtkTextBuffer *buffer; GtkTextMark *mark; + buffer = get_buffer (text_view); get_iter_at_pointer (text_view, device, &newplace, NULL, NULL); - - mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name); - + + mark = gtk_text_buffer_get_mark (buffer, mark_name); + /* This may invalidate the layout */ DV(g_print (G_STRLOC": move mark\n")); - - gtk_text_buffer_move_mark (get_buffer (text_view), - mark, - &newplace); - + + gtk_text_buffer_move_mark (buffer, mark, &newplace); + DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, mark); @@ -6458,16 +6705,22 @@ selection_motion_event_handler (GtkTextView *text_view, SelectionData *data) { GtkTextViewPrivate *priv; + GdkInputSource input_source; + GdkDevice *device; priv = text_view->priv; gdk_event_request_motions (event); + device = gdk_event_get_source_device ((GdkEvent *) event); + input_source = gdk_device_get_source (device); + if (priv->grab_device != event->device) return FALSE; if (data->granularity == SELECT_CHARACTERS) { - move_mark_to_pointer_and_scroll (text_view, "insert", event->device); + move_mark_to_pointer_and_scroll (text_view, "insert", + event->device, input_source); } else { @@ -6491,7 +6744,7 @@ selection_motion_event_handler (GtkTextView *text_view, else gtk_text_buffer_select_range (buffer, &end, &orig_start); - gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer)); } @@ -6506,6 +6759,9 @@ selection_motion_event_handler (GtkTextView *text_view, text_view->priv->scroll_timeout = gdk_threads_add_timeout (50, selection_scan_timeout, text_view); + if (input_source == GDK_SOURCE_TOUCHSCREEN) + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION); + return TRUE; } @@ -7757,7 +8013,10 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment, * changes made by the validation are pushed through. */ gtk_text_view_update_im_spot_location (text_view); - + + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (priv->text_handle)); + DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n")); } @@ -7933,7 +8192,11 @@ gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, } if (need_reset) - gtk_text_view_reset_im_context (text_view); + { + gtk_text_view_reset_im_context (text_view); + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (text_view->priv->text_handle)); + } } static void |