X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextview.c;h=b378265c7bfb6a703ef79ebb655f91b47a6c5fe1;hb=437de1aa07a5d29138f6d9451234c8f478d1ddbf;hp=d45b1891ab3f9345aa61cf593da842babeee64ba;hpb=2145a4ef10fef74e42c4f893097021d6b1a7d5e9;p=~andy%2Fgtk diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index d45b1891a..b378265c7 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -32,17 +32,19 @@ #include "gtkimagemenuitem.h" #include "gtkintl.h" #include "gtkmain.h" +#include "gtkmarshalers.h" #include "gtkmenu.h" #include "gtkmenuitem.h" #include "gtkseparatormenuitem.h" #include "gtksettings.h" -#include "gtksignal.h" #include "gtkstock.h" #include "gtktextdisplay.h" #include "gtktextview.h" #include "gtkimmulticontext.h" #include "gdk/gdkkeysyms.h" -#include +#include "gtksizegroup.h" /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */ +#include "gtktextutil.h" +#include "gtkwindow.h" /* How scrolling, validation, exposes, etc. work. * @@ -79,9 +81,6 @@ * */ -#define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window) -#define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window) - #if 0 #define DEBUG_VALIDATION_AND_SCROLLING #endif @@ -92,6 +91,9 @@ #define DV(x) #endif +#define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window) +#define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window) + struct _GtkTextPendingScroll { GtkTextMark *mark; @@ -106,6 +108,7 @@ enum SET_SCROLL_ADJUSTMENTS, POPULATE_POPUP, MOVE_CURSOR, + PAGE_HORIZONTALLY, SET_ANCHOR, INSERT_AT_CURSOR, DELETE_FROM_CURSOR, @@ -113,6 +116,7 @@ enum COPY_CLIPBOARD, PASTE_CLIPBOARD, TOGGLE_OVERWRITE, + MOVE_FOCUS, LAST_SIGNAL }; @@ -175,6 +179,9 @@ static gint gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *expose); static void gtk_text_view_draw_focus (GtkWidget *widget); static void gtk_text_view_grab_focus (GtkWidget *widget); +static gboolean gtk_text_view_focus (GtkWidget *widget, + GtkDirectionType direction); + /* Source side drag signals */ static void gtk_text_view_drag_begin (GtkWidget *widget, @@ -214,15 +221,22 @@ static void gtk_text_view_drag_data_received (GtkWidget *widget, static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view, GtkAdjustment *hadj, GtkAdjustment *vadj); -static void gtk_text_view_popup_menu (GtkWidget *widget); - -static void gtk_text_view_move_cursor (GtkTextView *text_view, - GtkMovementStep step, - gint count, - gboolean extend_selection); +static gboolean gtk_text_view_popup_menu (GtkWidget *widget); + +static void gtk_text_view_move_cursor (GtkTextView *text_view, + GtkMovementStep step, + gint count, + gboolean extend_selection); +static void gtk_text_view_page_horizontally (GtkTextView *text_view, + gint count, + gboolean extend_selection); static void gtk_text_view_set_anchor (GtkTextView *text_view); static void gtk_text_view_scroll_pages (GtkTextView *text_view, - gint count); + gint count, + gboolean extend_selection); +static void gtk_text_view_scroll_hpages (GtkTextView *text_view, + gint count, + gboolean extend_selection); static void gtk_text_view_insert_at_cursor (GtkTextView *text_view, const gchar *str); static void gtk_text_view_delete_from_cursor (GtkTextView *text_view, @@ -232,6 +246,8 @@ static void gtk_text_view_cut_clipboard (GtkTextView *text_view); static void gtk_text_view_copy_clipboard (GtkTextView *text_view); static void gtk_text_view_paste_clipboard (GtkTextView *text_view); static void gtk_text_view_toggle_overwrite (GtkTextView *text_view); +static void gtk_text_view_move_focus (GtkTextView *text_view, + GtkDirectionType direction_type); static void gtk_text_view_unselect (GtkTextView *text_view); static void gtk_text_view_validate_onscreen (GtkTextView *text_view); @@ -257,13 +273,21 @@ static void gtk_text_view_check_cursor_blink (GtkTextView *text_v static void gtk_text_view_pend_cursor_blink (GtkTextView *text_view); static void gtk_text_view_stop_cursor_blink (GtkTextView *text_view); -static void gtk_text_view_value_changed (GtkAdjustment *adj, - GtkTextView *view); -static void gtk_text_view_commit_handler (GtkIMContext *context, - const gchar *str, - GtkTextView *text_view); -static void gtk_text_view_preedit_changed_handler (GtkIMContext *context, - GtkTextView *text_view); +static void gtk_text_view_value_changed (GtkAdjustment *adj, + GtkTextView *view); +static void gtk_text_view_commit_handler (GtkIMContext *context, + const gchar *str, + GtkTextView *text_view); +static void gtk_text_view_commit_text (GtkTextView *text_view, + const gchar *text); +static void gtk_text_view_preedit_changed_handler (GtkIMContext *context, + GtkTextView *text_view); +static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext *context, + GtkTextView *text_view); +static gboolean gtk_text_view_delete_surrounding_handler (GtkIMContext *context, + gint offset, + gint n_chars, + GtkTextView *text_view); static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, const GtkTextIter *location, @@ -289,9 +313,10 @@ static void gtk_text_view_queue_scroll (GtkTextView *text_view, gdouble xalign, gdouble yalign); -static gboolean gtk_text_view_flush_scroll (GtkTextView *text_view); -static void gtk_text_view_update_adjustments (GtkTextView *text_view); -static void gtk_text_view_invalidate (GtkTextView *text_view); +static gboolean gtk_text_view_flush_scroll (GtkTextView *text_view); +static void gtk_text_view_update_adjustments (GtkTextView *text_view); +static void gtk_text_view_invalidate (GtkTextView *text_view); +static void gtk_text_view_flush_first_validate (GtkTextView *text_view); static void gtk_text_view_update_im_spot_location (GtkTextView *text_view); @@ -302,7 +327,7 @@ static void gtk_text_view_remove (GtkContainer *container, GtkWidget *child); static void gtk_text_view_forall (GtkContainer *container, gboolean include_internals, - GtkCallback callback, + GtkCallback callback, gpointer callback_data); /* FIXME probably need the focus methods. */ @@ -363,8 +388,6 @@ static void text_window_invalidate_rect (GtkTextWindow *win, static gint text_window_get_width (GtkTextWindow *win); static gint text_window_get_height (GtkTextWindow *win); -static void text_window_get_allocation (GtkTextWindow *win, - GdkRectangle *rect); enum @@ -376,39 +399,40 @@ enum TARGET_TEXT_BUFFER_CONTENTS }; -static GtkTargetEntry target_table[] = { +static const GtkTargetEntry target_table[] = { { "GTK_TEXT_BUFFER_CONTENTS", GTK_TARGET_SAME_APP, TARGET_TEXT_BUFFER_CONTENTS }, { "UTF8_STRING", 0, TARGET_UTF8_STRING }, { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, { "TEXT", 0, TARGET_TEXT }, - { "text/plain", 0, TARGET_STRING }, { "STRING", 0, TARGET_STRING } }; static GtkContainerClass *parent_class = NULL; static guint signals[LAST_SIGNAL] = { 0 }; -GtkType +GType gtk_text_view_get_type (void) { - static GtkType our_type = 0; + static GType our_type = 0; if (our_type == 0) { - static const GtkTypeInfo our_info = + static const GTypeInfo our_info = { - "GtkTextView", - sizeof (GtkTextView), - sizeof (GtkTextViewClass), - (GtkClassInitFunc) gtk_text_view_class_init, - (GtkObjectInitFunc) gtk_text_view_init, - /* reserved_1 */ NULL, - /* reserved_2 */ NULL, - (GtkClassInitFunc) NULL + sizeof (GtkTextViewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_text_view_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkTextView), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_text_view_init, }; - our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info); + our_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTextView", + &our_info, 0); } return our_type; @@ -425,16 +449,16 @@ add_move_binding (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move_cursor", 3, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count, - GTK_TYPE_BOOL, FALSE); + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, FALSE); /* Selection-extending version */ gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, "move_cursor", 3, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count, - GTK_TYPE_BOOL, TRUE); + G_TYPE_ENUM, step, + G_TYPE_INT, count, + G_TYPE_BOOLEAN, TRUE); } static void @@ -446,7 +470,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkBindingSet *binding_set; - parent_class = gtk_type_class (GTK_TYPE_CONTAINER); + parent_class = g_type_class_peek_parent (klass); /* Default handlers and virtual methods */ @@ -472,6 +496,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) widget_class->motion_notify_event = gtk_text_view_motion_event; widget_class->expose_event = gtk_text_view_expose_event; widget_class->grab_focus = gtk_text_view_grab_focus; + widget_class->focus = gtk_text_view_focus; widget_class->drag_begin = gtk_text_view_drag_begin; widget_class->drag_end = gtk_text_view_drag_end; @@ -490,6 +515,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) container_class->forall = gtk_text_view_forall; klass->move_cursor = gtk_text_view_move_cursor; + klass->page_horizontally = gtk_text_view_page_horizontally; klass->set_anchor = gtk_text_view_set_anchor; klass->insert_at_cursor = gtk_text_view_insert_at_cursor; klass->delete_from_cursor = gtk_text_view_delete_from_cursor; @@ -497,6 +523,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) klass->copy_clipboard = gtk_text_view_copy_clipboard; klass->paste_clipboard = gtk_text_view_paste_clipboard; klass->toggle_overwrite = gtk_text_view_toggle_overwrite; + klass->move_focus = gtk_text_view_move_focus; klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments; /* @@ -606,109 +633,137 @@ gtk_text_view_class_init (GtkTextViewClass *klass) G_PARAM_READWRITE)); - /* - * Style properties - */ - - gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("cursor_color", - _("Cursor color"), - _("Color with which to draw insertion cursor"), - GDK_TYPE_COLOR, - G_PARAM_READABLE)); - - /* * Signals */ signals[MOVE_CURSOR] = - gtk_signal_new ("move_cursor", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, move_cursor), - gtk_marshal_VOID__ENUM_INT_BOOLEAN, - GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL); - + g_signal_new ("move_cursor", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, move_cursor), + NULL, NULL, + _gtk_marshal_VOID__ENUM_INT_BOOLEAN, + G_TYPE_NONE, 3, + GTK_TYPE_MOVEMENT_STEP, + G_TYPE_INT, + G_TYPE_BOOLEAN); + + signals[PAGE_HORIZONTALLY] = + g_signal_new ("page_horizontally", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, page_horizontally), + NULL, NULL, + _gtk_marshal_VOID__INT_BOOLEAN, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_BOOLEAN); + signals[SET_ANCHOR] = - gtk_signal_new ("set_anchor", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new ("set_anchor", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, set_anchor), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[INSERT_AT_CURSOR] = - gtk_signal_new ("insert_at_cursor", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, insert_at_cursor), - gtk_marshal_VOID__STRING, - GTK_TYPE_NONE, 1, GTK_TYPE_STRING); + g_signal_new ("insert_at_cursor", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, insert_at_cursor), + NULL, NULL, + _gtk_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); signals[DELETE_FROM_CURSOR] = - gtk_signal_new ("delete_from_cursor", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_from_cursor), - gtk_marshal_VOID__ENUM_INT, - GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT); + g_signal_new ("delete_from_cursor", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, delete_from_cursor), + NULL, NULL, + _gtk_marshal_VOID__ENUM_INT, + G_TYPE_NONE, 2, + GTK_TYPE_DELETE_TYPE, + G_TYPE_INT); signals[CUT_CLIPBOARD] = - gtk_signal_new ("cut_clipboard", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_clipboard), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new ("cut_clipboard", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, cut_clipboard), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[COPY_CLIPBOARD] = - gtk_signal_new ("copy_clipboard", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_clipboard), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new ("copy_clipboard", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, copy_clipboard), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[PASTE_CLIPBOARD] = - gtk_signal_new ("paste_clipboard", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_clipboard), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new ("paste_clipboard", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, paste_clipboard), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); signals[TOGGLE_OVERWRITE] = - gtk_signal_new ("toggle_overwrite", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); - + g_signal_new ("toggle_overwrite", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, toggle_overwrite), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[MOVE_FOCUS] = + g_signal_new ("move_focus", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, move_focus), + NULL, NULL, + _gtk_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GTK_TYPE_DIRECTION_TYPE); + signals[SET_SCROLL_ADJUSTMENTS] = - gtk_signal_new ("set_scroll_adjustments", - GTK_RUN_LAST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments), - gtk_marshal_VOID__OBJECT_OBJECT, - GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + g_signal_new ("set_scroll_adjustments", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, set_scroll_adjustments), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_ADJUSTMENT, + GTK_TYPE_ADJUSTMENT); widget_class->set_scroll_adjustments_signal = signals[SET_SCROLL_ADJUSTMENTS]; signals[POPULATE_POPUP] = - gtk_signal_new ("populate_popup", - GTK_RUN_LAST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, populate_popup), - gtk_marshal_VOID__OBJECT, - GTK_TYPE_NONE, 1, GTK_TYPE_MENU); + g_signal_new ("populate_popup", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTextViewClass, populate_popup), + NULL, NULL, + _gtk_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_MENU); /* * Key bindings */ binding_set = gtk_binding_set_by_class (klass); - + /* Moving the insertion point */ add_move_binding (binding_set, GDK_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1); @@ -722,12 +777,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) add_move_binding (binding_set, GDK_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); - add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK, - GTK_MOVEMENT_LOGICAL_POSITIONS, 1); - - add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK, - GTK_MOVEMENT_LOGICAL_POSITIONS, -1); - add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); @@ -740,7 +789,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS, 1); - /* Eventually we want to move by display lines, not paragraphs */ add_move_binding (binding_set, GDK_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); @@ -753,12 +801,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) add_move_binding (binding_set, GDK_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, - GTK_MOVEMENT_DISPLAY_LINES, -1); - - add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, - GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_binding (binding_set, GDK_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, -1); @@ -771,18 +813,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass) add_move_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS, 1); - add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK, - GTK_MOVEMENT_PARAGRAPH_ENDS, -1); - - add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK, - GTK_MOVEMENT_PARAGRAPH_ENDS, 1); - - add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK, - GTK_MOVEMENT_WORDS, 1); - - add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK, - GTK_MOVEMENT_WORDS, -1); - add_move_binding (binding_set, GDK_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); @@ -818,106 +848,134 @@ gtk_text_view_class_init (GtkTextViewClass *klass) add_move_binding (binding_set, GDK_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); - - /* Setting the cut/paste/copy anchor */ - gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, - "set_anchor", 0); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_CONTROL_MASK, - "set_anchor", 0); + + /* Select all + */ + gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, + "move_cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, + "move_cursor", 3, + GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); + + + gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_CONTROL_MASK, + "page_horizontally", 2, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "page_horizontally", 2, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, GDK_CONTROL_MASK, + "page_horizontally", 2, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "page_horizontally", 2, + G_TYPE_INT, -1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_CONTROL_MASK, + "page_horizontally", 2, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "page_horizontally", 2, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, GDK_CONTROL_MASK, + "page_horizontally", 2, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "page_horizontally", 2, + G_TYPE_INT, 1, + G_TYPE_BOOLEAN, TRUE); /* Deleting text */ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_CHARS, - GTK_TYPE_INT, 1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_CHARS, - GTK_TYPE_INT, 1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_CHARS, - GTK_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_CHARS, - GTK_TYPE_INT, -1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, -1); + + /* Make this do the same as Backspace, to help with mis-typing */ + gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_SHIFT_MASK, + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_CHARS, + G_TYPE_INT, -1); gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, - GTK_TYPE_INT, 1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, - GTK_TYPE_INT, 1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, - GTK_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, - GTK_TYPE_INT, -1); - - gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, - GTK_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS, - GTK_TYPE_INT, 1); - - gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE, - GTK_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE, - GTK_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK, - "insert_at_cursor", 1, - GTK_TYPE_STRING, " "); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_MOD1_MASK, - "insert_at_cursor", 1, - GTK_TYPE_STRING, " "); - - gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK, - "delete_from_cursor", 2, - GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE, - GTK_TYPE_INT, 1); + "delete_from_cursor", 2, + G_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + G_TYPE_INT, -1); /* Cut/copy/paste */ gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK, - "cut_clipboard", 0); - - gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK, - "cut_clipboard", 0); - + "cut_clipboard", 0); gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK, - "copy_clipboard", 0); - + "copy_clipboard", 0); gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK, - "paste_clipboard", 0); + "paste_clipboard", 0); - gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK, - "paste_clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK, + "cut_clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK, + "copy_clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK, + "paste_clipboard", 0); /* Overwrite */ gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0, - "toggle_overwrite", 0); + "toggle_overwrite", 0); gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0, - "toggle_overwrite", 0); + "toggle_overwrite", 0); + + /* Control-tab focus motion */ + gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_CONTROL_MASK, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_CONTROL_MASK, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD); + + gtk_binding_entry_add_signal (binding_set, GDK_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK, + "move_focus", 1, + GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); } static void @@ -954,11 +1012,14 @@ gtk_text_view_init (GtkTextView *text_view) */ text_view->im_context = gtk_im_multicontext_new (); - g_signal_connect (G_OBJECT (text_view->im_context), "commit", + g_signal_connect (text_view->im_context, "commit", G_CALLBACK (gtk_text_view_commit_handler), text_view); - - g_signal_connect (G_OBJECT (text_view->im_context), "preedit_changed", + g_signal_connect (text_view->im_context, "preedit_changed", G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view); + g_signal_connect (text_view->im_context, "retrieve_surrounding", + G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view); + g_signal_connect (text_view->im_context, "delete_surrounding", + G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view); text_view->cursor_visible = TRUE; @@ -969,6 +1030,9 @@ gtk_text_view_init (GtkTextView *text_view) text_view->drag_start_y = -1; text_view->pending_place_cursor_button = 0; + + /* We handle all our own redrawing */ + gtk_widget_set_redraw_on_allocate (widget, FALSE); } /** @@ -984,7 +1048,7 @@ gtk_text_view_init (GtkTextView *text_view) GtkWidget* gtk_text_view_new (void) { - return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ())); + return g_object_new (GTK_TYPE_TEXT_VIEW, NULL); } /** @@ -1057,14 +1121,18 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_slist_free (copy); - g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->buffer), - gtk_text_view_mark_set_handler, text_view); - g_object_unref (G_OBJECT (text_view->buffer)); + g_signal_handlers_disconnect_by_func (text_view->buffer, + gtk_text_view_mark_set_handler, + text_view); + g_object_unref (text_view->buffer); text_view->dnd_mark = NULL; if (GTK_WIDGET_REALIZED (text_view)) - gtk_text_buffer_remove_selection_clipboard (text_view->buffer, - gtk_clipboard_get (GDK_SELECTION_PRIMARY)); + { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_PRIMARY); + gtk_text_buffer_remove_selection_clipboard (text_view->buffer, clipboard); + } } text_view->buffer = buffer; @@ -1073,7 +1141,7 @@ gtk_text_view_set_buffer (GtkTextView *text_view, { GtkTextIter start; - g_object_ref (G_OBJECT (buffer)); + g_object_ref (buffer); if (text_view->layout) gtk_text_layout_set_buffer (text_view->layout, buffer); @@ -1090,17 +1158,21 @@ gtk_text_view_set_buffer (GtkTextView *text_view, text_view->first_para_pixels = 0; - g_signal_connect (G_OBJECT (text_view->buffer), "mark_set", + g_signal_connect (text_view->buffer, "mark_set", G_CALLBACK (gtk_text_view_mark_set_handler), text_view); if (GTK_WIDGET_REALIZED (text_view)) - gtk_text_buffer_add_selection_clipboard (text_view->buffer, - gtk_clipboard_get (GDK_SELECTION_PRIMARY)); + { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_PRIMARY); + gtk_text_buffer_add_selection_clipboard (text_view->buffer, clipboard); + } } if (GTK_WIDGET_VISIBLE (text_view)) gtk_widget_queue_draw (GTK_WIDGET (text_view)); - + + DV(g_print ("Invalidating due to set_buffer\n")); gtk_text_view_invalidate (text_view); } @@ -1112,7 +1184,7 @@ get_buffer (GtkTextView *text_view) GtkTextBuffer *b; b = gtk_text_buffer_new (NULL); gtk_text_view_set_buffer (text_view, b); - g_object_unref (G_OBJECT (b)); + g_object_unref (b); } return text_view->buffer; @@ -1158,8 +1230,9 @@ gtk_text_view_get_iter_at_location (GtkTextView *text_view, { g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (iter != NULL); - g_return_if_fail (text_view->layout != NULL); + gtk_text_view_ensure_layout (text_view); + gtk_text_layout_get_iter_at_pixel (text_view->layout, iter, x, @@ -1186,6 +1259,8 @@ gtk_text_view_get_iter_location (GtkTextView *text_view, g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view)); + gtk_text_view_ensure_layout (text_view); + gtk_text_layout_get_iter_location (text_view->layout, iter, location); } @@ -1210,6 +1285,8 @@ gtk_text_view_get_line_yrange (GtkTextView *text_view, g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view)); + gtk_text_view_ensure_layout (text_view); + gtk_text_layout_get_line_yrange (text_view->layout, iter, y, @@ -1237,6 +1314,8 @@ gtk_text_view_get_line_at_y (GtkTextView *text_view, { g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + gtk_text_view_ensure_layout (text_view); + gtk_text_layout_get_line_at_y (text_view->layout, target_iter, y, @@ -1452,10 +1531,20 @@ free_pending_scroll (GtkTextPendingScroll *scroll) if (!gtk_text_mark_get_deleted (scroll->mark)) gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (scroll->mark), scroll->mark); - g_object_unref (G_OBJECT (scroll->mark)); + g_object_unref (scroll->mark); g_free (scroll); } +static void +cancel_pending_scroll (GtkTextView *text_view) +{ + if (text_view->pending_scroll) + { + free_pending_scroll (text_view->pending_scroll); + text_view->pending_scroll = NULL; + } +} + static void gtk_text_view_queue_scroll (GtkTextView *text_view, GtkTextMark *mark, @@ -1483,10 +1572,9 @@ gtk_text_view_queue_scroll (GtkTextView *text_view, &iter, gtk_text_mark_get_left_gravity (mark)); - g_object_ref (G_OBJECT (scroll->mark)); + g_object_ref (scroll->mark); - if (text_view->pending_scroll) - free_pending_scroll (text_view->pending_scroll); + cancel_pending_scroll (text_view); text_view->pending_scroll = scroll; } @@ -1497,11 +1585,17 @@ gtk_text_view_flush_scroll (GtkTextView *text_view) GtkTextIter iter; GtkTextPendingScroll *scroll; gboolean retval; + GtkWidget *widget; + + widget = GTK_WIDGET (text_view); DV(g_print(G_STRLOC"\n")); if (text_view->pending_scroll == NULL) - return FALSE; + { + DV (g_print ("in flush scroll, no pending scroll\n")); + return FALSE; + } scroll = text_view->pending_scroll; @@ -1510,11 +1604,17 @@ gtk_text_view_flush_scroll (GtkTextView *text_view) gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, scroll->mark); - /* Validate arbitrary area around the scroll destination, so the adjustment - * can meaningfully point into that area + /* Validate area around the scroll destination, so the adjustment + * can meaningfully point into that area. We must validate + * enough area to be sure that after we scroll, everything onscreen + * is valid; otherwise, validation will maintain the first para + * in one place, but may push the target iter off the bottom of + * the screen. */ DV(g_print (">Validating scroll destination ("G_STRLOC")\n")); - gtk_text_layout_validate_yrange (text_view->layout, &iter, -300, 300); + gtk_text_layout_validate_yrange (text_view->layout, &iter, + - (widget->allocation.height * 2), + widget->allocation.height * 2); DV(g_print (">Done validating scroll destination ("G_STRLOC")\n")); @@ -1549,13 +1649,13 @@ gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gdouble upper) value_changed = TRUE; } - gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed"); + gtk_adjustment_changed (adj); DV(g_print(">Changed adj upper to %g ("G_STRLOC")\n", upper)); if (value_changed) { DV(g_print(">Changed adj value because upper decreased ("G_STRLOC")\n")); - gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed"); + gtk_adjustment_value_changed (adj); } } } @@ -1595,8 +1695,8 @@ gtk_text_view_update_adjustments (GtkTextView *text_view) text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) * 0.9; - gtk_signal_emit_by_name (GTK_OBJECT (get_hadjustment (text_view)), "changed"); - gtk_signal_emit_by_name (GTK_OBJECT (get_hadjustment (text_view)), "changed"); + gtk_adjustment_changed (get_hadjustment (text_view)); + gtk_adjustment_changed (get_vadjustment (text_view)); } } @@ -1674,6 +1774,15 @@ gtk_text_view_scroll_to_mark (GtkTextView *text_view, gtk_text_view_flush_scroll (text_view); } +/** + * gtk_text_view_scroll_mark_onscreen: + * @text_view: a #GtkTextView + * @mark: a mark in the buffer for @text_view + * + * Scrolls @text_view the minimum distance such that @mark is contained + * within the visible area of the widget. + * + **/ void gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view, GtkTextMark *mark) @@ -1816,6 +1925,7 @@ gtk_text_view_set_editable (GtkTextView *text_view, gboolean setting) { g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + setting = setting != FALSE; if (text_view->editable != setting) { @@ -1826,9 +1936,9 @@ gtk_text_view_set_editable (GtkTextView *text_view, text_view->layout->default_style->editable = text_view->editable; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "editable"); + g_object_notify (G_OBJECT (text_view), "editable"); + } } /** @@ -1848,6 +1958,15 @@ gtk_text_view_get_editable (GtkTextView *text_view) return text_view->editable; } +/** + * gtk_text_view_set_pixels_above_lines: + * @text_view: a #GtkTextView + * @pixels_above_lines: pixels above paragraphs + * + * Sets the default number of blank pixels above paragraphs in @text_view. + * Tags in the buffer for @text_view may override the defaults. + * + **/ void gtk_text_view_set_pixels_above_lines (GtkTextView *text_view, gint pixels_above_lines) @@ -1863,11 +1982,19 @@ gtk_text_view_set_pixels_above_lines (GtkTextView *text_view, text_view->layout->default_style->pixels_above_lines = pixels_above_lines; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "pixels_above_lines"); + g_object_notify (G_OBJECT (text_view), "pixels_above_lines"); + } } +/** + * gtk_text_view_get_pixels_above_lines: + * @text_view: a #GtkTextView + * + * Gets the default number of pixels to put above paragraphs. + * + * Return value: default number of pixels above paragraphs + **/ gint gtk_text_view_get_pixels_above_lines (GtkTextView *text_view) { @@ -1876,6 +2003,16 @@ gtk_text_view_get_pixels_above_lines (GtkTextView *text_view) return text_view->pixels_above_lines; } +/** + * gtk_text_view_set_pixels_below_lines: + * @text_view: a #GtkTextView + * @pixels_below_lines: pixels below paragraphs + * + * Sets the default number of pixels of blank space + * to put below paragraphs in @text_view. May be overridden + * by tags applied to @text_view's buffer. + * + **/ void gtk_text_view_set_pixels_below_lines (GtkTextView *text_view, gint pixels_below_lines) @@ -1891,11 +2028,19 @@ gtk_text_view_set_pixels_below_lines (GtkTextView *text_view, text_view->layout->default_style->pixels_below_lines = pixels_below_lines; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "pixels_below_lines"); + g_object_notify (G_OBJECT (text_view), "pixels_below_lines"); + } } +/** + * gtk_text_view_get_pixels_below_lines: + * @text_view: a #GtkTextView + * + * Gets the value set by gtk_text_view_set_pixels_below_lines(). + * + * Return value: default number of blank pixels below paragraphs + **/ gint gtk_text_view_get_pixels_below_lines (GtkTextView *text_view) { @@ -1904,6 +2049,16 @@ gtk_text_view_get_pixels_below_lines (GtkTextView *text_view) return text_view->pixels_below_lines; } +/** + * gtk_text_view_set_pixels_inside_wrap: + * @text_view: a #GtkTextView + * @pixels_inside_wrap: default number of pixels between wrapped lines + * + * Sets the default number of pixels of blank space to leave between + * display/wrapped lines within a paragraph. May be overridden by + * tags in @text_view's buffer. + * + **/ void gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view, gint pixels_inside_wrap) @@ -1919,10 +2074,19 @@ gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view, text_view->layout->default_style->pixels_inside_wrap = pixels_inside_wrap; gtk_text_layout_default_style_changed (text_view->layout); } + + g_object_notify (G_OBJECT (text_view), "pixels_inside_wrap"); } - g_object_notify (G_OBJECT (text_view), "pixels_inside_wrap"); } +/** + * gtk_text_view_get_pixels_inside_wrap: + * @text_view: a #GtkTextView + * + * Gets the value set by gtk_text_view_set_pixels_inside_wrap(). + * + * Return value: default number of pixels of blank space between wrapped lines + **/ gint gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view) { @@ -1931,26 +2095,44 @@ gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view) return text_view->pixels_inside_wrap; } +/** + * gtk_text_view_set_justification: + * @text_view: a #GtkTextView + * @justification: justification + * + * Sets the default justification of text in @text_view. + * Tags in the view's buffer may override the default. + * + **/ void gtk_text_view_set_justification (GtkTextView *text_view, - GtkJustification justify) + GtkJustification justification) { g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); - if (text_view->justify != justify) + if (text_view->justify != justification) { - text_view->justify = justify; + text_view->justify = justification; if (text_view->layout) { - text_view->layout->default_style->justification = justify; + text_view->layout->default_style->justification = justification; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "justification"); + g_object_notify (G_OBJECT (text_view), "justification"); + } } +/** + * gtk_text_view_get_justification: + * @text_view: a #GtkTextView + * + * Gets the default justification of paragraphs in @text_view. + * Tags in the buffer may override the default. + * + * Return value: default justification + **/ GtkJustification gtk_text_view_get_justification (GtkTextView *text_view) { @@ -1959,6 +2141,15 @@ gtk_text_view_get_justification (GtkTextView *text_view) return text_view->justify; } +/** + * gtk_text_view_set_left_margin: + * @text_view: a #GtkTextView + * @left_margin: left margin in pixels + * + * Sets the default left margin for text in @text_view. + * Tags in the buffer may override the default. + * + **/ void gtk_text_view_set_left_margin (GtkTextView *text_view, gint left_margin) @@ -1974,11 +2165,20 @@ gtk_text_view_set_left_margin (GtkTextView *text_view, text_view->layout->default_style->left_margin = left_margin; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "left_margin"); + g_object_notify (G_OBJECT (text_view), "left_margin"); + } } +/** + * gtk_text_view_get_left_margin: + * @text_view: a #GtkTextView + * + * Gets the default left margin size of paragraphs in the @text_view. + * Tags in the buffer may override the default. + * + * Return value: left margin in pixels + **/ gint gtk_text_view_get_left_margin (GtkTextView *text_view) { @@ -1987,6 +2187,15 @@ gtk_text_view_get_left_margin (GtkTextView *text_view) return text_view->left_margin; } +/** + * gtk_text_view_set_right_margin: + * @text_view: a #GtkTextView + * @right_margin: right margin in pixels + * + * Sets the default right margin for text in the text view. + * Tags in the buffer may override the default. + * + **/ void gtk_text_view_set_right_margin (GtkTextView *text_view, gint right_margin) @@ -2002,11 +2211,20 @@ gtk_text_view_set_right_margin (GtkTextView *text_view, text_view->layout->default_style->right_margin = right_margin; gtk_text_layout_default_style_changed (text_view->layout); } - } - g_object_notify (G_OBJECT (text_view), "right_margin"); + g_object_notify (G_OBJECT (text_view), "right_margin"); + } } +/** + * gtk_text_view_get_right_margin: + * @text_view: a #GtkTextView + * + * Gets the default right margin for text in @text_view. Tags + * in the buffer may override the default. + * + * Return value: right margin in pixels + **/ gint gtk_text_view_get_right_margin (GtkTextView *text_view) { @@ -2015,6 +2233,15 @@ gtk_text_view_get_right_margin (GtkTextView *text_view) return text_view->right_margin; } +/** + * gtk_text_view_set_indent: + * @text_view: a #GtkTextView + * @indent: indentation in pixels + * + * Sets the default indentation for paragraphs in @text_view. + * Tags in the buffer may override the default. + * + **/ void gtk_text_view_set_indent (GtkTextView *text_view, gint indent) @@ -2030,10 +2257,21 @@ gtk_text_view_set_indent (GtkTextView *text_view, text_view->layout->default_style->indent = indent; gtk_text_layout_default_style_changed (text_view->layout); } + + g_object_notify (G_OBJECT (text_view), "indent"); } - g_object_notify (G_OBJECT (text_view), "indent"); } +/** + * gtk_text_view_get_indent: + * @text_view: a #GtkTextView + * + * Gets the default indentation of paragraphs in @text_view. + * Tags in the view's buffer may override the default. + * The indentation may be negative. + * + * Return value: number of pixels of indentation + **/ gint gtk_text_view_get_indent (GtkTextView *text_view) { @@ -2042,6 +2280,15 @@ gtk_text_view_get_indent (GtkTextView *text_view) return text_view->indent; } +/** + * gtk_text_view_set_tabs: + * @text_view: a #GtkTextView + * @tabs: tabs as a #PangoTabArray + * + * Sets the default tab stops for paragraphs in @text_view. + * Tags in the buffer may override the default. + * + **/ void gtk_text_view_set_tabs (GtkTextView *text_view, PangoTabArray *tabs) @@ -2068,6 +2315,17 @@ gtk_text_view_set_tabs (GtkTextView *text_view, g_object_notify (G_OBJECT (text_view), "tabs"); } +/** + * gtk_text_view_get_tabs: + * @text_view: a #GtkTextView + * + * Gets the default tabs for @text_view. Tags in the buffer may + * override the defaults. The returned array will be %NULL if + * "standard" (8-space) tabs are used. Free the return value + * with pango_tab_array_free(). + * + * Return value: copy of default tab array, or %NULL if "standard" tabs are used; must be freed with pango_tab_array_free(). + **/ PangoTabArray* gtk_text_view_get_tabs (GtkTextView *text_view) { @@ -2105,9 +2363,9 @@ gtk_text_view_set_cursor_visible (GtkTextView *text_view, gtk_text_view_check_cursor_blink (text_view); } } - } - g_object_notify (G_OBJECT (text_view), "cursor_visible"); + g_object_notify (G_OBJECT (text_view), "cursor_visible"); + } } /** @@ -2156,6 +2414,23 @@ gtk_text_view_place_cursor_onscreen (GtkTextView *text_view) return FALSE; } +static void +gtk_text_view_remove_validate_idles (GtkTextView *text_view) +{ + if (text_view->first_validate_idle != 0) + { + DV (g_print ("Removing first validate idle: %s\n", G_STRLOC)); + g_source_remove (text_view->first_validate_idle); + text_view->first_validate_idle = 0; + } + + if (text_view->incremental_validate_idle != 0) + { + g_source_remove (text_view->incremental_validate_idle); + text_view->incremental_validate_idle = 0; + } +} + static void gtk_text_view_destroy (GtkObject *object) { @@ -2166,6 +2441,7 @@ gtk_text_view_destroy (GtkObject *object) layout = text_view->layout; + gtk_text_view_remove_validate_idles (text_view); gtk_text_view_set_buffer (text_view, NULL); gtk_text_view_destroy_layout (text_view); @@ -2184,16 +2460,15 @@ gtk_text_view_finalize (GObject *object) gtk_text_view_destroy_layout (text_view); gtk_text_view_set_buffer (text_view, NULL); - if (text_view->pending_scroll) - { - free_pending_scroll (text_view->pending_scroll); - text_view->pending_scroll = NULL; - } + cancel_pending_scroll (text_view); + + if (text_view->tabs) + pango_tab_array_free (text_view->tabs); if (text_view->hadjustment) - g_object_unref (G_OBJECT (text_view->hadjustment)); + g_object_unref (text_view->hadjustment); if (text_view->vadjustment) - g_object_unref (G_OBJECT (text_view->vadjustment)); + g_object_unref (text_view->vadjustment); text_window_free (text_view->text_window); @@ -2209,8 +2484,8 @@ gtk_text_view_finalize (GObject *object) if (text_view->bottom_window) text_window_free (text_view->bottom_window); - g_object_unref (G_OBJECT (text_view->im_context)); - + g_object_unref (text_view->im_context); + (* G_OBJECT_CLASS (parent_class)->finalize) (object); } @@ -2263,7 +2538,7 @@ gtk_text_view_set_property (GObject *object, break; case PROP_TABS: - gtk_text_view_set_tabs (text_view, g_value_get_object (value)); + gtk_text_view_set_tabs (text_view, g_value_get_boxed (value)); break; case PROP_CURSOR_VISIBLE: @@ -2325,7 +2600,7 @@ gtk_text_view_get_property (GObject *object, break; case PROP_TABS: - g_value_set_object (value, gtk_text_view_get_tabs (text_view)); + g_value_set_boxed (value, text_view->tabs); break; case PROP_CURSOR_VISIBLE: @@ -2345,16 +2620,20 @@ gtk_text_view_size_request (GtkWidget *widget, GtkTextView *text_view; GSList *tmp_list; gint focus_edge_width; + gint focus_width; gboolean interior_focus; text_view = GTK_TEXT_VIEW (widget); - gtk_widget_style_get (widget, "interior_focus", &interior_focus, NULL); + gtk_widget_style_get (widget, + "interior_focus", &interior_focus, + "focus_line_width", &focus_width, + NULL); if (interior_focus) focus_edge_width = 0; else - focus_edge_width = 1; + focus_edge_width = focus_width; if (text_view->layout) { @@ -2382,6 +2661,9 @@ gtk_text_view_size_request (GtkWidget *widget, if (text_view->bottom_window) requisition->height += text_view->bottom_window->requisition.height; + requisition->width += GTK_CONTAINER (text_view)->border_width * 2; + requisition->height += GTK_CONTAINER (text_view)->border_width * 2; + tmp_list = text_view->children; while (tmp_list != NULL) { @@ -2392,10 +2674,13 @@ gtk_text_view_size_request (GtkWidget *widget, GtkRequisition child_req; GtkRequisition old_req; - old_req = child->widget->requisition; - + gtk_widget_get_child_requisition (child->widget, &old_req); + gtk_widget_size_request (child->widget, &child_req); + gtk_widget_get_child_requisition (child->widget, &child_req); + + /* Invalidate layout lines if required */ if (text_view->layout && (old_req.width != child_req.width || old_req.height != child_req.height)) @@ -2404,7 +2689,9 @@ gtk_text_view_size_request (GtkWidget *widget, } else { - + GtkRequisition child_req; + + gtk_widget_size_request (child->widget, &child_req); } tmp_list = g_slist_next (tmp_list); @@ -2412,12 +2699,13 @@ gtk_text_view_size_request (GtkWidget *widget, } static void -gtk_text_view_update_child_allocation (GtkTextView *text_view, - GtkTextViewChild *vc) +gtk_text_view_compute_child_allocation (GtkTextView *text_view, + GtkTextViewChild *vc, + GtkAllocation *allocation) { gint buffer_y; GtkTextIter iter; - GtkAllocation allocation; + GtkRequisition req; gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view), &iter, @@ -2428,12 +2716,31 @@ gtk_text_view_update_child_allocation (GtkTextView *text_view, buffer_y += vc->from_top_of_line; - allocation.x = vc->from_left_of_buffer; - allocation.y = buffer_y; - allocation.width = vc->widget->requisition.width; - allocation.height = vc->widget->requisition.height; + allocation->x = vc->from_left_of_buffer - text_view->xoffset; + allocation->y = buffer_y - text_view->yoffset; + + gtk_widget_get_child_requisition (vc->widget, &req); + allocation->width = req.width; + allocation->height = req.height; +} + +static void +gtk_text_view_update_child_allocation (GtkTextView *text_view, + GtkTextViewChild *vc) +{ + GtkAllocation allocation; + + gtk_text_view_compute_child_allocation (text_view, vc, &allocation); gtk_widget_size_allocate (vc->widget, &allocation); + +#if 0 + g_print ("allocation for %p allocated to %d,%d yoffset = %d\n", + vc->widget, + vc->widget->allocation.x, + vc->widget->allocation.y, + text_view->yoffset); +#endif } static void @@ -2452,11 +2759,11 @@ gtk_text_view_child_allocated (GtkTextLayout *layout, */ vc = g_object_get_data (G_OBJECT (child), - "gtk-text-view-child"); + "gtk-text-view-child"); g_assert (vc != NULL); - g_print ("child allocated at %d,%d\n", x, y); + DV (g_print ("child allocated at %d,%d\n", x, y)); vc->from_left_of_buffer = x; vc->from_top_of_line = y; @@ -2465,7 +2772,7 @@ gtk_text_view_child_allocated (GtkTextLayout *layout, } static void -gtk_text_view_validate_children (GtkTextView *text_view) +gtk_text_view_allocate_children (GtkTextView *text_view) { GSList *tmp_list; @@ -2492,7 +2799,20 @@ gtk_text_view_validate_children (GtkTextView *text_view) } else { + GtkAllocation allocation; + GtkRequisition child_req; + + g_assert (child != NULL); + + allocation.x = child->x; + allocation.y = child->y; + gtk_widget_get_child_requisition (child->widget, &child_req); + + allocation.width = child_req.width; + allocation.height = child_req.height; + + gtk_widget_size_allocate (child->widget, &allocation); } tmp_list = g_slist_next (tmp_list); @@ -2515,11 +2835,17 @@ gtk_text_view_size_allocate (GtkWidget *widget, GdkRectangle top_rect; GdkRectangle bottom_rect; gint focus_edge_width; + gint focus_width; gboolean interior_focus; + gboolean size_changed; text_view = GTK_TEXT_VIEW (widget); DV(g_print(G_STRLOC"\n")); + + size_changed = + widget->allocation.width != allocation->width || + widget->allocation.height != allocation->height; widget->allocation = *allocation; @@ -2534,26 +2860,29 @@ gtk_text_view_size_allocate (GtkWidget *widget, * windows get at least a 1x1 allocation. */ - gtk_widget_style_get (widget, "interior_focus", &interior_focus, NULL); + gtk_widget_style_get (widget, + "interior_focus", &interior_focus, + "focus_line_width", &focus_width, + NULL); if (interior_focus) focus_edge_width = 0; else - focus_edge_width = 1; + focus_edge_width = focus_width; - width = allocation->width - focus_edge_width * 2; + width = allocation->width - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; if (text_view->left_window) left_rect.width = text_view->left_window->requisition.width; else - left_rect.width = 1; + left_rect.width = 0; width -= left_rect.width; if (text_view->right_window) right_rect.width = text_view->right_window->requisition.width; else - right_rect.width = 1; + right_rect.width = 0; width -= right_rect.width; @@ -2563,19 +2892,19 @@ gtk_text_view_size_allocate (GtkWidget *widget, bottom_rect.width = text_rect.width; - height = allocation->height - focus_edge_width * 2; + height = allocation->height - focus_edge_width * 2 - GTK_CONTAINER (text_view)->border_width * 2; if (text_view->top_window) top_rect.height = text_view->top_window->requisition.height; else - top_rect.height = 1; + top_rect.height = 0; height -= top_rect.height; if (text_view->bottom_window) bottom_rect.height = text_view->bottom_window->requisition.height; else - bottom_rect.height = 1; + bottom_rect.height = 0; height -= bottom_rect.height; @@ -2585,8 +2914,8 @@ gtk_text_view_size_allocate (GtkWidget *widget, right_rect.height = text_rect.height; /* Origins */ - left_rect.x = focus_edge_width; - top_rect.y = focus_edge_width; + left_rect.x = focus_edge_width + GTK_CONTAINER (text_view)->border_width; + top_rect.y = focus_edge_width + GTK_CONTAINER (text_view)->border_width; text_rect.x = left_rect.x + left_rect.width; text_rect.y = top_rect.y + top_rect.height; @@ -2621,8 +2950,8 @@ gtk_text_view_size_allocate (GtkWidget *widget, gtk_text_view_update_layout_width (text_view); - /* This is because validating children ends up size allocating them. */ - gtk_text_view_validate_children (text_view); + /* Note that this will do some layout validation */ + gtk_text_view_allocate_children (text_view); /* Now adjust the value of the adjustment to keep the cursor at the * same place in the buffer @@ -2642,40 +2971,40 @@ gtk_text_view_size_allocate (GtkWidget *widget, if (y != text_view->yoffset) { - vadj->value = text_view->yoffset = y; + vadj->value = y; yoffset_changed = TRUE; } text_view->hadjustment->page_size = SCREEN_WIDTH (text_view); - text_view->hadjustment->page_increment = SCREEN_WIDTH (text_view) / 2; + text_view->hadjustment->page_increment = SCREEN_WIDTH (text_view) * 0.9; + text_view->hadjustment->step_increment = SCREEN_WIDTH (text_view) * 0.1; text_view->hadjustment->lower = 0; text_view->hadjustment->upper = MAX (SCREEN_WIDTH (text_view), text_view->width); - gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed"); + gtk_adjustment_changed (text_view->hadjustment); text_view->vadjustment->page_size = SCREEN_HEIGHT (text_view); - text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) / 2; + text_view->vadjustment->page_increment = SCREEN_HEIGHT (text_view) * 0.9; + text_view->vadjustment->step_increment = SCREEN_HEIGHT (text_view) * 0.1; text_view->vadjustment->lower = 0; text_view->vadjustment->upper = MAX (SCREEN_HEIGHT (text_view), text_view->height); - gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed"); + gtk_adjustment_changed (text_view->vadjustment); if (yoffset_changed) gtk_adjustment_value_changed (vadj); - if (text_view->first_validate_idle != 0) - { - /* The GTK resize loop processes all the pending exposes right - * after doing the resize stuff, so the idle sizer won't have a - * chance to run. So we do the work here. - */ + /* The GTK resize loop processes all the pending exposes right + * after doing the resize stuff, so the idle sizer won't have a + * chance to run. So we do the work here. + */ + gtk_text_view_flush_first_validate (text_view); - g_source_remove (text_view->first_validate_idle); - text_view->first_validate_idle = 0; - - if (!gtk_text_view_flush_scroll (text_view)) - gtk_text_view_validate_onscreen (text_view); - } + /* widget->window doesn't get auto-redrawn as the layout is computed, so has to + * be invalidated + */ + if (size_changed && GTK_WIDGET_REALIZED (widget)) + gdk_window_invalidate_rect (widget->window, NULL, FALSE); } static void @@ -2723,23 +3052,18 @@ gtk_text_view_validate_onscreen (GtkTextView *text_view) g_assert (text_view->onscreen_validated); } -static gboolean -first_validate_callback (gpointer data) +static void +gtk_text_view_flush_first_validate (GtkTextView *text_view) { - GtkTextView *text_view = data; - - GDK_THREADS_ENTER (); - - /* Note that some of this code is duplicated at the end of size_allocate, - * keep in sync with that. - */ - - DV(g_print(G_STRLOC"\n")); + if (text_view->first_validate_idle == 0) + return; /* Do this first, which means that if an "invalidate" * occurs during any of this process, a new first_validate_callback * will be installed, and we'll start again. */ + DV (g_print ("removing first validate in %s\n", G_STRLOC)); + g_source_remove (text_view->first_validate_idle); text_view->first_validate_idle = 0; /* be sure we have up-to-date screen size set on the @@ -2756,18 +3080,36 @@ first_validate_callback (gpointer data) } else { - /* scroll to any marks, if that's pending. This can - * jump us to the validation codepath used for scrolling - * onscreen, if so we bail out. + /* scroll to any marks, if that's pending. This can jump us to + * the validation codepath used for scrolling onscreen, if so we + * bail out. It won't jump if already in that codepath since + * value_changed is not recursive, so also validate if + * necessary. */ - if (!gtk_text_view_flush_scroll (text_view)) + if (!gtk_text_view_flush_scroll (text_view) || + !text_view->onscreen_validated) gtk_text_view_validate_onscreen (text_view); DV(g_print(">Leaving first validate idle ("G_STRLOC")\n")); g_assert (text_view->onscreen_validated); - } +} + +static gboolean +first_validate_callback (gpointer data) +{ + GtkTextView *text_view = data; + + GDK_THREADS_ENTER (); + + /* Note that some of this code is duplicated at the end of size_allocate, + * keep in sync with that. + */ + + DV(g_print(G_STRLOC"\n")); + + gtk_text_view_flush_first_validate (text_view); GDK_THREADS_LEAVE (); @@ -2801,10 +3143,15 @@ incremental_validate_callback (gpointer data) static void gtk_text_view_invalidate (GtkTextView *text_view) -{ +{ + DV (g_print (">Invalidate, onscreen_validated = %d now FALSE ("G_STRLOC")\n", + text_view->onscreen_validated)); + text_view->onscreen_validated = FALSE; - - DV(g_print(">Invalidate, onscreen_validated = FALSE ("G_STRLOC")\n")); + + /* We'll invalidate when the layout is created */ + if (text_view->layout == NULL) + return; if (!text_view->first_validate_idle) { @@ -2829,6 +3176,7 @@ invalidated_handler (GtkTextLayout *layout, text_view = GTK_TEXT_VIEW (data); + DV (g_print ("Invalidating due to layout invalidate signal\n")); gtk_text_view_invalidate (text_view); } @@ -2859,9 +3207,11 @@ changed_handler (GtkTextLayout *layout, if (old_height == new_height) redraw_rect.height = old_height; - else + else if (start_y + old_height > visible_rect.y) redraw_rect.height = MAX (0, visible_rect.y + visible_rect.height - start_y); - + else + redraw_rect.height = 0; + if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect)) { /* text_window_invalidate_rect() takes buffer coordinates */ @@ -2924,7 +3274,10 @@ changed_handler (GtkTextLayout *layout, } if (yoffset_changed) - gtk_adjustment_value_changed (get_vadjustment (text_view)); + { + DV(g_print ("Changing scroll position (%s)\n", G_STRLOC)); + gtk_adjustment_value_changed (get_vadjustment (text_view)); + } /* FIXME be smarter about which anchored widgets we update */ @@ -2940,25 +3293,25 @@ changed_handler (GtkTextLayout *layout, } } - gtk_widget_queue_resize (widget); -} - -static void -gtk_text_view_realize_cursor_gc (GtkTextView *text_view) -{ - GdkColor *cursor_color; - GdkColor red = { 0, 0xffff, 0x0000, 0x0000 }; - - if (text_view->cursor_gc) - gdk_gc_unref (text_view->cursor_gc); + { + GtkRequisition old_req; + GtkRequisition new_req; - gtk_widget_style_get (GTK_WIDGET (text_view), "cursor_color", &cursor_color, NULL); + old_req = widget->requisition; - if (!cursor_color) - cursor_color = &red; + /* Use this instead of gtk_widget_size_request wrapper + * 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); - text_view->cursor_gc = gdk_gc_new (text_view->text_window->bin_window); - gdk_gc_set_rgb_fg_color (text_view->cursor_gc, cursor_color); + if (old_req.width != new_req.width || + old_req.height != new_req.height) + { + /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */ + _gtk_size_group_queue_resize (widget); + } + } } static void @@ -2967,6 +3320,7 @@ gtk_text_view_realize (GtkWidget *widget) GtkTextView *text_view; GdkWindowAttr attributes; gint attributes_mask; + GSList *tmp_list; text_view = GTK_TEXT_VIEW (widget); GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED); @@ -3011,13 +3365,24 @@ gtk_text_view_realize (GtkWidget *widget) text_window_realize (text_view->bottom_window, widget->window); - gtk_text_view_realize_cursor_gc (text_view); - gtk_text_view_ensure_layout (text_view); if (text_view->buffer) - gtk_text_buffer_add_selection_clipboard (text_view->buffer, - gtk_clipboard_get (GDK_SELECTION_PRIMARY)); + { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_PRIMARY); + gtk_text_buffer_add_selection_clipboard (text_view->buffer, clipboard); + } + + tmp_list = text_view->children; + while (tmp_list != NULL) + { + GtkTextViewChild *vc = tmp_list->data; + + text_view_child_set_parent_window (text_view, vc); + + tmp_list = tmp_list->next; + } } static void @@ -3028,26 +3393,13 @@ gtk_text_view_unrealize (GtkWidget *widget) text_view = GTK_TEXT_VIEW (widget); if (text_view->buffer) - gtk_text_buffer_remove_selection_clipboard (text_view->buffer, - gtk_clipboard_get (GDK_SELECTION_PRIMARY)); - - if (text_view->cursor_gc) { - gdk_gc_unref (text_view->cursor_gc); - text_view->cursor_gc = NULL; + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_PRIMARY); + gtk_text_buffer_remove_selection_clipboard (text_view->buffer, clipboard); } - if (text_view->first_validate_idle) - { - g_source_remove (text_view->first_validate_idle); - text_view->first_validate_idle = 0; - } - - if (text_view->incremental_validate_idle) - { - g_source_remove (text_view->incremental_validate_idle); - text_view->incremental_validate_idle = 0; - } + gtk_text_view_remove_validate_idles (text_view); if (text_view->popup_menu) { @@ -3102,8 +3454,6 @@ gtk_text_view_style_set (GtkWidget *widget, if (text_view->bottom_window) gdk_window_set_background (text_view->bottom_window->bin_window, &widget->style->bg[GTK_WIDGET_STATE (widget)]); - - gtk_text_view_realize_cursor_gc (text_view); } if (text_view->layout && previous_style) @@ -3128,6 +3478,45 @@ gtk_text_view_direction_changed (GtkWidget *widget, } } + +static void +set_invisible_cursor (GdkWindow *window) +{ + GdkBitmap *empty_bitmap; + GdkCursor *cursor; + GdkColor useless; + char invisible_cursor_bits[] = { 0x0 }; + + useless.red = useless.green = useless.blue = 0; + useless.pixel = 0; + + empty_bitmap = gdk_bitmap_create_from_data (window, + invisible_cursor_bits, + 1, 1); + + cursor = gdk_cursor_new_from_pixmap (empty_bitmap, + empty_bitmap, + &useless, + &useless, 0, 0); + + gdk_window_set_cursor (window, cursor); + + gdk_cursor_unref (cursor); + + g_object_unref (empty_bitmap); +} + +static void +gtk_text_view_obscure_mouse_cursor (GtkTextView *text_view) +{ + if (text_view->mouse_cursor_obscured) + return; + + set_invisible_cursor (text_view->text_window->bin_window); + + text_view->mouse_cursor_obscured = TRUE; +} + /* * Events */ @@ -3263,9 +3652,11 @@ static gint gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event) { gboolean retval = FALSE; + gboolean obscure = FALSE; GtkTextView *text_view = GTK_TEXT_VIEW (widget); GtkTextMark *insert; GtkTextIter iter; + gboolean can_insert; if (text_view->layout == NULL || get_buffer (text_view) == NULL) @@ -3273,44 +3664,57 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event) insert = gtk_text_buffer_get_insert (get_buffer (text_view)); gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert); - if (gtk_text_iter_can_insert (&iter, text_view->editable) && + can_insert = gtk_text_iter_can_insert (&iter, text_view->editable); + if (can_insert && gtk_im_context_filter_keypress (text_view->im_context, event)) { text_view->need_im_reset = TRUE; + obscure = TRUE; retval = TRUE; } + /* Binding set */ else if (GTK_WIDGET_CLASS (parent_class)->key_press_event && GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event)) retval = TRUE; - else if (event->keyval == GDK_Return || - event->keyval == GDK_KP_Enter) + /* use overall editability not can_insert, more predictable for users */ + else if (text_view->editable && + (event->keyval == GDK_Return || + event->keyval == GDK_KP_Enter)) { - gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1, - text_view->editable); - DV(g_print (G_STRLOC": scrolling to mark\n")); - gtk_text_view_scroll_to_mark (text_view, - gtk_text_buffer_get_mark (get_buffer (text_view), - "insert"), - 0.0, FALSE, 0.0, 0.0); + /* this won't actually insert the newline if the cursor isn't + * editable + */ + gtk_text_view_commit_text (text_view, "\n"); + + obscure = TRUE; retval = TRUE; } /* Pass through Tab as literal tab, unless Control is held down */ else if ((event->keyval == GDK_Tab || - event->keyval == GDK_KP_Tab || - event->keyval == GDK_ISO_Left_Tab) && + event->keyval == GDK_KP_Tab) && !(event->state & GDK_CONTROL_MASK)) { - gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\t", 1, - text_view->editable); - DV(g_print (G_STRLOC": scrolling onscreen\n")); - gtk_text_view_scroll_mark_onscreen (text_view, - gtk_text_buffer_get_mark (get_buffer (text_view), - "insert")); + /* If the text widget isn't editable overall, move the focus + * instead + */ + if (text_view->editable) + { + gtk_text_view_commit_text (text_view, "\t"); + obscure = TRUE; + } + else + gtk_text_view_move_focus (text_view, + (event->state & GDK_SHIFT_MASK) ? + GTK_DIR_TAB_BACKWARD: GTK_DIR_TAB_FORWARD); + retval = TRUE; } else retval = FALSE; + if (obscure) + gtk_text_view_obscure_mouse_cursor (text_view); + gtk_text_view_pend_cursor_blink (text_view); return retval; @@ -3319,7 +3723,23 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event) static gint gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event) { - return FALSE; + GtkTextView *text_view = GTK_TEXT_VIEW (widget); + GtkTextMark *insert; + GtkTextIter iter; + + if (text_view->layout == NULL || get_buffer (text_view) == NULL) + return FALSE; + + insert = gtk_text_buffer_get_insert (get_buffer (text_view)); + gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert); + if (gtk_text_iter_can_insert (&iter, text_view->editable) && + gtk_im_context_filter_keypress (text_view->im_context, event)) + { + text_view->need_im_reset = TRUE; + return TRUE; + } + else + return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event); } static gint @@ -3390,7 +3810,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) event->y + text_view->yoffset); gtk_text_buffer_paste_clipboard (get_buffer (text_view), - gtk_clipboard_get (GDK_SELECTION_PRIMARY), + gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY), &iter, text_view->editable); return TRUE; @@ -3404,8 +3824,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) event->type == GDK_3BUTTON_PRESS) && event->button == 1) { - GtkTextIter start, end; - + GtkTextIter start, end; + /* End the selection drag, otherwise we'd clear the new * word/line selection on button release */ @@ -3451,12 +3871,28 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } } - gtk_text_buffer_move_mark (get_buffer (text_view), - gtk_text_buffer_get_selection_bound (get_buffer (text_view)), - &start); - gtk_text_buffer_move_mark (get_buffer (text_view), - gtk_text_buffer_get_insert (get_buffer (text_view)), - &end); + if (event->state & GDK_SHIFT_MASK) + { + /* Take union of old and new selection */ + GtkTextIter old_start, old_end; + + gtk_text_buffer_get_selection_bounds (get_buffer (text_view), + &old_start, &old_end); + + gtk_text_iter_order (&start, &old_start); + gtk_text_iter_order (&old_end, &end); + + /* Now start is the first of the starts, and end is the + * last of the ends + */ + } + + gtk_text_buffer_move_mark_by_name (get_buffer (text_view), + "selection_bound", + &start); + gtk_text_buffer_move_mark_by_name (get_buffer (text_view), + "insert", + &end); text_view->just_selected_element = TRUE; @@ -3527,7 +3963,6 @@ gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event) { GtkTextView *text_view = GTK_TEXT_VIEW (widget); - GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); gtk_widget_queue_draw (widget); DV(g_print (G_STRLOC": focus_in_event\n")); @@ -3538,7 +3973,7 @@ gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event) gtk_text_view_check_cursor_blink (text_view); } - g_signal_connect (gdk_keymap_get_default (), + g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), "direction_changed", G_CALLBACK (keymap_direction_changed), text_view); gtk_text_view_check_keymap_direction (text_view); @@ -3554,7 +3989,6 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event) { GtkTextView *text_view = GTK_TEXT_VIEW (widget); - GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); gtk_widget_queue_draw (widget); DV(g_print (G_STRLOC": focus_out_event\n")); @@ -3565,9 +3999,9 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event) gtk_text_view_check_cursor_blink (text_view); } - g_signal_handlers_disconnect_by_func (gdk_keymap_get_default (), - keymap_direction_changed, - text_view); + g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), + keymap_direction_changed, + text_view); text_view->need_im_reset = TRUE; gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context); @@ -3580,6 +4014,17 @@ gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event) { GtkTextView *text_view = GTK_TEXT_VIEW (widget); + if (text_view->mouse_cursor_obscured) + { + GdkCursor *cursor; + + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), + GDK_XTERM); + gdk_window_set_cursor (text_view->text_window->bin_window, cursor); + gdk_cursor_unref (cursor); + text_view->mouse_cursor_obscured = FALSE; + } + if (event->window == text_view->text_window->bin_window && text_view->drag_start_x >= 0) { @@ -3616,23 +4061,45 @@ gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event) } static void -gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area) +gtk_text_view_paint (GtkWidget *widget, + GdkRectangle *area, + GdkEventExpose *event) { GtkTextView *text_view; - + GList *child_exposes; + GList *tmp_list; + GdkRegion *updates; + text_view = GTK_TEXT_VIEW (widget); g_return_if_fail (text_view->layout != NULL); g_return_if_fail (text_view->xoffset >= 0); g_return_if_fail (text_view->yoffset >= 0); - - DV (g_print (G_STRLOC": first_validate_idle: %d\n", - text_view->first_validate_idle)); + + while (text_view->first_validate_idle != 0) + { + DV (g_print (G_STRLOC": first_validate_idle: %d\n", + text_view->first_validate_idle)); + gtk_text_view_flush_first_validate (text_view); + } + + /* More regions could have become invalid in the above loop */ + updates = gdk_window_get_update_area (text_view->text_window->bin_window); + if (updates) + { + GdkRectangle rect; + + gdk_region_get_clipbox (updates, &rect); + + gdk_rectangle_union (area, &rect, area); + + gdk_region_destroy (updates); + } if (!text_view->onscreen_validated) { - G_BREAKPOINT (); - g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen"); + g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen - may be a text widget bug."); + g_assert_not_reached (); } #if 0 @@ -3640,45 +4107,68 @@ gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area) area->x, area->y, area->width, area->height); #endif - + + child_exposes = NULL; gtk_text_layout_draw (text_view->layout, widget, text_view->text_window->bin_window, - text_view->cursor_gc, + NULL, text_view->xoffset, text_view->yoffset, area->x, area->y, - area->width, area->height); + area->width, area->height, + &child_exposes); + + tmp_list = child_exposes; + while (tmp_list != NULL) + { + GtkWidget *child = tmp_list->data; + + gtk_container_propagate_expose (GTK_CONTAINER (text_view), + child, + event); + + g_object_unref (child); + + tmp_list = tmp_list->next; + } + + g_list_free (child_exposes); } static gint gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event) { -#if 0 - { - GdkWindow *win = event->window; - GdkColor color = { 0, 0, 0, 65535 }; - GdkGC *gc = gdk_gc_new (win); - gdk_gc_set_rgb_fg_color (gc, &color); - gdk_draw_rectangle (win, - gc, TRUE, - event->area.x, event->area.y, - event->area.width, event->area.height); - gdk_gc_unref (gc); - } -#endif + GSList *tmp_list; if (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT)) { DV(g_print (">Exposed ("G_STRLOC")\n")); - gtk_text_view_paint (widget, &event->area); + gtk_text_view_paint (widget, &event->area, event); } if (event->window == widget->window) gtk_text_view_draw_focus (widget); - return TRUE; + /* Propagate exposes to all children not in the buffer. */ + tmp_list = GTK_TEXT_VIEW (widget)->children; + while (tmp_list != NULL) + { + GtkTextViewChild *vc = tmp_list->data; + + /* propagate_expose checks that event->window matches + * child->window + */ + if (vc->type != GTK_TEXT_WINDOW_TEXT) + gtk_container_propagate_expose (GTK_CONTAINER (widget), + vc->widget, + event); + + tmp_list = tmp_list->next; + } + + return FALSE; } static void @@ -3687,17 +4177,19 @@ gtk_text_view_draw_focus (GtkWidget *widget) gboolean interior_focus; /* We clear the focus if we are in interior focus mode. */ - gtk_widget_style_get (widget, "interior_focus", &interior_focus, NULL); + gtk_widget_style_get (widget, + "interior_focus", &interior_focus, + NULL); if (GTK_WIDGET_DRAWABLE (widget)) { if (GTK_WIDGET_HAS_FOCUS (widget) && !interior_focus) { - gtk_paint_focus (widget->style, widget->window, + gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget), NULL, widget, "textview", 0, 0, - widget->allocation.width - 1, - widget->allocation.height - 1); + widget->allocation.width, + widget->allocation.height); } else { @@ -3721,6 +4213,37 @@ gtk_text_view_grab_focus (GtkWidget *widget) "insert")); } +static gboolean +gtk_text_view_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkTextView *text_view; + GtkContainer *container; + gboolean result; + + text_view = GTK_TEXT_VIEW (widget); + container = GTK_CONTAINER (widget); + + if (!gtk_widget_is_focus (widget) && + container->focus_child == NULL) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + else + { + /* + * Unset CAN_FOCUS flag so that gtk_container_focus() allows + * children to get the focus + */ + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + result = GTK_WIDGET_CLASS (parent_class)->focus (widget, direction); + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + + return result; + } +} + /* * Container */ @@ -3783,13 +4306,15 @@ gtk_text_view_forall (GtkContainer *container, { GSList *iter; GtkTextView *text_view; + GSList *copy; g_return_if_fail (GTK_IS_TEXT_VIEW (container)); g_return_if_fail (callback != NULL); text_view = GTK_TEXT_VIEW (container); - iter = text_view->children; + copy = g_slist_copy (text_view->children); + iter = copy; while (iter != NULL) { @@ -3799,6 +4324,8 @@ gtk_text_view_forall (GtkContainer *container, iter = g_slist_next (iter); } + + g_slist_free (copy); } #define CURSOR_ON_MULTIPLIER 0.66 @@ -3954,10 +4481,29 @@ gtk_text_view_move_iter_by_lines (GtkTextView *text_view, } static void -gtk_text_view_move_cursor (GtkTextView *text_view, - GtkMovementStep step, - gint count, - gboolean extend_selection) +move_cursor (GtkTextView *text_view, + const GtkTextIter *new_location, + gboolean extend_selection) +{ + if (extend_selection) + gtk_text_buffer_move_mark_by_name (get_buffer (text_view), + "insert", + new_location); + else + gtk_text_buffer_place_cursor (get_buffer (text_view), + new_location); +} + +/* FIXME when we are unfrozen and can change GtkMovementStep, + * fix this + */ +#define PAGE_HORIZONTALLY_HACK_VALUE 57 + +static void +gtk_text_view_move_cursor_internal (GtkTextView *text_view, + GtkMovementStep step, + gint count, + gboolean extend_selection) { GtkTextIter insert; GtkTextIter newplace; @@ -3968,7 +4514,13 @@ gtk_text_view_move_cursor (GtkTextView *text_view, if (step == GTK_MOVEMENT_PAGES) { - gtk_text_view_scroll_pages (text_view, count); + gtk_text_view_scroll_pages (text_view, count, extend_selection); + gtk_text_view_pend_cursor_blink (text_view); + return; + } + else if (step == PAGE_HORIZONTALLY_HACK_VALUE) + { + gtk_text_view_scroll_hpages (text_view, count, extend_selection); gtk_text_view_pend_cursor_blink (text_view); return; } @@ -4015,15 +4567,40 @@ gtk_text_view_move_cursor (GtkTextView *text_view, break; case GTK_MOVEMENT_PARAGRAPHS: - gtk_text_iter_forward_lines (&newplace, count); - gtk_text_iter_set_line_offset (&newplace, 0); + if (count > 0) + { + if (!gtk_text_iter_ends_line (&newplace)) + { + gtk_text_iter_forward_to_line_end (&newplace); + --count; + } + } + else if (count < 0) + { + if (gtk_text_iter_get_line_offset (&newplace) > 0) + { + gtk_text_iter_set_line_offset (&newplace, 0); + ++count; + } + } + + if (count != 0) + { + gtk_text_iter_forward_lines (&newplace, count); + gtk_text_iter_set_line_offset (&newplace, 0); + } break; case GTK_MOVEMENT_PARAGRAPH_ENDS: if (count > 0) - gtk_text_iter_forward_to_line_end (&newplace); + { + if (!gtk_text_iter_ends_line (&newplace)) + gtk_text_iter_forward_to_line_end (&newplace); + } else if (count < 0) - gtk_text_iter_set_line_offset (&newplace, 0); + { + gtk_text_iter_set_line_offset (&newplace, 0); + } break; case GTK_MOVEMENT_BUFFER_ENDS: @@ -4032,20 +4609,14 @@ gtk_text_view_move_cursor (GtkTextView *text_view, else if (count < 0) gtk_text_buffer_get_iter_at_offset (get_buffer (text_view), &newplace, 0); break; - + default: break; } if (!gtk_text_iter_equal (&insert, &newplace)) { - if (extend_selection) - gtk_text_buffer_move_mark (get_buffer (text_view), - gtk_text_buffer_get_mark (get_buffer (text_view), - "insert"), - &newplace); - else - gtk_text_buffer_place_cursor (get_buffer (text_view), &newplace); + move_cursor (text_view, &newplace, extend_selection); DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, @@ -4061,6 +4632,24 @@ gtk_text_view_move_cursor (GtkTextView *text_view, gtk_text_view_pend_cursor_blink (text_view); } +static void +gtk_text_view_move_cursor (GtkTextView *text_view, + GtkMovementStep step, + gint count, + gboolean extend_selection) +{ + gtk_text_view_move_cursor_internal (text_view, step, count, extend_selection); +} + +static void +gtk_text_view_page_horizontally (GtkTextView *text_view, + gint count, + gboolean extend_selection) +{ + gtk_text_view_move_cursor_internal (text_view, PAGE_HORIZONTALLY_HACK_VALUE, + count, extend_selection); +} + static void gtk_text_view_set_anchor (GtkTextView *text_view) { @@ -4075,9 +4664,11 @@ gtk_text_view_set_anchor (GtkTextView *text_view) static void gtk_text_view_scroll_pages (GtkTextView *text_view, - gint count) + gint count, + gboolean extend_selection) { gdouble newval; + gdouble oldval; GtkAdjustment *adj; gint cursor_x_pos, cursor_y_pos; GtkTextIter new_insert; @@ -4086,6 +4677,8 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, g_return_if_fail (text_view->vadjustment != NULL); + cancel_pending_scroll (text_view); + adj = text_view->vadjustment; /* Validate the region that will be brought into view by the cursor motion @@ -4103,24 +4696,113 @@ gtk_text_view_scroll_pages (GtkTextView *text_view, y1 = 0; } - gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1); - /* FIXME do we need to update the adjustment ranges here? */ + gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1); + /* FIXME do we need to update the adjustment ranges here? */ + + if (count < 0 && adj->value <= (adj->lower + 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)) + { + /* already at bottom, just be sure we are at the end */ + gtk_text_buffer_get_end_iter (get_buffer (text_view), &new_insert); + move_cursor (text_view, &new_insert, extend_selection); + } + else + { + gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos); + + newval = adj->value; + oldval = adj->value; + + newval += count * adj->page_increment; + + set_adjustment_clamped (adj, newval); + cursor_y_pos += adj->value - oldval; + + gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos); + clamp_iter_onscreen (text_view, &new_insert); + move_cursor (text_view, &new_insert, extend_selection); + + gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos); + } + + /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen + * only guarantees 1 pixel onscreen. + */ + DV(g_print (G_STRLOC": scrolling onscreen\n")); + gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_buffer_get_mark (get_buffer (text_view), + "insert")); +} + +static void +gtk_text_view_scroll_hpages (GtkTextView *text_view, + gint count, + gboolean extend_selection) +{ + gdouble newval; + gdouble oldval; + GtkAdjustment *adj; + gint cursor_x_pos, cursor_y_pos; + GtkTextIter new_insert; + gint y, height; + + g_return_if_fail (text_view->hadjustment != NULL); + + cancel_pending_scroll (text_view); + + adj = text_view->hadjustment; + + /* Validate the line that we're moving within. + */ + gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), + &new_insert, + gtk_text_buffer_get_mark (get_buffer (text_view), "insert")); + gtk_text_layout_get_line_yrange (text_view->layout, &new_insert, &y, &height); + gtk_text_layout_validate_yrange (text_view->layout, &new_insert, y, y + height); + /* FIXME do we need to update the adjustment ranges here? */ + + if (count < 0 && adj->value <= (adj->lower + 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)) + { + /* already at far right, just be sure we are at the end */ + gtk_text_iter_forward_to_line_end (&new_insert); + move_cursor (text_view, &new_insert, extend_selection); + } + else + { + gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos); + + newval = adj->value; + oldval = adj->value; - gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos); - - newval = adj->value; + newval += count * adj->page_increment; - newval += count * adj->page_increment; + set_adjustment_clamped (adj, newval); + cursor_x_pos += adj->value - oldval; - cursor_y_pos += newval - adj->value; - set_adjustment_clamped (adj, newval); + gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos); + clamp_iter_onscreen (text_view, &new_insert); + move_cursor (text_view, &new_insert, extend_selection); - gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos); - clamp_iter_onscreen (text_view, &new_insert); - gtk_text_buffer_place_cursor (get_buffer (text_view), &new_insert); - - gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos); + gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos); + } + /* FIXME for lines shorter than the overall widget width, this results in a + * "bounce" effect as we scroll to the right of the widget, then scroll + * back to get the end of the line onscreen. + * http://bugzilla.gnome.org/show_bug.cgi?id=68963 + */ + /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen * only guarantees 1 pixel onscreen. */ @@ -4291,8 +4973,11 @@ gtk_text_view_delete_from_cursor (GtkTextView *text_view, static void gtk_text_view_cut_clipboard (GtkTextView *text_view) { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_CLIPBOARD); + gtk_text_buffer_cut_clipboard (get_buffer (text_view), - gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + clipboard, text_view->editable); DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, @@ -4303,8 +4988,11 @@ gtk_text_view_cut_clipboard (GtkTextView *text_view) static void gtk_text_view_copy_clipboard (GtkTextView *text_view) { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_CLIPBOARD); + gtk_text_buffer_copy_clipboard (get_buffer (text_view), - gtk_clipboard_get (GDK_SELECTION_CLIPBOARD)); + clipboard); DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_mark (get_buffer (text_view), @@ -4314,8 +5002,11 @@ gtk_text_view_copy_clipboard (GtkTextView *text_view) static void gtk_text_view_paste_clipboard (GtkTextView *text_view) { + GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_CLIPBOARD); + gtk_text_buffer_paste_clipboard (get_buffer (text_view), - gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + clipboard, NULL, text_view->editable); DV(g_print (G_STRLOC": scrolling onscreen\n")); @@ -4330,6 +5021,19 @@ gtk_text_view_toggle_overwrite (GtkTextView *text_view) text_view->overwrite_mode = !text_view->overwrite_mode; } +static void +gtk_text_view_move_focus (GtkTextView *text_view, + GtkDirectionType direction_type) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (text_view)); + + if (!GTK_WIDGET_TOPLEVEL (toplevel)) + return; + + /* Propagate to toplevel */ + g_signal_emit_by_name (toplevel, "move_focus", direction_type); +} + /* * Selections */ @@ -4373,6 +5077,7 @@ move_mark_to_pointer_and_scroll (GtkTextView *text_view, GtkTextMark *mark = gtk_text_buffer_get_mark (get_buffer (text_view), 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, @@ -4381,6 +5086,9 @@ move_mark_to_pointer_and_scroll (GtkTextView *text_view, DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, mark); } + + DV (g_print ("first validate idle leaving %s is %d\n", + G_STRLOC, text_view->first_validate_idle)); } static gint @@ -4462,19 +5170,50 @@ gtk_text_view_start_selection_drag (GtkTextView *text_view, GdkEventButton *button) { GtkTextIter newplace; - + GtkTextBuffer *buffer; + g_return_if_fail (text_view->selection_drag_handler == 0); gtk_grab_add (GTK_WIDGET (text_view)); + buffer = get_buffer (text_view); + newplace = *iter; - gtk_text_buffer_place_cursor (get_buffer (text_view), &newplace); + if (button->state & GDK_SHIFT_MASK) + { + /* Extend selection */ + GtkTextIter start, end; + + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + + if (gtk_text_iter_compare (&newplace, &start) <= 0) + { + gtk_text_buffer_move_mark_by_name (buffer, "insert", + &newplace); + + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", + &end); + } + else if (gtk_text_iter_compare (&newplace, &end) >= 0) + { + gtk_text_buffer_move_mark_by_name (buffer, "insert", + &newplace); + + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", + &start); + } + } + else + { + /* Replace selection */ + gtk_text_buffer_place_cursor (buffer, &newplace); + } - text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view), - "motion_notify_event", - GTK_SIGNAL_FUNC (selection_motion_event_handler), - NULL); + text_view->selection_drag_handler = g_signal_connect (text_view, + "motion_notify_event", + G_CALLBACK (selection_motion_event_handler), + NULL); } /* returns whether we were really dragging */ @@ -4484,7 +5223,7 @@ gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event) if (text_view->selection_drag_handler == 0) return FALSE; - gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler); + g_signal_handler_disconnect (text_view, text_view->selection_drag_handler); text_view->selection_drag_handler = 0; if (text_view->scroll_timeout != 0) @@ -4533,10 +5272,15 @@ gtk_text_view_check_keymap_direction (GtkTextView *text_view) "gtk-split-cursor", &split_cursor, NULL); if (split_cursor) - new_dir = GTK_TEXT_DIR_NONE; + { + new_dir = GTK_TEXT_DIR_NONE; + } else - new_dir = (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ? - GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + { + GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (text_view))); + new_dir = (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_LTR) ? + GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; + } if (text_view->layout->cursor_direction != new_dir) gtk_text_layout_set_cursor_direction (text_view->layout, new_dir); @@ -4560,17 +5304,17 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) text_view->layout = gtk_text_layout_new (); - g_signal_connect (G_OBJECT (text_view->layout), + g_signal_connect (text_view->layout, "invalidated", G_CALLBACK (invalidated_handler), text_view); - g_signal_connect (G_OBJECT (text_view->layout), + g_signal_connect (text_view->layout, "changed", G_CALLBACK (changed_handler), text_view); - g_signal_connect (G_OBJECT (text_view->layout), + g_signal_connect (text_view->layout, "allocate_child", G_CALLBACK (gtk_text_view_child_allocated), text_view); @@ -4590,8 +5334,8 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context); - g_object_unref (G_OBJECT (ltr_context)); - g_object_unref (G_OBJECT (rtl_context)); + g_object_unref (ltr_context); + g_object_unref (rtl_context); gtk_text_view_check_keymap_direction (text_view); @@ -4633,6 +5377,8 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) tmp_list = g_slist_next (tmp_list); } + + gtk_text_view_invalidate (text_view); } } @@ -4643,7 +5389,7 @@ gtk_text_view_ensure_layout (GtkTextView *text_view) * Obtains a copy of the default text attributes. These are the * attributes used for text unless a tag overrides them. * You'd typically pass the default attributes in to - * gtk_text_tag_get_attributes() in order to get the + * gtk_text_iter_get_attributes() in order to get the * attributes in effect at a given text position. * * The return value is a copy owned by the caller of this function, @@ -4668,17 +5414,14 @@ gtk_text_view_destroy_layout (GtkTextView *text_view) { GSList *tmp_list; - if (text_view->first_validate_idle) - { - g_source_remove (text_view->first_validate_idle); - text_view->first_validate_idle = 0; - } + gtk_text_view_remove_validate_idles (text_view); - if (text_view->incremental_validate_idle) - { - g_source_remove (text_view->incremental_validate_idle); - text_view->incremental_validate_idle = 0; - } + g_signal_handlers_disconnect_by_func (text_view->layout, + invalidated_handler, + text_view); + g_signal_handlers_disconnect_by_func (text_view->layout, + changed_handler, + text_view); /* Remove layout from all anchored children */ tmp_list = text_view->children; @@ -4698,11 +5441,7 @@ gtk_text_view_destroy_layout (GtkTextView *text_view) gtk_text_view_stop_cursor_blink (text_view); gtk_text_view_end_selection_drag (text_view, NULL); - g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout), - invalidated_handler, text_view); - g_signal_handlers_disconnect_by_func (G_OBJECT (text_view->layout), - changed_handler, text_view); - g_object_unref (G_OBJECT (text_view->layout)); + g_object_unref (text_view->layout); text_view->layout = NULL; } } @@ -4875,7 +5614,8 @@ gtk_text_view_drag_motion (GtkWidget *widget, } else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view), &start, &end) && - gtk_text_iter_in_range (&newplace, &start, &end)) + gtk_text_iter_compare (&newplace, &start) >= 0 && + gtk_text_iter_compare (&newplace, &end) <= 0) { /* We're inside the selection. */ } @@ -5111,37 +5851,41 @@ gtk_text_view_set_scroll_adjustments (GtkTextView *text_view, if (text_view->hadjustment && (text_view->hadjustment != hadj)) { - gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view); - g_object_unref (G_OBJECT (text_view->hadjustment)); + g_signal_handlers_disconnect_by_func (text_view->hadjustment, + gtk_text_view_value_changed, + text_view); + g_object_unref (text_view->hadjustment); } if (text_view->vadjustment && (text_view->vadjustment != vadj)) { - gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view); - g_object_unref (G_OBJECT (text_view->vadjustment)); + g_signal_handlers_disconnect_by_func (text_view->vadjustment, + gtk_text_view_value_changed, + text_view); + g_object_unref (text_view->vadjustment); } if (text_view->hadjustment != hadj) { text_view->hadjustment = hadj; - g_object_ref (G_OBJECT (text_view->hadjustment)); + g_object_ref (text_view->hadjustment); gtk_object_sink (GTK_OBJECT (text_view->hadjustment)); - gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed", - (GtkSignalFunc) gtk_text_view_value_changed, - text_view); + g_signal_connect (text_view->hadjustment, "value_changed", + G_CALLBACK (gtk_text_view_value_changed), + text_view); need_adjust = TRUE; } if (text_view->vadjustment != vadj) { text_view->vadjustment = vadj; - g_object_ref (G_OBJECT (text_view->vadjustment)); + g_object_ref (text_view->vadjustment); gtk_object_sink (GTK_OBJECT (text_view->vadjustment)); - gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed", - (GtkSignalFunc) gtk_text_view_value_changed, - text_view); + g_signal_connect (text_view->vadjustment, "value_changed", + G_CALLBACK (gtk_text_view_value_changed), + text_view); need_adjust = TRUE; } @@ -5149,6 +5893,77 @@ gtk_text_view_set_scroll_adjustments (GtkTextView *text_view, gtk_text_view_value_changed (NULL, text_view); } +/* FIXME this adjust_allocation is a big cut-and-paste from + * GtkCList, needs to be some "official" way to do this + * factored out. + */ +typedef struct +{ + GdkWindow *window; + int dx; + int dy; +} ScrollData; + +/* The window to which widget->window is relative */ +#define ALLOCATION_WINDOW(widget) \ + (GTK_WIDGET_NO_WINDOW (widget) ? \ + (widget)->window : \ + gdk_window_get_parent ((widget)->window)) + +static void +adjust_allocation_recurse (GtkWidget *widget, + gpointer data) +{ + ScrollData *scroll_data = data; + + /* Need to really size allocate instead of just poking + * into widget->allocation if the widget is not realized. + * FIXME someone figure out why this was. + */ + if (!GTK_WIDGET_REALIZED (widget)) + { + if (GTK_WIDGET_VISIBLE (widget)) + { + GdkRectangle tmp_rectangle = widget->allocation; + tmp_rectangle.x += scroll_data->dx; + tmp_rectangle.y += scroll_data->dy; + + gtk_widget_size_allocate (widget, &tmp_rectangle); + } + } + else + { + if (ALLOCATION_WINDOW (widget) == scroll_data->window) + { + widget->allocation.x += scroll_data->dx; + widget->allocation.y += scroll_data->dy; + + if (GTK_IS_CONTAINER (widget)) + gtk_container_forall (GTK_CONTAINER (widget), + adjust_allocation_recurse, + data); + } + } +} + +static void +adjust_allocation (GtkWidget *widget, + int dx, + int dy) +{ + ScrollData scroll_data; + + if (GTK_WIDGET_REALIZED (widget)) + scroll_data.window = ALLOCATION_WINDOW (widget); + else + scroll_data.window = NULL; + + scroll_data.dx = dx; + scroll_data.dy = dy; + + adjust_allocation_recurse (widget, &scroll_data); +} + static void gtk_text_view_value_changed (GtkAdjustment *adj, GtkTextView *text_view) @@ -5157,7 +5972,7 @@ gtk_text_view_value_changed (GtkAdjustment *adj, gint line_top; gint dx = 0; gint dy = 0; - + /* Note that we oddly call this function with adj == NULL * sometimes */ @@ -5188,30 +6003,49 @@ gtk_text_view_value_changed (GtkAdjustment *adj, } } - if (GTK_WIDGET_REALIZED (text_view) && (dx != 0 || dy != 0)) + if (dx != 0 || dy != 0) { - if (dy != 0) - { - if (text_view->left_window) - text_window_scroll (text_view->left_window, 0, dy); - if (text_view->right_window) - text_window_scroll (text_view->right_window, 0, dy); - } + GSList *tmp_list; - if (dx != 0) + if (GTK_WIDGET_REALIZED (text_view)) { - if (text_view->top_window) - text_window_scroll (text_view->top_window, dx, 0); - if (text_view->bottom_window) - text_window_scroll (text_view->bottom_window, dx, 0); + if (dy != 0) + { + if (text_view->left_window) + text_window_scroll (text_view->left_window, 0, dy); + if (text_view->right_window) + text_window_scroll (text_view->right_window, 0, dy); + } + + if (dx != 0) + { + if (text_view->top_window) + text_window_scroll (text_view->top_window, dx, 0); + if (text_view->bottom_window) + text_window_scroll (text_view->bottom_window, dx, 0); + } + + /* It looks nicer to scroll the main area last, because + * it takes a while, and making the side areas update + * afterward emphasizes the slowness of scrolling the + * main area. + */ + text_window_scroll (text_view->text_window, dx, dy); } - - /* It looks nicer to scroll the main area last, because - * it takes a while, and making the side areas update - * afterward emphasizes the slowness of scrolling the - * main area. + + /* Children are now "moved" in the text window, poke + * into widget->allocation for each child */ - text_window_scroll (text_view->text_window, dx, dy); + tmp_list = text_view->children; + while (tmp_list != NULL) + { + GtkTextViewChild *child = tmp_list->data; + + if (child->anchor) + adjust_allocation (child->widget, dx, dy); + + tmp_list = g_slist_next (tmp_list); + } } /* This could result in invalidation, which would install the @@ -5227,10 +6061,12 @@ gtk_text_view_value_changed (GtkAdjustment *adj, * that, or shouldn't be. */ gtk_text_view_validate_onscreen (text_view); - + /* process exposes */ if (GTK_WIDGET_REALIZED (text_view)) { + DV (g_print ("Processing updates (%s)\n", G_STRLOC)); + if (text_view->left_window) gdk_window_process_updates (text_view->left_window->bin_window, TRUE); @@ -5262,6 +6098,13 @@ static void gtk_text_view_commit_handler (GtkIMContext *context, const gchar *str, GtkTextView *text_view) +{ + gtk_text_view_commit_text (text_view, str); +} + +static void +gtk_text_view_commit_text (GtkTextView *text_view, + const gchar *str) { gboolean had_selection; @@ -5281,7 +6124,16 @@ gtk_text_view_commit_handler (GtkIMContext *context, else { if (!had_selection && text_view->overwrite_mode) - gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1); + { + GtkTextIter insert; + + gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), + &insert, + gtk_text_buffer_get_mark (get_buffer (text_view), + "insert")); + if (!gtk_text_iter_ends_line (&insert)) + gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1); + } gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1, text_view->editable); } @@ -5309,6 +6161,51 @@ gtk_text_view_preedit_changed_handler (GtkIMContext *context, g_free (str); } +static gboolean +gtk_text_view_retrieve_surrounding_handler (GtkIMContext *context, + GtkTextView *text_view) +{ + GtkTextIter start; + GtkTextIter end; + gint pos; + gchar *text; + + gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start, + gtk_text_buffer_get_insert (text_view->buffer)); + end = start; + + pos = gtk_text_iter_get_line_index (&start); + gtk_text_iter_set_line_offset (&start, 0); + gtk_text_iter_forward_to_line_end (&end); + + text = gtk_text_iter_get_slice (&start, &end); + gtk_im_context_set_surrounding (context, text, -1, pos); + g_free (text); + + return TRUE; +} + +static gboolean +gtk_text_view_delete_surrounding_handler (GtkIMContext *context, + gint offset, + gint n_chars, + GtkTextView *text_view) +{ + GtkTextIter start; + GtkTextIter end; + + gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start, + gtk_text_buffer_get_insert (text_view->buffer)); + end = start; + + gtk_text_iter_forward_chars (&start, offset); + gtk_text_iter_forward_chars (&end, offset + n_chars); + + gtk_text_buffer_delete (text_view->buffer, &start, &end); + + return TRUE; +} + static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, const GtkTextIter *location, @@ -5393,7 +6290,7 @@ activate_cb (GtkWidget *menuitem, GtkTextView *text_view) { const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal"); - gtk_signal_emit_by_name (GTK_OBJECT (text_view), signal); + g_signal_emit_by_name (text_view, signal); } static void @@ -5406,8 +6303,8 @@ append_action_signal (GtkTextView *text_view, GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL); g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal); - gtk_signal_connect (GTK_OBJECT (menuitem), "activate", - GTK_SIGNAL_FUNC (activate_cb), text_view); + g_signal_connect (menuitem, "activate", + G_CALLBACK (activate_cb), text_view); gtk_widget_set_sensitive (menuitem, sensitive); @@ -5436,11 +6333,14 @@ popup_position_func (GtkMenu *menu, gint root_x, root_y; GtkTextIter iter; GtkRequisition req; + GdkScreen *screen; text_view = GTK_TEXT_VIEW (user_data); widget = GTK_WIDGET (text_view); g_return_if_fail (GTK_WIDGET_REALIZED (text_view)); + + screen = gtk_widget_get_screen (widget); gdk_window_get_origin (widget->window, &root_x, &root_y); @@ -5481,8 +6381,8 @@ popup_position_func (GtkMenu *menu, *x = CLAMP (*x, root_x, (root_x + widget->allocation.width)); *y = CLAMP (*y, root_y, (root_y + widget->allocation.height)); - *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width)); - *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height)); + *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width)); + *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height)); } typedef struct @@ -5510,6 +6410,15 @@ range_contains_editable_text (const GtkTextIter *start, return FALSE; } +static void +unichar_chosen_func (const char *text, + gpointer data) +{ + GtkTextView *text_view = GTK_TEXT_VIEW (data); + + gtk_text_view_commit_text (text_view, text); +} + static void popup_targets_received (GtkClipboard *clipboard, GtkSelectionData *data, @@ -5562,7 +6471,7 @@ popup_targets_received (GtkClipboard *clipboard, gtk_widget_show (menuitem); gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem); - menuitem = gtk_menu_item_new_with_label (_("Input Methods")); + menuitem = gtk_menu_item_new_with_mnemonic (_("Input _Methods")); gtk_widget_show (menuitem); submenu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); @@ -5570,19 +6479,35 @@ popup_targets_received (GtkClipboard *clipboard, gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context), GTK_MENU_SHELL (submenu)); + + menuitem = gtk_menu_item_new_with_mnemonic (_("_Insert Unicode control character")); + gtk_widget_show (menuitem); + gtk_widget_set_sensitive (menuitem, can_insert); + + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); + gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem); + + _gtk_text_util_append_special_char_menuitems (GTK_MENU_SHELL (submenu), + unichar_chosen_func, + text_view); - gtk_signal_emit (GTK_OBJECT (text_view), - signals[POPULATE_POPUP], - text_view->popup_menu); + g_signal_emit (text_view, + signals[POPULATE_POPUP], + 0, + text_view->popup_menu); if (info->button) gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL, NULL, NULL, info->button, info->time); else - gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL, - popup_position_func, text_view, - 0, gtk_get_current_event_time ()); + { + gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL, + popup_position_func, text_view, + 0, gtk_get_current_event_time ()); + gtk_menu_shell_select_first (GTK_MENU_SHELL (text_view->popup_menu), FALSE); + } } g_object_unref (text_view); @@ -5595,6 +6520,9 @@ gtk_text_view_do_popup (GtkTextView *text_view, { PopupInfo *info = g_new (PopupInfo, 1); + /* should not need this, see http://bugzilla.gnome.org/show_bug.cgi?id=74620 */ + gtk_text_view_end_selection_drag (text_view, event); + /* In order to know what entries we should make sensitive, we * ask for the current targets of the clipboard, and when * we get them, then we actually pop up the menu. @@ -5612,16 +6540,18 @@ gtk_text_view_do_popup (GtkTextView *text_view, info->time = gtk_get_current_event_time (); } - gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_CLIPBOARD), gdk_atom_intern ("TARGETS", FALSE), popup_targets_received, info); } -static void +static gboolean gtk_text_view_popup_menu (GtkWidget *widget) { gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL); + return TRUE; } /* Child GdkWindows */ @@ -5710,9 +6640,10 @@ text_window_realize (GtkTextWindow *win, if (win->type == GTK_TEXT_WINDOW_TEXT) { /* I-beam cursor */ - cursor = gdk_cursor_new (GDK_XTERM); + cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (parent), + GDK_XTERM); gdk_window_set_cursor (win->bin_window, cursor); - gdk_cursor_destroy (cursor); + gdk_cursor_unref (cursor); gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->im_context, win->window); @@ -5832,7 +6763,7 @@ text_window_invalidate_rect (GtkTextWindow *win, gdk_draw_rectangle (win->bin_window, gc, TRUE, window_rect.x, window_rect.y, window_rect.width, window_rect.height); - gdk_gc_unref (gc); + g_object_unref (gc); } #endif } @@ -5849,13 +6780,6 @@ text_window_get_height (GtkTextWindow *win) return win->allocation.height; } -static void -text_window_get_allocation (GtkTextWindow *win, - GdkRectangle *rect) -{ - *rect = win->allocation; -} - /* Windows */ @@ -5917,11 +6841,14 @@ gtk_text_view_get_window (GtkTextView *text_view, return NULL; break; - default: - g_warning ("%s: Unknown GtkTextWindowType", G_STRLOC); + case GTK_TEXT_WINDOW_PRIVATE: + g_warning ("%s: You can't get GTK_TEXT_WINDOW_PRIVATE, it has \"PRIVATE\" in the name because it is private.", G_GNUC_FUNCTION); return NULL; break; } + + g_warning ("%s: Unknown GtkTextWindowType", G_GNUC_FUNCTION); + return NULL; } /** @@ -5965,29 +6892,17 @@ buffer_to_widget (GtkTextView *text_view, gint buffer_y, gint *window_x, gint *window_y) -{ - gint focus_edge_width; - gboolean interior_focus; - - gtk_widget_style_get (GTK_WIDGET (text_view), "interior_focus", &interior_focus, NULL); - - if (interior_focus) - focus_edge_width = 0; - else - focus_edge_width = 1; - +{ if (window_x) { - *window_x = buffer_x - text_view->xoffset + focus_edge_width; - if (text_view->left_window) - *window_x += text_view->left_window->allocation.width; + *window_x = buffer_x - text_view->xoffset; + *window_x += text_view->text_window->allocation.x; } if (window_y) { - *window_y = buffer_y - text_view->yoffset + focus_edge_width; - if (text_view->top_window) - *window_y += text_view->top_window->allocation.height; + *window_y = buffer_y - text_view->yoffset; + *window_y += text_view->text_window->allocation.y; } } @@ -6034,14 +6949,17 @@ buffer_to_text_window (GtkTextView *text_view, /** * gtk_text_view_buffer_to_window_coords: * @text_view: a #GtkTextView - * @win: a #GtkTextWindowType + * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE * @buffer_x: buffer x coordinate * @buffer_y: buffer y coordinate * @window_x: window x coordinate return location * @window_y: window y coordinate return location * * Converts coordinate (@buffer_x, @buffer_y) to coordinates for the window - * @win, and stores the result in (@window_x, @window_y). + * @win, and stores the result in (@window_x, @window_y). + * + * Note that you can't convert coordinates for a nonexisting window (see + * gtk_text_view_set_border_window_size()). **/ void gtk_text_view_buffer_to_window_coords (GtkTextView *text_view, @@ -6112,29 +7030,17 @@ widget_to_buffer (GtkTextView *text_view, gint widget_y, gint *buffer_x, gint *buffer_y) -{ - gint focus_edge_width; - gboolean interior_focus; - - gtk_widget_style_get (GTK_WIDGET (text_view), "interior_focus", &interior_focus, NULL); - - if (interior_focus) - focus_edge_width = 0; - else - focus_edge_width = 1; - +{ if (buffer_x) { - *buffer_x = widget_x - focus_edge_width + text_view->xoffset; - if (text_view->left_window) - *buffer_x -= text_view->left_window->allocation.width; + *buffer_x = widget_x + text_view->xoffset; + *buffer_x -= text_view->text_window->allocation.x; } if (buffer_y) { - *buffer_y = widget_y - focus_edge_width + text_view->yoffset; - if (text_view->top_window) - *buffer_y -= text_view->top_window->allocation.height; + *buffer_y = widget_y + text_view->yoffset; + *buffer_y -= text_view->text_window->allocation.y; } } @@ -6183,7 +7089,7 @@ text_window_to_buffer (GtkTextView *text_view, /** * gtk_text_view_window_to_buffer_coords: * @text_view: a #GtkTextView - * @win: a #GtkTextWindowType + * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE * @window_x: window x coordinate * @window_y: window y coordinate * @buffer_x: buffer x coordinate return location @@ -6191,6 +7097,9 @@ text_window_to_buffer (GtkTextView *text_view, * * Converts coordinates on the window identified by @win to buffer * coordinates, storing the result in (@buffer_x,@buffer_y). + * + * Note that you can't convert coordinates for a nonexisting window (see + * gtk_text_view_set_border_window_size()). **/ void gtk_text_view_window_to_buffer_coords (GtkTextView *text_view, @@ -6285,6 +7194,8 @@ set_window_width (GtkTextView *text_view, { if ((*winp)->requisition.width == width) return; + + (*winp)->requisition.width = width; } gtk_widget_queue_resize (GTK_WIDGET (text_view)); @@ -6323,6 +7234,8 @@ set_window_height (GtkTextView *text_view, { if ((*winp)->requisition.height == height) return; + + (*winp)->requisition.height = height; } gtk_widget_queue_resize (GTK_WIDGET (text_view)); @@ -6435,14 +7348,15 @@ text_view_child_new_anchored (GtkWidget *child, vc = g_new (GtkTextViewChild, 1); + vc->type = GTK_TEXT_WINDOW_PRIVATE; vc->widget = child; vc->anchor = anchor; vc->from_top_of_line = 0; vc->from_left_of_buffer = 0; - g_object_ref (G_OBJECT (vc->widget)); - g_object_ref (G_OBJECT (vc->anchor)); + g_object_ref (vc->widget); + g_object_ref (vc->anchor); g_object_set_data (G_OBJECT (child), "gtk-text-view-child", @@ -6468,13 +7382,17 @@ text_view_child_new_window (GtkWidget *child, vc->from_top_of_line = 0; vc->from_left_of_buffer = 0; - - g_object_ref (G_OBJECT (vc->widget)); + + g_object_ref (vc->widget); vc->type = type; vc->x = x; vc->y = y; + g_object_set_data (G_OBJECT (child), + "gtk-text-view-child", + vc); + return vc; } @@ -6488,10 +7406,10 @@ text_view_child_free (GtkTextViewChild *child) { gtk_text_child_anchor_unregister_child (child->anchor, child->widget); - g_object_unref (G_OBJECT (child->anchor)); + g_object_unref (child->anchor); } - g_object_unref (G_OBJECT (child->widget)); + g_object_unref (child->widget); g_free (child); } @@ -6525,6 +7443,15 @@ add_child (GtkTextView *text_view, gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view)); } +/** + * gtk_text_view_add_child_at_anchor: + * @text_view: a #GtkTextView + * @child: a #GtkWidget + * @anchor: a #GtkTextChildAnchor in the #GtkTextBuffer for @text_view + * + * Adds a child widget in the text buffer, at the given @anchor. + * + **/ void gtk_text_view_add_child_at_anchor (GtkTextView *text_view, GtkWidget *child, @@ -6543,8 +7470,37 @@ gtk_text_view_add_child_at_anchor (GtkTextView *text_view, text_view->layout); add_child (text_view, vc); + + g_assert (vc->widget == child); + g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view)); } +/** + * gtk_text_view_add_child_in_window: + * @text_view: a #GtkTextView + * @child: a #GtkWidget + * @which_window: which window the child should appear in + * @xpos: X position of child in window coordinates + * @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 + * 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 + * placing a child in #GTK_TEXT_WINDOW_WIDGET, scrolling is + * irrelevant, the child floats above all scrollable areas. But when + * placing a child in one of the scrollable windows (border windows or + * 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. + * + **/ void gtk_text_view_add_child_in_window (GtkTextView *text_view, GtkWidget *child, @@ -6556,16 +7512,27 @@ gtk_text_view_add_child_in_window (GtkTextView *text_view, g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (GTK_IS_WIDGET (child)); - g_return_if_fail (xpos >= 0); - g_return_if_fail (ypos >= 0); g_return_if_fail (child->parent == NULL); vc = text_view_child_new_window (child, which_window, xpos, ypos); add_child (text_view, vc); + + g_assert (vc->widget == child); + g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view)); } +/** + * gtk_text_view_move_child: + * @text_view: a #GtkTextView + * @child: child widget already added to the text view + * @xpos: new X position in window coordinates + * @ypos: new Y position in window coordinates + * + * Updates the position of a child, as for gtk_text_view_add_child_in_window(). + * + **/ void gtk_text_view_move_child (GtkTextView *text_view, GtkWidget *child, @@ -6576,8 +7543,6 @@ gtk_text_view_move_child (GtkTextView *text_view, g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (GTK_IS_WIDGET (child)); - g_return_if_fail (xpos >= 0); - g_return_if_fail (ypos >= 0); g_return_if_fail (child->parent == (GtkWidget*) text_view); vc = g_object_get_data (G_OBJECT (child), @@ -6585,6 +7550,10 @@ gtk_text_view_move_child (GtkTextView *text_view, g_assert (vc != NULL); + if (vc->x == xpos && + vc->y == ypos) + return; + vc->x = xpos; vc->y = ypos; @@ -6593,9 +7562,24 @@ gtk_text_view_move_child (GtkTextView *text_view, } - /* Iterator operations */ +/** + * gtk_text_view_forward_display_line: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * + * Moves the given @iter forward by one display (wrapped) line. A + * display line is different from a paragraph. Paragraphs are + * separated by newlines or other paragraph separator characters. + * Display lines are created by line-wrapping a paragraph. If + * wrapping is turned off, display lines and paragraphs will be the + * same. Display lines are divided differently for each view, since + * they depend on the view's width; paragraphs are the same in all + * views, since they depend on the contents of the #GtkTextBuffer. + * + * Return value: %TRUE if @iter was moved and is not on the end iterator + **/ gboolean gtk_text_view_forward_display_line (GtkTextView *text_view, GtkTextIter *iter) @@ -6608,6 +7592,22 @@ gtk_text_view_forward_display_line (GtkTextView *text_view, return gtk_text_layout_move_iter_to_next_line (text_view->layout, iter); } +/** + * gtk_text_view_backward_display_line: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * + * Moves the given @iter backward by one display (wrapped) line. A + * display line is different from a paragraph. Paragraphs are + * separated by newlines or other paragraph separator characters. + * Display lines are created by line-wrapping a paragraph. If + * wrapping is turned off, display lines and paragraphs will be the + * same. Display lines are divided differently for each view, since + * they depend on the view's width; paragraphs are the same in all + * views, since they depend on the contents of the #GtkTextBuffer. + * + * Return value: %TRUE if @iter was moved and is not on the end iterator + **/ gboolean gtk_text_view_backward_display_line (GtkTextView *text_view, GtkTextIter *iter) @@ -6620,6 +7620,22 @@ gtk_text_view_backward_display_line (GtkTextView *text_view, return gtk_text_layout_move_iter_to_previous_line (text_view->layout, iter); } +/** + * gtk_text_view_forward_display_line_end: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * + * Moves the given @iter forward to the next display line end. A + * display line is different from a paragraph. Paragraphs are + * separated by newlines or other paragraph separator characters. + * Display lines are created by line-wrapping a paragraph. If + * wrapping is turned off, display lines and paragraphs will be the + * same. Display lines are divided differently for each view, since + * they depend on the view's width; paragraphs are the same in all + * views, since they depend on the contents of the #GtkTextBuffer. + * + * Return value: %TRUE if @iter was moved and is not on the end iterator + **/ gboolean gtk_text_view_forward_display_line_end (GtkTextView *text_view, GtkTextIter *iter) @@ -6632,6 +7648,22 @@ gtk_text_view_forward_display_line_end (GtkTextView *text_view, return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, 1); } +/** + * gtk_text_view_backward_display_line_start: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * + * Moves the given @iter backward to the next display line start. A + * display line is different from a paragraph. Paragraphs are + * separated by newlines or other paragraph separator characters. + * Display lines are created by line-wrapping a paragraph. If + * wrapping is turned off, display lines and paragraphs will be the + * same. Display lines are divided differently for each view, since + * they depend on the view's width; paragraphs are the same in all + * views, since they depend on the contents of the #GtkTextBuffer. + * + * Return value: %TRUE if @iter was moved and is not on the end iterator + **/ gboolean gtk_text_view_backward_display_line_start (GtkTextView *text_view, GtkTextIter *iter) @@ -6644,6 +7676,17 @@ gtk_text_view_backward_display_line_start (GtkTextView *text_view, return gtk_text_layout_move_iter_to_line_end (text_view->layout, iter, -1); } +/** + * gtk_text_view_starts_display_line: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * + * Determines whether @iter is at the start of a display line. + * See gtk_text_view_forward_display_line() for an explanation of + * display lines vs. paragraphs. + * + * Return value: %TRUE if @iter begins a wrapped line + **/ gboolean gtk_text_view_starts_display_line (GtkTextView *text_view, const GtkTextIter *iter) @@ -6656,6 +7699,18 @@ gtk_text_view_starts_display_line (GtkTextView *text_view, return gtk_text_layout_iter_starts_line (text_view->layout, iter); } +/** + * gtk_text_view_move_visually: + * @text_view: a #GtkTextView + * @iter: a #GtkTextIter + * @count: number of lines to move + * + * Moves @iter up or down by @count display (wrapped) lines. + * See gtk_text_view_forward_display_line() for an explanation of + * display lines vs. paragraphs. + * + * Return value: %TRUE if @iter moved and is not on the end iterator + **/ gboolean gtk_text_view_move_visually (GtkTextView *text_view, GtkTextIter *iter,