X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextview.c;h=ed3d98bb361c6645acd892d300e1a98ab19e704f;hb=320613c439c6c7eeb5bc64685522195e0a6adc4e;hp=f0eca6575fc9d68dc18a88b0a46ad385cd6eda88;hpb=8885320d211aa55bcfa63e43ee4583b5533364ce;p=~andy%2Fgtk diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index f0eca6575..ed3d98bb3 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -26,6 +24,7 @@ */ #include "config.h" + #include #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API @@ -39,17 +38,35 @@ #include "gtkmenuitem.h" #include "gtkseparatormenuitem.h" #include "gtksettings.h" +#include "gtkselectionprivate.h" #include "gtkstock.h" #include "gtktextbufferrichtext.h" #include "gtktextdisplay.h" #include "gtktextview.h" #include "gtkimmulticontext.h" -#include "gdk/gdkkeysyms.h" #include "gtkprivate.h" #include "gtktextutil.h" #include "gtkwidgetprivate.h" #include "gtkwindow.h" #include "gtkscrollable.h" +#include "gtktypebuiltins.h" +#include "gtktexthandleprivate.h" +#include "gtkstylecontextprivate.h" +#include "gtkcssstylepropertyprivate.h" +#include "gtkselectionwindow.h" + +#include "a11y/gtktextviewaccessibleprivate.h" + +/** + * SECTION:gtktextview + * @Short_description: Widget that displays a GtkTextBuffer + * @Title: GtkTextView + * @See_also: #GtkTextBuffer, #GtkTextIter + * + * You may wish to begin by reading the text widget + * conceptual overview which gives an overview of all the objects and data + * types related to the text widget and how they work together. + */ /* How scrolling, validation, exposes, etc. work. @@ -119,7 +136,9 @@ struct _GtkTextViewPrivate GdkDevice *dnd_device; gulong selection_drag_handler; - guint scroll_timeout; + GtkTextHandle *text_handle; + GtkWidget *selection_bubble; + guint selection_bubble_timeout_id; GtkTextWindow *text_window; GtkTextWindow *left_window; @@ -162,12 +181,16 @@ struct _GtkTextViewPrivate GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */ gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */ - GtkTextMark *dnd_mark; guint blink_timeout; + guint scroll_timeout; guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */ guint incremental_validate_idle; /* Idle to revalidate offscreen portions, runs after redraw */ + gint pending_place_cursor_button; + + GtkTextMark *dnd_mark; + GtkIMContext *im_context; GtkWidget *popup_menu; @@ -178,8 +201,6 @@ struct _GtkTextViewPrivate GtkTextPendingScroll *pending_scroll; - gint pending_place_cursor_button; - /* Default style settings */ gint pixels_above_lines; gint pixels_below_lines; @@ -195,7 +216,7 @@ struct _GtkTextViewPrivate guint overwrite_mode : 1; guint cursor_visible : 1; - /* if we have reset the IM since the last character entered */ + /* if we have reset the IM since the last character entered */ guint need_im_reset : 1; guint accepts_tab : 1; @@ -210,6 +231,13 @@ struct _GtkTextViewPrivate guint mouse_cursor_obscured : 1; guint scroll_after_paste : 1; + + /* GtkScrollablePolicy needs to be checked when + * 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 @@ -260,7 +288,11 @@ enum PROP_ACCEPTS_TAB, PROP_IM_MODULE, PROP_HADJUSTMENT, - PROP_VADJUSTMENT + PROP_VADJUSTMENT, + PROP_HSCROLL_POLICY, + PROP_VSCROLL_POLICY, + PROP_INPUT_PURPOSE, + PROP_INPUT_HINTS }; static void gtk_text_view_finalize (GObject *object); @@ -275,18 +307,23 @@ static void gtk_text_view_get_property (GObject *object, static void gtk_text_view_destroy (GtkWidget *widget); static void gtk_text_view_size_request (GtkWidget *widget, GtkRequisition *requisition); +static void gtk_text_view_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural); +static void gtk_text_view_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural); static void gtk_text_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_text_view_realize (GtkWidget *widget); static void gtk_text_view_unrealize (GtkWidget *widget); -static void gtk_text_view_style_set (GtkWidget *widget, - GtkStyle *previous_style); +static void gtk_text_view_style_updated (GtkWidget *widget); static void gtk_text_view_direction_changed (GtkWidget *widget, GtkTextDirection previous_direction); static void gtk_text_view_grab_notify (GtkWidget *widget, gboolean was_grabbed); -static void gtk_text_view_state_changed (GtkWidget *widget, - GtkStateType previous_state); +static void gtk_text_view_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state); static gint gtk_text_view_event (GtkWidget *widget, GdkEvent *event); @@ -312,7 +349,7 @@ static gboolean gtk_text_view_focus (GtkWidget *widget, GtkDirectionType direction); static void gtk_text_view_select_all (GtkWidget *widget, gboolean select); - +static gboolean get_middle_click_paste (GtkTextView *text_view); /* Source side drag signals */ static void gtk_text_view_drag_begin (GtkWidget *widget, @@ -355,7 +392,7 @@ static void gtk_text_view_move_cursor (GtkTextView *text_view, GtkMovementStep step, gint count, gboolean extend_selection); -static gboolean gtk_text_view_move_viewport (GtkTextView *text_view, +static void gtk_text_view_move_viewport (GtkTextView *text_view, GtkScrollStep step, gint count); static void gtk_text_view_set_anchor (GtkTextView *text_view); @@ -384,8 +421,7 @@ static void gtk_text_view_get_first_para_iter (GtkTextView *text_vi GtkTextIter *iter); static void gtk_text_view_update_layout_width (GtkTextView *text_view); static void gtk_text_view_set_attributes_from_style (GtkTextView *text_view, - GtkTextAttributes *values, - GtkStyle *style); + GtkTextAttributes *values); static void gtk_text_view_ensure_layout (GtkTextView *text_view); static void gtk_text_view_destroy_layout (GtkTextView *text_view); static void gtk_text_view_check_keymap_direction (GtkTextView *text_view); @@ -401,7 +437,7 @@ static void gtk_text_view_pend_cursor_blink (GtkTextView *text_v static void gtk_text_view_stop_cursor_blink (GtkTextView *text_view); static void gtk_text_view_reset_blink_time (GtkTextView *text_view); -static void gtk_text_view_value_changed (GtkAdjustment *adj, +static void gtk_text_view_value_changed (GtkAdjustment *adjustment, GtkTextView *view); static void gtk_text_view_commit_handler (GtkIMContext *context, const gchar *str, @@ -427,8 +463,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_get_cursor_location (GtkTextView *text_view, - GdkRectangle *pos); +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, @@ -472,6 +508,22 @@ 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_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkTextView *text_view); +static void gtk_text_view_update_handles (GtkTextView *text_view, + GtkTextHandleMode mode); + +static void gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view); +static void gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view); + + /* FIXME probably need the focus methods. */ typedef struct _GtkTextViewChild GtkTextViewChild; @@ -534,6 +586,7 @@ static gint text_window_get_height (GtkTextWindow *win); static guint signals[LAST_SIGNAL] = { 0 }; +static gboolean test_touchscreen = FALSE; G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) @@ -578,11 +631,12 @@ gtk_text_view_class_init (GtkTextViewClass *klass) widget_class->destroy = gtk_text_view_destroy; widget_class->realize = gtk_text_view_realize; widget_class->unrealize = gtk_text_view_unrealize; - widget_class->style_set = gtk_text_view_style_set; + widget_class->style_updated = gtk_text_view_style_updated; widget_class->direction_changed = gtk_text_view_direction_changed; widget_class->grab_notify = gtk_text_view_grab_notify; - widget_class->state_changed = gtk_text_view_state_changed; - widget_class->size_request = gtk_text_view_size_request; + widget_class->state_flags_changed = gtk_text_view_state_flags_changed; + widget_class->get_preferred_width = gtk_text_view_get_preferred_width; + widget_class->get_preferred_height = gtk_text_view_get_preferred_height; widget_class->size_allocate = gtk_text_view_size_allocate; widget_class->event = gtk_text_view_event; widget_class->key_press_event = gtk_text_view_key_press_event; @@ -753,7 +807,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) /** * GtkTextView:im-module: * - * Which IM (input method) module should be used for this entry. + * Which IM (input method) module should be used for this text_view. * See #GtkIMContext. * * Setting this to a non-%NULL value overrides the @@ -770,9 +824,47 @@ gtk_text_view_class_init (GtkTextViewClass *klass) NULL, GTK_PARAM_READWRITE)); + /** + * GtkTextView:input-purpose: + * + * The purpose of this text field. + * + * This property can be used by on-screen keyboards and other input + * methods to adjust their behaviour. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_PURPOSE, + g_param_spec_enum ("input-purpose", + P_("Purpose"), + P_("Purpose of the text field"), + GTK_TYPE_INPUT_PURPOSE, + GTK_INPUT_PURPOSE_FREE_FORM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GtkTextView:input-hints: + * + * Additional hints (beyond #GtkTextView:input-purpose) that + * allow input methods to fine-tune their behaviour. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_HINTS, + g_param_spec_flags ("input-hints", + P_("hints"), + P_("Hints for the text field behaviour"), + GTK_TYPE_INPUT_HINTS, + GTK_INPUT_HINT_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /* GtkScrollable interface */ - g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); - g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); + g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); + g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); + g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy"); + g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy"); /* * Style properties @@ -1029,7 +1121,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) /** * GtkTextView::populate-popup: - * @entry: The text view on which the signal is emitted + * @text_view: The text view on which the signal is emitted * @menu: the menu that is being populated * * The ::populate-popup signal gets emitted before showing the @@ -1324,6 +1416,9 @@ gtk_text_view_class_init (GtkTextViewClass *klass) GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate)); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_VIEW_ACCESSIBLE); + test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN") != NULL; } static void @@ -1390,6 +1485,12 @@ 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); + g_signal_connect (priv->text_handle, "drag-finished", + G_CALLBACK (gtk_text_view_handle_drag_finished), text_view); } /** @@ -1449,6 +1550,7 @@ gtk_text_view_set_buffer (GtkTextView *text_view, GtkTextBuffer *buffer) { GtkTextViewPrivate *priv; + GtkTextBuffer *old_buffer; g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer)); @@ -1458,6 +1560,7 @@ gtk_text_view_set_buffer (GtkTextView *text_view, if (priv->buffer == buffer) return; + old_buffer = priv->buffer; if (priv->buffer != NULL) { /* Destroy all anchored children */ @@ -1490,6 +1593,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))) { @@ -1501,7 +1607,6 @@ gtk_text_view_set_buffer (GtkTextView *text_view, if (priv->layout) gtk_text_layout_set_buffer (priv->layout, NULL); - g_object_unref (priv->buffer); priv->dnd_mark = NULL; priv->first_para_mark = NULL; cancel_pending_scroll (text_view); @@ -1540,6 +1645,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); @@ -1549,8 +1657,14 @@ 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); + if (old_buffer) + g_object_unref (old_buffer); + g_object_notify (G_OBJECT (text_view), "buffer"); if (gtk_widget_get_visible (GTK_WIDGET (text_view))) @@ -1592,6 +1706,61 @@ gtk_text_view_get_buffer (GtkTextView *text_view) return get_buffer (text_view); } +/** + * gtk_text_view_get_cursor_locations: + * @text_view: a #GtkTextView + * @iter: (allow-none): a #GtkTextIter + * @strong: (out) (allow-none): location to store the strong + * cursor position (may be %NULL) + * @weak: (out) (allow-none): location to store the weak + * cursor position (may be %NULL) + * + * Given an @iter within a text layout, determine the positions of the + * strong and weak cursors if the insertion point is at that + * iterator. The position of each cursor is stored as a zero-width + * rectangle. The strong cursor location is the location where + * characters of the directionality equal to the base direction of the + * paragraph are inserted. The weak cursor location is the location + * where characters of the directionality opposite to the base + * direction of the paragraph are inserted. + * + * If @iter is %NULL, the actual cursor position is used. + * + * Note that if @iter happens to be the actual cursor position, and + * there is currently an IM preedit sequence being entered, the + * returned locations will be adjusted to account for the preedit + * cursor's offset within the preedit sequence. + * + * The rectangle position is in buffer coordinates; use + * gtk_text_view_buffer_to_window_coords() to convert these + * coordinates to coordinates for one of the windows in the text view. + * + * Since: 3.0 + **/ +void +gtk_text_view_get_cursor_locations (GtkTextView *text_view, + const GtkTextIter *iter, + GdkRectangle *strong, + GdkRectangle *weak) +{ + GtkTextIter insert; + + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + g_return_if_fail (iter == NULL || + gtk_text_iter_get_buffer (iter) == get_buffer (text_view)); + + gtk_text_view_ensure_layout (text_view); + + if (iter) + insert = *iter; + else + gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert, + gtk_text_buffer_get_insert (get_buffer (text_view))); + + gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert, + strong, weak); +} + /** * gtk_text_view_get_iter_at_location: * @text_view: a #GtkTextView @@ -1741,30 +1910,6 @@ gtk_text_view_get_line_at_y (GtkTextView *text_view, line_top); } -static gboolean -set_adjustment_clamped (GtkAdjustment *adj, gdouble val) -{ - DV (g_print (" Setting adj to raw value %g\n", val)); - - /* We don't really want to clamp to upper; we want to clamp to - upper - page_size which is the highest value the scrollbar - will let us reach. */ - if (val > (adj->upper - adj->page_size)) - val = adj->upper - adj->page_size; - - if (val < adj->lower) - val = adj->lower; - - if (val != adj->value) - { - DV (g_print (" Setting adj to clamped value %g\n", val)); - gtk_adjustment_set_value (adj, val); - return TRUE; - } - else - return FALSE; -} - /** * gtk_text_view_scroll_to_iter: * @text_view: a #GtkTextView @@ -1899,8 +2044,8 @@ gtk_text_view_scroll_to_iter (GtkTextView *text_view, if (scroll_inc != 0) { - retval = set_adjustment_clamped (text_view->priv->vadjustment, - current_y_scroll + scroll_inc); + gtk_adjustment_set_value (text_view->priv->vadjustment, + current_y_scroll + scroll_inc); DV (g_print (" vert increment %d\n", scroll_inc)); } @@ -1936,12 +2081,15 @@ gtk_text_view_scroll_to_iter (GtkTextView *text_view, if (scroll_inc != 0) { - retval = set_adjustment_clamped (text_view->priv->hadjustment, - current_x_scroll + scroll_inc); + gtk_adjustment_set_value (text_view->priv->hadjustment, + current_x_scroll + scroll_inc); DV (g_print (" horiz increment %d\n", scroll_inc)); } + retval = (current_y_scroll != gtk_adjustment_get_value (text_view->priv->vadjustment)) + || (current_x_scroll != gtk_adjustment_get_value (text_view->priv->hadjustment)); + if (retval) { DV(g_print (">Actually scrolled ("G_STRLOC")\n")); @@ -2112,13 +2260,13 @@ gtk_text_view_update_im_spot_location (GtkTextView *text_view) if (text_view->priv->layout == NULL) return; - gtk_text_view_get_cursor_location (text_view, &area); + gtk_text_view_get_cursor_locations (text_view, NULL, &area, NULL); area.x -= text_view->priv->xoffset; area.y -= text_view->priv->yoffset; /* Width returned by Pango indicates direction of cursor, - * by it's sign more than the size of cursor. + * by its sign more than the size of cursor. */ area.width = 0; @@ -2286,7 +2434,7 @@ gtk_text_view_move_mark_onscreen (GtkTextView *text_view, /** * gtk_text_view_get_visible_rect: * @text_view: a #GtkTextView - * @visible_rect: rectangle to fill + * @visible_rect: (out): rectangle to fill * * Fills @visible_rect with the currently-visible * region of the buffer, in buffer coordinates. Convert to window coordinates @@ -2338,7 +2486,7 @@ gtk_text_view_set_wrap_mode (GtkTextView *text_view, { priv->wrap_mode = wrap_mode; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->wrap_mode = wrap_mode; gtk_text_layout_default_style_changed (priv->layout); @@ -2398,7 +2546,7 @@ gtk_text_view_set_editable (GtkTextView *text_view, if (setting && gtk_widget_has_focus (GTK_WIDGET (text_view))) gtk_im_context_focus_in (priv->im_context); - if (priv->layout) + if (priv->layout && priv->layout->default_style) { gtk_text_layout_set_overwrite_mode (priv->layout, priv->overwrite_mode && priv->editable); @@ -2449,7 +2597,7 @@ gtk_text_view_set_pixels_above_lines (GtkTextView *text_view, { priv->pixels_above_lines = pixels_above_lines; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->pixels_above_lines = pixels_above_lines; gtk_text_layout_default_style_changed (priv->layout); @@ -2498,7 +2646,7 @@ gtk_text_view_set_pixels_below_lines (GtkTextView *text_view, { priv->pixels_below_lines = pixels_below_lines; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->pixels_below_lines = pixels_below_lines; gtk_text_layout_default_style_changed (priv->layout); @@ -2547,7 +2695,7 @@ gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view, { priv->pixels_inside_wrap = pixels_inside_wrap; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->pixels_inside_wrap = pixels_inside_wrap; gtk_text_layout_default_style_changed (priv->layout); @@ -2596,7 +2744,7 @@ gtk_text_view_set_justification (GtkTextView *text_view, { priv->justify = justification; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->justification = justification; gtk_text_layout_default_style_changed (priv->layout); @@ -2645,7 +2793,7 @@ gtk_text_view_set_left_margin (GtkTextView *text_view, { priv->left_margin = left_margin; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->left_margin = left_margin; gtk_text_layout_default_style_changed (priv->layout); @@ -2692,7 +2840,7 @@ gtk_text_view_set_right_margin (GtkTextView *text_view, { priv->right_margin = right_margin; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->right_margin = right_margin; gtk_text_layout_default_style_changed (priv->layout); @@ -2741,7 +2889,7 @@ gtk_text_view_set_indent (GtkTextView *text_view, { priv->indent = indent; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->indent = indent; gtk_text_layout_default_style_changed (priv->layout); @@ -2792,7 +2940,7 @@ gtk_text_view_set_tabs (GtkTextView *text_view, priv->tabs = tabs ? pango_tab_array_copy (tabs) : NULL; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { /* some unkosher futzing in internal struct details... */ if (priv->layout->default_style->tabs) @@ -2971,10 +3119,11 @@ gtk_text_view_finalize (GObject *object) text_view = GTK_TEXT_VIEW (object); priv = text_view->priv; - g_assert (priv->buffer == NULL); - gtk_text_view_destroy_layout (text_view); gtk_text_view_set_buffer (text_view, NULL); + + /* at this point, no "notify::buffer" handler should recreate the buffer. */ + g_assert (priv->buffer == NULL); cancel_pending_scroll (text_view); @@ -3000,6 +3149,10 @@ gtk_text_view_finalize (GObject *object) if (priv->bottom_window) text_window_free (priv->bottom_window); + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + g_object_unref (priv->text_handle); g_object_unref (priv->im_context); g_free (priv->im_module); @@ -3092,6 +3245,24 @@ gtk_text_view_set_property (GObject *object, gtk_text_view_set_vadjustment (text_view, g_value_get_object (value)); break; + case PROP_HSCROLL_POLICY: + priv->hscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (text_view)); + break; + + case PROP_VSCROLL_POLICY: + priv->vscroll_policy = g_value_get_enum (value); + gtk_widget_queue_resize (GTK_WIDGET (text_view)); + break; + + case PROP_INPUT_PURPOSE: + gtk_text_view_set_input_purpose (text_view, g_value_get_enum (value)); + break; + + case PROP_INPUT_HINTS: + gtk_text_view_set_input_hints (text_view, g_value_get_flags (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3180,6 +3351,22 @@ gtk_text_view_get_property (GObject *object, g_value_set_object (value, priv->vadjustment); break; + case PROP_HSCROLL_POLICY: + g_value_set_enum (value, priv->hscroll_policy); + break; + + case PROP_VSCROLL_POLICY: + g_value_set_enum (value, priv->vscroll_policy); + break; + + case PROP_INPUT_PURPOSE: + g_value_set_enum (value, gtk_text_view_get_input_purpose (text_view)); + break; + + case PROP_INPUT_HINTS: + g_value_set_flags (value, gtk_text_view_get_input_hints (text_view)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3278,6 +3465,31 @@ gtk_text_view_size_request (GtkWidget *widget, priv->cached_size_request = *requisition; } +static void +gtk_text_view_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + gtk_text_view_size_request (widget, &requisition); + + *minimum = *natural = requisition.width; +} + +static void +gtk_text_view_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + gtk_text_view_size_request (widget, &requisition); + + *minimum = *natural = requisition.height; +} + + static void gtk_text_view_compute_child_allocation (GtkTextView *text_view, GtkTextViewChild *vc, @@ -3804,7 +4016,6 @@ changed_handler (GtkTextLayout *layout, if (old_height != new_height) { - gboolean yoffset_changed = FALSE; GSList *tmp_list; int new_first_para_top; int old_first_para_top; @@ -3830,14 +4041,7 @@ changed_handler (GtkTextLayout *layout, { priv->yoffset += new_first_para_top - old_first_para_top; - text_view->priv->vadjustment->value = priv->yoffset; - yoffset_changed = TRUE; - } - - if (yoffset_changed) - { - DV(g_print ("Changing scroll position (%s)\n", G_STRLOC)); - gtk_adjustment_value_changed (text_view->priv->vadjustment); + gtk_adjustment_set_value (text_view->priv->vadjustment, priv->yoffset); } /* FIXME be smarter about which anchored widgets we update */ @@ -3862,7 +4066,7 @@ changed_handler (GtkTextLayout *layout, * to avoid the optimization which just returns widget->requisition * if a resize hasn't been queued. */ - GTK_WIDGET_GET_CLASS (widget)->size_request (widget, &new_req); + gtk_text_view_size_request (widget, &new_req); if (old_req.width != new_req.width || old_req.height != new_req.height) @@ -3878,11 +4082,12 @@ gtk_text_view_realize (GtkWidget *widget) GtkAllocation allocation; GtkTextView *text_view; GtkTextViewPrivate *priv; + GtkStyleContext *context; GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; GSList *tmp_list; - + text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -3904,13 +4109,14 @@ gtk_text_view_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, widget); + gtk_widget_register_window (widget, window); - /* must come before text_window_realize calls */ - gtk_widget_style_attach (widget); + context = gtk_widget_get_style_context (widget); - gdk_window_set_background (window, - >k_widget_get_style (widget)->bg[gtk_widget_get_state (widget)]); + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + gtk_style_context_set_background (context, window); + gtk_style_context_restore (context); text_window_realize (priv->text_window, widget); @@ -3927,6 +4133,7 @@ gtk_text_view_realize (GtkWidget *widget) text_window_realize (priv->bottom_window, widget); gtk_text_view_ensure_layout (text_view); + gtk_text_view_invalidate (text_view); if (priv->buffer) { @@ -3947,6 +4154,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 @@ -3987,7 +4196,7 @@ gtk_text_view_unrealize (GtkWidget *widget) if (priv->bottom_window) text_window_unrealize (priv->bottom_window); - gtk_text_view_destroy_layout (text_view); + _gtk_text_handle_set_relative_to (priv->text_handle, NULL); GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget); } @@ -3995,60 +4204,70 @@ gtk_text_view_unrealize (GtkWidget *widget) static void gtk_text_view_set_background (GtkTextView *text_view) { - GtkStyle *style; - GtkStateType state; + GtkStyleContext *context; + GtkStateFlags state; GtkWidget *widget; GtkTextViewPrivate *priv; + GdkRGBA color; widget = GTK_WIDGET (text_view); priv = text_view->priv; - style = gtk_widget_get_style (widget); - state = gtk_widget_get_state (widget); + context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); + + /* Set bin window background */ + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); - gdk_window_set_background (gtk_widget_get_window (widget), - &style->bg[state]); + gtk_style_context_set_background (context, priv->text_window->bin_window); + gtk_style_context_set_background (context, gtk_widget_get_window (widget)); - gdk_window_set_background (priv->text_window->bin_window, - &style->base[state]); + gtk_style_context_restore (context); + + /* Set lateral panes background */ + gtk_style_context_get_background_color (context, state, &color); if (priv->left_window) - gdk_window_set_background (priv->left_window->bin_window, - &style->bg[state]); + gdk_window_set_background_rgba (priv->left_window->bin_window, &color); + if (priv->right_window) - gdk_window_set_background (priv->right_window->bin_window, - &style->bg[state]); + gdk_window_set_background_rgba (priv->right_window->bin_window, &color); if (priv->top_window) - gdk_window_set_background (priv->top_window->bin_window, - &style->bg[state]); + gdk_window_set_background_rgba (priv->top_window->bin_window, &color); if (priv->bottom_window) - gdk_window_set_background (priv->bottom_window->bin_window, - &style->bg[state]); + gdk_window_set_background_rgba (priv->bottom_window->bin_window, &color); } static void -gtk_text_view_style_set (GtkWidget *widget, - GtkStyle *previous_style) +gtk_text_view_style_updated (GtkWidget *widget) { GtkTextView *text_view; GtkTextViewPrivate *priv; PangoContext *ltr_context, *rtl_context; + GtkStyleContext *style_context; + const GtkBitmask *changes; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; + GTK_WIDGET_CLASS (gtk_text_view_parent_class)->style_updated (widget); + if (gtk_widget_get_realized (widget)) { gtk_text_view_set_background (text_view); } - if (priv->layout && previous_style) + + style_context = gtk_widget_get_style_context (widget); + changes = _gtk_style_context_get_changes (style_context); + if ((changes == NULL || _gtk_css_style_property_changes_affect_font (changes)) && + priv->layout && priv->layout->default_style) { gtk_text_view_set_attributes_from_style (text_view, - priv->layout->default_style, - gtk_widget_get_style (widget)); + priv->layout->default_style); ltr_context = gtk_widget_create_pango_context (widget); pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR); @@ -4068,7 +4287,7 @@ gtk_text_view_direction_changed (GtkWidget *widget, { GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv; - if (priv->layout) + if (priv->layout && priv->layout->default_style) { priv->layout->default_style->direction = gtk_widget_get_direction (widget); @@ -4077,8 +4296,8 @@ gtk_text_view_direction_changed (GtkWidget *widget, } static void -gtk_text_view_state_changed (GtkWidget *widget, - GtkStateType previous_state) +gtk_text_view_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state) { GtkTextView *text_view = GTK_TEXT_VIEW (widget); GdkCursor *cursor; @@ -4095,7 +4314,7 @@ gtk_text_view_state_changed (GtkWidget *widget, gdk_window_set_cursor (text_view->priv->text_window->bin_window, cursor); if (cursor) - gdk_cursor_unref (cursor); + g_object_unref (cursor); text_view->priv->mouse_cursor_obscured = FALSE; } @@ -4120,7 +4339,7 @@ set_invisible_cursor (GdkWindow *window) gdk_window_set_cursor (window, cursor); - gdk_cursor_unref (cursor); + g_object_unref (cursor); } static void @@ -4144,7 +4363,7 @@ gtk_text_view_unobscure_mouse_cursor (GtkTextView *text_view) cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)), GDK_XTERM); gdk_window_set_cursor (text_view->priv->text_window->bin_window, cursor); - gdk_cursor_unref (cursor); + g_object_unref (cursor); text_view->priv->mouse_cursor_obscured = FALSE; } } @@ -4160,8 +4379,15 @@ gtk_text_view_grab_notify (GtkWidget *widget, if (priv->grab_device && gtk_widget_device_is_shadowed (widget, priv->grab_device)) { + if (priv->drag_start_x >= 0) + { + priv->drag_start_x = -1; + priv->drag_start_y = -1; + } + gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget)); gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget)); + priv->grab_device = NULL; } } @@ -4245,6 +4471,183 @@ 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_view_selection_bubble_popup_unset (text_view); + 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_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkTextView *text_view) +{ + gtk_text_view_selection_bubble_popup_set (text_view); +} + +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) { @@ -4376,6 +4779,12 @@ 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 (!event->send_event) + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + + gtk_text_view_selection_bubble_popup_unset (text_view); + return retval; } @@ -4410,6 +4819,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; @@ -4424,20 +4835,30 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } gtk_text_view_reset_blink_time (text_view); + gtk_text_view_selection_bubble_popup_unset (text_view); #if 0 /* debug hack */ - if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0) + if (event->button == GDK_BUTTON_SECONDARY && (event->state & GDK_CONTROL_MASK) != 0) _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer); - else if (event->button == 3) + else if (event->button == GDK_BUTTON_SECONDARY) gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout); #endif + device = gdk_event_get_source_device ((GdkEvent *) event); + is_touchscreen = test_touchscreen || + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN; + if (event->type == GDK_BUTTON_PRESS) { gtk_text_view_reset_im_context (text_view); - if (event->button == 1) + if (gdk_event_triggers_context_menu ((GdkEvent *) event)) + { + gtk_text_view_do_popup (text_view, event); + return TRUE; + } + else if (event->button == GDK_BUTTON_PRIMARY) { /* If we're in the selection, start a drag copy/move of the * selection; otherwise, start creating a new selection. @@ -4453,20 +4874,33 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view), &start, &end) && gtk_text_iter_in_range (&iter, &start, &end) && - !(event->state & GDK_SHIFT_MASK)) + !(event->state & + gtk_widget_get_modifier_mask (widget, + GDK_MODIFIER_INTENT_EXTEND_SELECTION))) { + priv->grab_device = event->device; priv->drag_start_x = event->x; priv->drag_start_y = event->y; priv->pending_place_cursor_button = event->button; } 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; } - else if (event->button == 2) + else if (event->button == GDK_BUTTON_MIDDLE && + get_middle_click_paste (text_view)) { GtkTextIter iter; @@ -4485,17 +4919,13 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) priv->editable); return TRUE; } - else if (event->button == 3) - { - gtk_text_view_do_popup (text_view, event); - return TRUE; - } } else if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) && - event->button == 1) + event->button == GDK_BUTTON_PRIMARY) { GtkTextIter iter; + GtkTextHandleMode mode; gtk_text_view_end_selection_drag (text_view); @@ -4503,11 +4933,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; } @@ -4516,14 +4953,16 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) { GtkTextView *text_view; GtkTextViewPrivate *priv; + GdkDevice *device; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; + device = gdk_event_get_source_device ((GdkEvent *) event); if (event->window != priv->text_window->bin_window) return FALSE; - if (event->button == 1) + if (event->button == GDK_BUTTON_PRIMARY) { if (priv->drag_start_x >= 0) { @@ -4532,9 +4971,15 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) } if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget))) - return TRUE; + { + if (test_touchscreen || + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN) + gtk_text_view_selection_bubble_popup_set (text_view); + return TRUE; + } else if (priv->pending_place_cursor_button == event->button) { + GtkTextHandleMode mode; GtkTextIter iter; /* Unselect everything; we clicked inside selection, but @@ -4548,9 +4993,17 @@ 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); - + + if (gtk_widget_is_sensitive (widget) && + (test_touchscreen || + 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; } } @@ -4624,6 +5077,9 @@ 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_view_selection_bubble_popup_unset (text_view); + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); if (priv->editable) { @@ -4686,8 +5142,6 @@ gtk_text_view_paint (GtkWidget *widget, { GtkTextView *text_view; GtkTextViewPrivate *priv; - GList *child_exposes; - GList *tmp_list; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4715,33 +5169,15 @@ gtk_text_view_paint (GtkWidget *widget, area->width, area->height); #endif - child_exposes = NULL; - cairo_save (cr); cairo_translate (cr, -priv->xoffset, -priv->yoffset); gtk_text_layout_draw (priv->layout, widget, cr, - &child_exposes); + NULL); cairo_restore (cr); - - tmp_list = child_exposes; - while (tmp_list != NULL) - { - GtkWidget *child = tmp_list->data; - - gtk_container_propagate_draw (GTK_CONTAINER (text_view), - child, - cr); - - g_object_unref (child); - - tmp_list = tmp_list->next; - } - - g_list_free (child_exposes); } static gboolean @@ -4750,9 +5186,22 @@ gtk_text_view_draw (GtkWidget *widget, { GSList *tmp_list; GdkWindow *window; - + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) - gtk_text_view_draw_focus (widget, cr); + { + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + gtk_render_background (context, cr, + 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + gtk_style_context_restore (context); + + gtk_text_view_draw_focus (widget, cr); + } window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); @@ -4776,10 +5225,9 @@ gtk_text_view_draw (GtkWidget *widget, /* propagate_draw checks that event->window matches * child->window */ - if (!vc->anchor) - gtk_container_propagate_draw (GTK_CONTAINER (widget), - vc->widget, - cr); + gtk_container_propagate_draw (GTK_CONTAINER (widget), + vc->widget, + cr); tmp_list = tmp_list->next; } @@ -4798,14 +5246,15 @@ gtk_text_view_draw_focus (GtkWidget *widget, "interior-focus", &interior_focus, NULL); - if (gtk_widget_has_focus (widget) && !interior_focus) - { - gtk_paint_focus (gtk_widget_get_style (widget), cr, - gtk_widget_get_state (widget), - widget, "textview", - 0, 0, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); + if (gtk_widget_has_visible_focus (widget) && !interior_focus) + { + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + + gtk_render_focus (context, cr, 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); } } @@ -4815,24 +5264,31 @@ gtk_text_view_focus (GtkWidget *widget, { GtkContainer *container; gboolean result; - - container = GTK_CONTAINER (widget); + + container = GTK_CONTAINER (widget); if (!gtk_widget_is_focus (widget) && gtk_container_get_focus_child (container) == NULL) { - gtk_widget_grab_focus (widget); - return TRUE; + if (gtk_widget_get_can_focus (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + + return FALSE; } else { + gboolean can_focus; /* * Unset CAN_FOCUS flag so that gtk_container_focus() allows * children to get the focus */ + can_focus = gtk_widget_get_can_focus (widget); gtk_widget_set_can_focus (widget, FALSE); result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction); - gtk_widget_set_can_focus (widget, TRUE); + gtk_widget_set_can_focus (widget, can_focus); return result; } @@ -4954,6 +5410,18 @@ cursor_blinks (GtkTextView *text_view) return FALSE; } +static gboolean +get_middle_click_paste (GtkTextView *text_view) +{ + GtkSettings *settings; + gboolean paste; + + settings = gtk_widget_get_settings (GTK_WIDGET (text_view)); + g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL); + + return paste; +} + static gint get_cursor_time (GtkTextView *text_view) { @@ -5177,6 +5645,7 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view, if (!priv->cursor_visible) { GtkScrollStep scroll_step; + gdouble old_xpos, old_ypos; switch (step) { @@ -5211,14 +5680,15 @@ gtk_text_view_move_cursor_internal (GtkTextView *text_view, break; } - if (!gtk_text_view_move_viewport (text_view, scroll_step, count)) + old_xpos = priv->xoffset; + old_ypos = priv->yoffset; + gtk_text_view_move_viewport (text_view, scroll_step, count); + if ((old_xpos == priv->xoffset && old_ypos == priv->yoffset) && + leave_direction != -1 && + !gtk_widget_keynav_failed (GTK_WIDGET (text_view), + leave_direction)) { - if (leave_direction != -1 && - !gtk_widget_keynav_failed (GTK_WIDGET (text_view), - leave_direction)) - { - g_signal_emit_by_name (text_view, "move-focus", leave_direction); - } + g_signal_emit_by_name (text_view, "move-focus", leave_direction); } return; @@ -5413,7 +5883,7 @@ gtk_text_view_move_cursor (GtkTextView *text_view, gtk_text_view_move_cursor_internal (text_view, step, count, extend_selection); } -static gboolean +static void gtk_text_view_move_viewport (GtkTextView *text_view, GtkScrollStep step, gint count) @@ -5442,22 +5912,22 @@ gtk_text_view_move_viewport (GtkTextView *text_view, { case GTK_SCROLL_STEPS: case GTK_SCROLL_HORIZONTAL_STEPS: - increment = adjustment->step_increment; + increment = gtk_adjustment_get_step_increment (adjustment); break; case GTK_SCROLL_PAGES: case GTK_SCROLL_HORIZONTAL_PAGES: - increment = adjustment->page_increment; + increment = gtk_adjustment_get_page_increment (adjustment); break; case GTK_SCROLL_ENDS: case GTK_SCROLL_HORIZONTAL_ENDS: - increment = adjustment->upper - adjustment->lower; + increment = gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment); break; default: increment = 0.0; break; } - return set_adjustment_clamped (adjustment, adjustment->value + count * increment); + gtk_adjustment_set_value (adjustment, gtk_adjustment_get_value (adjustment) + count * increment); } static void @@ -5477,7 +5947,7 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, gboolean extend_selection) { GtkTextViewPrivate *priv; - GtkAdjustment *adj; + GtkAdjustment *adjustment; gint cursor_x_pos, cursor_y_pos; GtkTextMark *insert_mark; GtkTextIter old_insert; @@ -5491,7 +5961,7 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, g_return_val_if_fail (priv->vadjustment != NULL, FALSE); - adj = priv->vadjustment; + adjustment = priv->vadjustment; insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view)); @@ -5512,13 +5982,13 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, if (count < 0) { gtk_text_view_get_first_para_iter (text_view, &anchor); - y0 = adj->page_size; - y1 = adj->page_size + count * adj->page_increment; + y0 = gtk_adjustment_get_page_size (adjustment); + y1 = gtk_adjustment_get_page_size (adjustment) + count * gtk_adjustment_get_page_increment (adjustment); } else { gtk_text_view_get_first_para_iter (text_view, &anchor); - y0 = count * adj->page_increment + adj->page_size; + y0 = count * gtk_adjustment_get_page_increment (adjustment) + gtk_adjustment_get_page_size (adjustment); y1 = 0; } @@ -5527,13 +5997,13 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, new_insert = old_insert; - if (count < 0 && adj->value <= (adj->lower + 1e-12)) + if (count < 0 && gtk_adjustment_get_value (adjustment) <= (gtk_adjustment_get_lower (adjustment) + 1e-12)) { /* already at top, just be sure we are at offset 0 */ gtk_text_buffer_get_start_iter (get_buffer (text_view), &new_insert); move_cursor (text_view, &new_insert, extend_selection); } - else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12)) + else if (count > 0 && gtk_adjustment_get_value (adjustment) >= (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_page_size (adjustment) - 1e-12)) { /* already at bottom, just be sure we are at the end */ gtk_text_buffer_get_end_iter (get_buffer (text_view), &new_insert); @@ -5543,13 +6013,13 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, { gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos); - oldval = adj->value; - newval = adj->value; + oldval = gtk_adjustment_get_value (adjustment); + newval = gtk_adjustment_get_value (adjustment); - newval += count * adj->page_increment; + newval += count * gtk_adjustment_get_page_increment (adjustment); - set_adjustment_clamped (adj, newval); - cursor_y_pos += adj->value - oldval; + gtk_adjustment_set_value (adjustment, newval); + cursor_y_pos += gtk_adjustment_get_value (adjustment) - oldval; gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos); clamp_iter_onscreen (text_view, &new_insert); @@ -5573,7 +6043,7 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view, gboolean extend_selection) { GtkTextViewPrivate *priv; - GtkAdjustment *adj; + GtkAdjustment *adjustment; gint cursor_x_pos, cursor_y_pos; GtkTextMark *insert_mark; GtkTextIter old_insert; @@ -5586,7 +6056,7 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view, g_return_val_if_fail (priv->hadjustment != NULL, FALSE); - adj = priv->hadjustment; + adjustment = priv->hadjustment; insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view)); @@ -5610,13 +6080,13 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view, new_insert = old_insert; - if (count < 0 && adj->value <= (adj->lower + 1e-12)) + if (count < 0 && gtk_adjustment_get_value (adjustment) <= (gtk_adjustment_get_lower (adjustment) + 1e-12)) { /* already at far left, just be sure we are at offset 0 */ gtk_text_iter_set_line_offset (&new_insert, 0); move_cursor (text_view, &new_insert, extend_selection); } - else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12)) + else if (count > 0 && gtk_adjustment_get_value (adjustment) >= (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_page_size (adjustment) - 1e-12)) { /* already at far right, just be sure we are at the end */ if (!gtk_text_iter_ends_line (&new_insert)) @@ -5627,13 +6097,13 @@ gtk_text_view_scroll_hpages (GtkTextView *text_view, { gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos); - oldval = adj->value; - newval = adj->value; + oldval = gtk_adjustment_get_value (adjustment); + newval = gtk_adjustment_get_value (adjustment); - newval += count * adj->page_increment; + newval += count * gtk_adjustment_get_page_increment (adjustment); - set_adjustment_clamped (adj, newval); - cursor_x_pos += adj->value - oldval; + gtk_adjustment_set_value (adjustment, newval); + cursor_x_pos += gtk_adjustment_get_value (adjustment) - oldval; gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos); clamp_iter_onscreen (text_view, &new_insert); @@ -5889,6 +6359,7 @@ gtk_text_view_cut_clipboard (GtkTextView *text_view) DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (get_buffer (text_view))); + gtk_text_view_selection_bubble_popup_unset (text_view); } static void @@ -5934,6 +6405,17 @@ gtk_text_view_paste_done_handler (GtkTextBuffer *buffer, priv->scroll_after_paste = TRUE; } +static void +gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer, + gpointer data) +{ + GtkTextView *text_view = data; + GtkTextViewPrivate *priv = text_view->priv; + + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (priv->text_handle)); +} + static void gtk_text_view_toggle_overwrite (GtkTextView *text_view) { @@ -6089,24 +6571,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); @@ -6131,12 +6614,12 @@ selection_scan_timeout (gpointer data) #define LOWER_OFFSET_ANCHOR 0.2 static gboolean -check_scroll (gdouble offset, GtkAdjustment *adj) +check_scroll (gdouble offset, GtkAdjustment *adjustment) { if ((offset > UPPER_OFFSET_ANCHOR && - adj->value + adj->page_size < adj->upper) || + gtk_adjustment_get_value (adjustment) + gtk_adjustment_get_page_size (adjustment) < gtk_adjustment_get_upper (adjustment)) || (offset < LOWER_OFFSET_ANCHOR && - adj->value > adj->lower)) + gtk_adjustment_get_value (adjustment) > gtk_adjustment_get_lower (adjustment))) return TRUE; return FALSE; @@ -6291,16 +6774,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 { @@ -6324,7 +6813,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)); } @@ -6339,6 +6828,9 @@ selection_motion_event_handler (GtkTextView *text_view, text_view->priv->scroll_timeout = gdk_threads_add_timeout (50, selection_scan_timeout, text_view); + if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN) + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION); + return TRUE; } @@ -6380,7 +6872,9 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view, orig_start = ins; orig_end = bound; - if (button->state & GDK_SHIFT_MASK) + if (button->state & + gtk_widget_get_modifier_mask (GTK_WIDGET (text_view), + GDK_MODIFIER_INTENT_EXTEND_SELECTION)) { /* Extend selection */ GtkTextIter old_ins, old_bound; @@ -6464,16 +6958,35 @@ gtk_text_view_end_selection_drag (GtkTextView *text_view) static void gtk_text_view_set_attributes_from_style (GtkTextView *text_view, - GtkTextAttributes *values, - GtkStyle *style) + GtkTextAttributes *values) { - values->appearance.bg_color = style->base[GTK_STATE_NORMAL]; - values->appearance.fg_color = style->text[GTK_STATE_NORMAL]; + GtkStyleContext *context; + GdkRGBA bg_color, fg_color; + GtkStateFlags state; + + context = gtk_widget_get_style_context (GTK_WIDGET (text_view)); + state = gtk_widget_get_state_flags (GTK_WIDGET (text_view)); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + + gtk_style_context_get_background_color (context, state, &bg_color); + gtk_style_context_get_color (context, state, &fg_color); + + values->appearance.bg_color.red = CLAMP (bg_color.red * 65535. + 0.5, 0, 65535); + values->appearance.bg_color.green = CLAMP (bg_color.green * 65535. + 0.5, 0, 65535); + values->appearance.bg_color.blue = CLAMP (bg_color.blue * 65535. + 0.5, 0, 65535); + + values->appearance.fg_color.red = CLAMP (fg_color.red * 65535. + 0.5, 0, 65535); + values->appearance.fg_color.green = CLAMP (fg_color.green * 65535. + 0.5, 0, 65535); + values->appearance.fg_color.blue = CLAMP (fg_color.blue * 65535. + 0.5, 0, 65535); if (values->font) pango_font_description_free (values->font); - values->font = pango_font_description_copy (style->font_desc); + gtk_style_context_get (context, state, "font", &values->font, NULL); + + gtk_style_context_restore (context); } static void @@ -6567,10 +7080,7 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) style = gtk_text_attributes_new (); - gtk_widget_ensure_style (widget); - gtk_text_view_set_attributes_from_style (text_view, - style, - gtk_widget_get_style (widget)); + gtk_text_view_set_attributes_from_style (text_view, style); style->pixels_above_lines = priv->pixels_above_lines; style->pixels_below_lines = priv->pixels_below_lines; @@ -6604,8 +7114,6 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) tmp_list = g_slist_next (tmp_list); } - - gtk_text_view_invalidate (text_view); } } @@ -6718,7 +7226,7 @@ gtk_text_view_reset_im_context (GtkTextView *text_view) * gtk_foo_bar_key_press_event (GtkWidget *widget, * GdkEventKey *event) * { - * if ((key->keyval == GDK_Return || key->keyval == GDK_KP_Enter)) + * if ((key->keyval == GDK_KEY_Return || key->keyval == GDK_KEY_KP_Enter)) * { * if (gtk_text_view_im_context_filter_keypress (GTK_TEXT_VIEW (view), event)) * return TRUE; @@ -6836,7 +7344,7 @@ gtk_text_view_drag_data_get (GtkWidget *widget, { /* Extract the selected text */ str = gtk_text_buffer_serialize (buffer, buffer, - selection_data->target, + gtk_selection_data_get_target (selection_data), &start, &end, &len); } @@ -6844,7 +7352,7 @@ gtk_text_view_drag_data_get (GtkWidget *widget, if (str) { gtk_selection_data_set (selection_data, - selection_data->target, + gtk_selection_data_get_target (selection_data), 8, /* bytes */ (guchar *) str, len); g_free (str); @@ -6957,7 +7465,7 @@ gtk_text_view_drag_motion (GtkWidget *widget, { GtkWidget *source_widget; - suggested_action = context->suggested_action; + suggested_action = gdk_drag_context_get_suggested_action (context); source_widget = gtk_drag_get_source_widget (context); @@ -6966,7 +7474,7 @@ gtk_text_view_drag_motion (GtkWidget *widget, /* Default to MOVE, unless the user has * pressed ctrl or alt to affect available actions */ - if ((context->actions & GDK_ACTION_MOVE) != 0) + if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0) suggested_action = GDK_ACTION_MOVE; } } @@ -7100,10 +7608,10 @@ gtk_text_view_drag_data_received (GtkWidget *widget, GtkTextIter start, end; gboolean copy_tags = TRUE; - if (selection_data->length != sizeof (src_buffer)) + if (gtk_selection_data_get_length (selection_data) != sizeof (src_buffer)) return; - memcpy (&src_buffer, selection_data->data, sizeof (src_buffer)); + memcpy (&src_buffer, gtk_selection_data_get_data (selection_data), sizeof (src_buffer)); if (src_buffer == NULL) return; @@ -7123,7 +7631,7 @@ gtk_text_view_drag_data_received (GtkWidget *widget, atoms = gtk_text_buffer_get_deserialize_formats (buffer, &n_atoms); - for (list = context->targets; list; list = g_list_next (list)) + for (list = gdk_drag_context_list_targets (context); list; list = g_list_next (list)) { gint i; @@ -7167,17 +7675,17 @@ gtk_text_view_drag_data_received (GtkWidget *widget, } } } - else if (selection_data->length > 0 && + else if (gtk_selection_data_get_length (selection_data) > 0 && info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT) { gboolean retval; GError *error = NULL; retval = gtk_text_buffer_deserialize (buffer, buffer, - selection_data->target, + gtk_selection_data_get_target (selection_data), &drop_point, - (guint8 *) selection_data->data, - selection_data->length, + (guint8 *) gtk_selection_data_get_data (selection_data), + gtk_selection_data_get_length (selection_data), &error); if (!retval) @@ -7191,7 +7699,7 @@ gtk_text_view_drag_data_received (GtkWidget *widget, done: gtk_drag_finish (context, success, - success && context->action == GDK_ACTION_MOVE, + success && gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE, time); if (success) @@ -7262,6 +7770,8 @@ gtk_text_view_set_hadjustment (GtkTextView *text_view, * Returns: (transfer none): pointer to the vertical #GtkAdjustment * * Since: 2.22 + * + * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment() **/ GtkAdjustment* gtk_text_view_get_vadjustment (GtkTextView *text_view) @@ -7394,6 +7904,8 @@ adjust_allocation_recurse (GtkWidget *widget, * into widget->allocation if the widget is not realized. * FIXME someone figure out why this was. */ + gtk_widget_get_allocation (widget, &allocation); + if (!gtk_widget_get_realized (widget)) { if (gtk_widget_get_visible (widget)) @@ -7440,9 +7952,9 @@ adjust_allocation (GtkWidget *widget, adjust_allocation_recurse (widget, &scroll_data); } - + static void -gtk_text_view_value_changed (GtkAdjustment *adj, +gtk_text_view_value_changed (GtkAdjustment *adjustment, GtkTextView *text_view) { GtkTextViewPrivate *priv; @@ -7453,20 +7965,20 @@ gtk_text_view_value_changed (GtkAdjustment *adj, priv = text_view->priv; - /* Note that we oddly call this function with adj == NULL + /* Note that we oddly call this function with adjustment == NULL * sometimes */ priv->onscreen_validated = FALSE; DV(g_print(">Scroll offset changed %s/%g, onscreen_validated = FALSE ("G_STRLOC")\n", - adj == priv->hadjustment ? "hadj" : adj == priv->vadjustment ? "vadj" : "none", - adj ? adj->value : 0.0)); + adjustment == priv->hadjustment ? "hadjustment" : adjustment == priv->vadjustment ? "vadjustment" : "none", + adjustment ? gtk_adjustment_get_value (adjustment) : 0.0)); - if (adj == priv->hadjustment) + if (adjustment == priv->hadjustment) { - dx = priv->xoffset - (gint)adj->value; - priv->xoffset = adj->value; + dx = priv->xoffset - (gint)gtk_adjustment_get_value (adjustment); + priv->xoffset = gtk_adjustment_get_value (adjustment); /* If the change is due to a size change we need * to invalidate the entire text window because there might be @@ -7480,18 +7992,18 @@ gtk_text_view_value_changed (GtkAdjustment *adj, priv->width_changed = FALSE; } } - else if (adj == priv->vadjustment) + else if (adjustment == priv->vadjustment) { - dy = priv->yoffset - (gint)adj->value; - priv->yoffset = adj->value; + dy = priv->yoffset - (gint)gtk_adjustment_get_value (adjustment); + priv->yoffset = gtk_adjustment_get_value (adjustment); if (priv->layout) { - gtk_text_layout_get_line_at_y (priv->layout, &iter, adj->value, &line_top); + gtk_text_layout_get_line_at_y (priv->layout, &iter, gtk_adjustment_get_value (adjustment), &line_top); gtk_text_buffer_move_mark (get_buffer (text_view), priv->first_para_mark, &iter); - priv->first_para_pixels = adj->value - line_top; + priv->first_para_pixels = gtk_adjustment_get_value (adjustment) - line_top; } } @@ -7559,26 +8071,6 @@ gtk_text_view_value_changed (GtkAdjustment *adj, */ gtk_text_view_validate_onscreen (text_view); - /* process exposes */ - if (gtk_widget_get_realized (GTK_WIDGET (text_view))) - { - DV (g_print ("Processing updates (%s)\n", G_STRLOC)); - - if (priv->left_window) - gdk_window_process_updates (priv->left_window->bin_window, TRUE); - - if (priv->right_window) - gdk_window_process_updates (priv->right_window->bin_window, TRUE); - - if (priv->top_window) - gdk_window_process_updates (priv->top_window->bin_window, TRUE); - - if (priv->bottom_window) - gdk_window_process_updates (priv->bottom_window->bin_window, TRUE); - - gdk_window_process_updates (priv->text_window->bin_window, TRUE); - } - /* If this got installed, get rid of it, it's just a waste of time. */ if (priv->first_validate_idle != 0) { @@ -7590,7 +8082,10 @@ gtk_text_view_value_changed (GtkAdjustment *adj, * 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")); } @@ -7766,7 +8261,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 @@ -7812,18 +8311,6 @@ gtk_text_view_target_list_notify (GtkTextBuffer *buffer, gtk_target_list_unref (view_list); } -static void -gtk_text_view_get_cursor_location (GtkTextView *text_view, - GdkRectangle *pos) -{ - GtkTextIter insert; - - gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert, - gtk_text_buffer_get_insert (get_buffer (text_view))); - - gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert, pos, NULL); -} - static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view, GtkTextIter *cursor, @@ -7874,7 +8361,7 @@ gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view, return; if (x == -1 || y == -1) - gtk_text_view_get_cursor_location (text_view, &pos); + gtk_text_view_get_cursor_locations (text_view, NULL, &pos, NULL); text_view->priv->virtual_cursor_x = (x == -1) ? pos.x : x; text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y; @@ -8023,7 +8510,7 @@ popup_position_func (GtkMenu *menu, monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y); gtk_menu_set_monitor (menu, monitor_num); - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width)); *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height)); @@ -8036,6 +8523,7 @@ typedef struct GtkTextView *text_view; gint button; guint time; + GdkDevice *device; } PopupInfo; static gboolean @@ -8136,6 +8624,8 @@ popup_targets_received (GtkClipboard *clipboard, gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem); menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL); + gtk_widget_set_sensitive (menuitem, + gtk_text_buffer_get_char_count (priv->buffer) > 0); g_signal_connect (menuitem, "activate", G_CALLBACK (select_all_cb), text_view); gtk_widget_show (menuitem); @@ -8187,9 +8677,9 @@ popup_targets_received (GtkClipboard *clipboard, 0, priv->popup_menu); - if (info->button) - gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL, - NULL, NULL, + if (info->device) + gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu), + info->device, NULL, NULL, NULL, NULL, NULL, info->button, info->time); else { @@ -8220,11 +8710,13 @@ gtk_text_view_do_popup (GtkTextView *text_view, { info->button = event->button; info->time = event->time; + info->device = event->device; } else { info->button = 0; info->time = gtk_get_current_event_time (); + info->device = NULL; } gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view), @@ -8241,6 +8733,114 @@ gtk_text_view_popup_menu (GtkWidget *widget) return TRUE; } +static void +gtk_text_view_get_selection_rect (GtkTextView *text_view, + cairo_rectangle_int_t *rect) +{ + cairo_rectangle_int_t rect_cursor, rect_bound; + GtkTextIter cursor, bound; + GtkTextBuffer *buffer; + gint x1, y1, x2, y2; + + 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)); + + gtk_text_view_get_cursor_locations (text_view, &cursor, &rect_cursor, NULL); + gtk_text_view_get_cursor_locations (text_view, &bound, &rect_bound, NULL); + + x1 = MIN (rect_cursor.x, rect_bound.x); + x2 = MAX (rect_cursor.x, rect_bound.x); + y1 = MIN (rect_cursor.y, rect_bound.y); + y2 = MAX (rect_cursor.y + rect_cursor.height, rect_bound.y + rect_bound.height); + + rect->x = x1; + rect->y = y1; + rect->width = x2 - x1; + rect->height = y2 - y1; +} + +static gboolean +gtk_text_view_selection_bubble_popup_cb (gpointer user_data) +{ + GtkTextView *text_view = user_data; + GtkTextViewPrivate *priv = text_view->priv; + cairo_rectangle_int_t rect; + gboolean has_selection; + GdkWindow *window; + + has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), + NULL, NULL); + if (!priv->editable && !has_selection) + { + priv->selection_bubble_timeout_id = 0; + return FALSE; + } + + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + window = gtk_widget_get_window (GTK_WIDGET (text_view)); + priv->selection_bubble = gtk_selection_window_new (); + gtk_selection_window_set_editable (GTK_SELECTION_WINDOW (priv->selection_bubble), + priv->editable); + gtk_selection_window_set_has_selection (GTK_SELECTION_WINDOW (priv->selection_bubble), + has_selection); + + g_signal_connect_swapped (priv->selection_bubble, "cut", + G_CALLBACK (gtk_text_view_cut_clipboard), + text_view); + g_signal_connect_swapped (priv->selection_bubble, "copy", + G_CALLBACK (gtk_text_view_copy_clipboard), + text_view); + g_signal_connect_swapped (priv->selection_bubble, "paste", + G_CALLBACK (gtk_text_view_paste_clipboard), + text_view); + + gtk_text_view_get_selection_rect (text_view, &rect); + rect.x -= priv->xoffset; + rect.y -= priv->yoffset; + gtk_bubble_window_popup (GTK_BUBBLE_WINDOW (priv->selection_bubble), + window, &rect, GTK_POS_TOP); + + priv->selection_bubble_timeout_id = 0; + return FALSE; +} + +static void +gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + + priv = text_view->priv; + + if (priv->selection_bubble) + gtk_bubble_window_popdown (GTK_BUBBLE_WINDOW (priv->selection_bubble)); + + if (priv->selection_bubble_timeout_id) + { + g_source_remove (priv->selection_bubble_timeout_id); + priv->selection_bubble_timeout_id = 0; + } +} + +static void +gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + + priv = text_view->priv; + + if (priv->selection_bubble_timeout_id) + g_source_remove (priv->selection_bubble_timeout_id); + + priv->selection_bubble_timeout_id = + gdk_threads_add_timeout_seconds (1, gtk_text_view_selection_bubble_popup_cb, + text_view); +} + /* Child GdkWindows */ @@ -8281,10 +8881,13 @@ static void text_window_realize (GtkTextWindow *win, GtkWidget *widget) { + GtkStyleContext *context; + GtkStateFlags state; GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; GdkCursor *cursor; + GdkRGBA color; attributes.window_type = GDK_WINDOW_CHILD; attributes.x = win->allocation.x; @@ -8303,7 +8906,7 @@ text_window_realize (GtkTextWindow *win, &attributes, attributes_mask); gdk_window_show (win->window); - gdk_window_set_user_data (win->window, win->widget); + gtk_widget_register_window (win->widget, win->window); gdk_window_lower (win->window); attributes.x = 0; @@ -8312,6 +8915,7 @@ text_window_realize (GtkTextWindow *win, attributes.height = win->allocation.height; attributes.event_mask = (GDK_EXPOSURE_MASK | GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK | GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | @@ -8324,7 +8928,10 @@ text_window_realize (GtkTextWindow *win, attributes_mask); gdk_window_show (win->bin_window); - gdk_window_set_user_data (win->bin_window, win->widget); + gtk_widget_register_window (win->widget, win->bin_window); + + context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); if (win->type == GTK_TEXT_WINDOW_TEXT) { @@ -8334,20 +8941,24 @@ text_window_realize (GtkTextWindow *win, cursor = gdk_cursor_new_for_display (gdk_window_get_display (window), GDK_XTERM); gdk_window_set_cursor (win->bin_window, cursor); - gdk_cursor_unref (cursor); + g_object_unref (cursor); } gtk_im_context_set_client_window (GTK_TEXT_VIEW (widget)->priv->im_context, win->window); + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + + gtk_style_context_get_background_color (context, state, &color); + gdk_window_set_background_rgba (win->bin_window, &color); - gdk_window_set_background (win->bin_window, - >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]); + gtk_style_context_restore (context); } else { - gdk_window_set_background (win->bin_window, - >k_widget_get_style (widget)->bg[gtk_widget_get_state (widget)]); + gtk_style_context_get_background_color (context, state, &color); + gdk_window_set_background_rgba (win->bin_window, &color); } g_object_set_qdata (G_OBJECT (win->window), @@ -8368,8 +8979,8 @@ text_window_unrealize (GtkTextWindow *win) NULL); } - gdk_window_set_user_data (win->window, NULL); - gdk_window_set_user_data (win->bin_window, NULL); + gtk_widget_unregister_window (win->widget, win->window); + gtk_widget_unregister_window (win->widget, win->bin_window); gdk_window_destroy (win->bin_window); gdk_window_destroy (win->window); win->window = NULL; @@ -8486,7 +9097,7 @@ text_window_invalidate_cursors (GtkTextWindow *win) gtk_text_layout_get_cursor_locations (priv->layout, &iter, &strong, &weak); - /* cursor width calculation as in gtkstyle.c:draw_insertion_cursor(), + /* cursor width calculation as in gtkstylecontext.c:draw_insertion_cursor(), * ignoring the text direction be exposing both sides of the cursor */ @@ -9269,7 +9880,9 @@ gtk_text_view_add_child_at_anchor (GtkTextView *text_view, * @ypos: Y position of child in window coordinates * * Adds a child at fixed coordinates in one of the text widget's - * windows. The window must have nonzero size (see + * windows. + * + * The window must have nonzero size (see * gtk_text_view_set_border_window_size()). Note that the child * coordinates are given relative to the #GdkWindow in question, and * that these coordinates have no sane relationship to scrolling. When @@ -9279,12 +9892,8 @@ gtk_text_view_add_child_at_anchor (GtkTextView *text_view, * text window), you'll need to compute the child's correct position * in buffer coordinates any time scrolling occurs or buffer changes * occur, and then call gtk_text_view_move_child() to update the - * child's position. Unfortunately there's no good way to detect that - * scrolling has occurred, using the current API; a possible hack - * would be to update all child positions when the scroll adjustments - * change or the text buffer changes. See bug 64518 on - * bugzilla.gnome.org for status of fixing this issue. - **/ + * child's position. + */ void gtk_text_view_add_child_in_window (GtkTextView *text_view, GtkWidget *child, @@ -9516,3 +10125,106 @@ gtk_text_view_move_visually (GtkTextView *text_view, return gtk_text_layout_move_iter_visually (text_view->priv->layout, iter, count); } + +/** + * gtk_text_view_set_input_purpose: + * @text_view: a #GtkTextView + * @purpose: the purpose + * + * Sets the #GtkTextView:input-purpose property which + * can be used by on-screen keyboards and other input + * methods to adjust their behaviour. + * + * Since: 3.6 + */ + +void +gtk_text_view_set_input_purpose (GtkTextView *text_view, + GtkInputPurpose purpose) + +{ + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + + if (gtk_text_view_get_input_purpose (text_view) != purpose) + { + g_object_set (G_OBJECT (text_view->priv->im_context), + "input-purpose", purpose, + NULL); + + g_object_notify (G_OBJECT (text_view), "input-purpose"); + } +} + +/** + * gtk_text_view_get_input_purpose: + * @text_view: a #GtkTextView + * + * Gets the value of the #GtkTextView:input-purpose property. + * + * Since: 3.6 + */ + +GtkInputPurpose +gtk_text_view_get_input_purpose (GtkTextView *text_view) +{ + GtkInputPurpose purpose; + + g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_PURPOSE_FREE_FORM); + + g_object_get (G_OBJECT (text_view->priv->im_context), + "input-purpose", &purpose, + NULL); + + return purpose; +} + +/** + * gtk_text_view_set_input_hints: + * @text_view: a #GtkTextView + * @hints: the hints + * + * Sets the #GtkTextView:input-hints property, which + * allows input methods to fine-tune their behaviour. + * + * Since: 3.6 + */ + +void +gtk_text_view_set_input_hints (GtkTextView *text_view, + GtkInputHints hints) + +{ + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + + if (gtk_text_view_get_input_hints (text_view) != hints) + { + g_object_set (G_OBJECT (text_view->priv->im_context), + "input-hints", hints, + NULL); + + g_object_notify (G_OBJECT (text_view), "input-hints"); + } +} + +/** + * gtk_text_view_get_input_hints: + * @text_view: a #GtkTextView + * + * Gets the value of the #GtkTextView:input-hints property. + * + * Since: 3.6 + */ + +GtkInputHints +gtk_text_view_get_input_hints (GtkTextView *text_view) +{ + GtkInputHints hints; + + g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_HINT_NONE); + + g_object_get (G_OBJECT (text_view->priv->im_context), + "input-hints", &hints, + NULL); + + return hints; +}