]> Pileus Git - ~andy/gtk/blob - gtk/gtktextview.c
gtk: clean up GtkWidget signals
[~andy/gtk] / gtk / gtktextview.c
1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3  * gtktextview.c Copyright (C) 2000 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27
28 #include "config.h"
29 #include <string.h>
30
31 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
32 #include "gtkbindings.h"
33 #include "gtkdnd.h"
34 #include "gtkimagemenuitem.h"
35 #include "gtkintl.h"
36 #include "gtkmain.h"
37 #include "gtkmarshalers.h"
38 #include "gtkmenu.h"
39 #include "gtkmenuitem.h"
40 #include "gtkseparatormenuitem.h"
41 #include "gtksettings.h"
42 #include "gtkstock.h"
43 #include "gtktextbufferrichtext.h"
44 #include "gtktextdisplay.h"
45 #include "gtktextview.h"
46 #include "gtkimmulticontext.h"
47 #include "gdk/gdkkeysyms.h"
48 #include "gtkprivate.h"
49 #include "gtktextutil.h"
50 #include "gtkwidgetprivate.h"
51 #include "gtkwindow.h"
52
53
54 /* How scrolling, validation, exposes, etc. work.
55  *
56  * The expose_event handler has the invariant that the onscreen lines
57  * have been validated.
58  *
59  * There are two ways that onscreen lines can become invalid. The first
60  * is to change which lines are onscreen. This happens when the value
61  * of a scroll adjustment changes. So the code path begins in
62  * gtk_text_view_value_changed() and goes like this:
63  *   - gdk_window_scroll() to reflect the new adjustment value
64  *   - validate the lines that were moved onscreen
65  *   - gdk_window_process_updates() to handle the exposes immediately
66  *
67  * The second way is that you get the "invalidated" signal from the layout,
68  * indicating that lines have become invalid. This code path begins in
69  * invalidated_handler() and goes like this:
70  *   - install high-priority idle which does the rest of the steps
71  *   - if a scroll is pending from scroll_to_mark(), do the scroll,
72  *     jumping to the gtk_text_view_value_changed() code path
73  *   - otherwise, validate the onscreen lines
74  *   - DO NOT process updates
75  *
76  * In both cases, validating the onscreen lines can trigger a scroll
77  * due to maintaining the first_para on the top of the screen.
78  * If validation triggers a scroll, we jump to the top of the code path
79  * for value_changed, and bail out of the current code path.
80  *
81  * Also, in size_allocate, if we invalidate some lines from changing
82  * the layout width, we need to go ahead and run the high-priority idle,
83  * because GTK sends exposes right after doing the size allocates without
84  * returning to the main loop. This is also why the high-priority idle
85  * is at a higher priority than resizing.
86  *
87  */
88
89 #if 0
90 #define DEBUG_VALIDATION_AND_SCROLLING
91 #endif
92
93 #ifdef DEBUG_VALIDATION_AND_SCROLLING
94 #define DV(x) (x)
95 #else
96 #define DV(x)
97 #endif
98
99 #define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->priv->text_window)
100 #define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->priv->text_window)
101
102 #define SPACE_FOR_CURSOR 1
103
104 #define GTK_TEXT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TEXT_VIEW, GtkTextViewPrivate))
105
106 typedef struct _GtkTextWindow GtkTextWindow;
107 typedef struct _GtkTextPendingScroll GtkTextPendingScroll;
108
109 struct _GtkTextViewPrivate 
110 {
111   GtkTextLayout *layout;
112   GtkTextBuffer *buffer;
113
114   guint blink_time;  /* time in msec the cursor has blinked since last user event */
115   guint im_spot_idle;
116   gchar *im_module;
117   GdkDevice *grab_device;
118   GdkDevice *dnd_device;
119
120   guint selection_drag_handler;
121   guint scroll_timeout;
122
123   GtkTextWindow *text_window;
124   GtkTextWindow *left_window;
125   GtkTextWindow *right_window;
126   GtkTextWindow *top_window;
127   GtkTextWindow *bottom_window;
128
129   GtkAdjustment *hadjustment;
130   GtkAdjustment *vadjustment;
131
132   gint xoffset;         /* Offsets between widget coordinates and buffer coordinates */
133   gint yoffset;
134   gint width;           /* Width and height of the buffer */
135   gint height;
136
137   /* This is used to monitor the overall size request 
138    * and decide whether we need to queue resizes when
139    * the buffer content changes. 
140    *
141    * FIXME: This could be done in a simpler way by 
142    * consulting the above width/height of the buffer + some
143    * padding values, however all of this request code needs
144    * to be changed to use GtkWidget     Iface and deserves
145    * more attention.
146    */
147   GtkRequisition cached_size_request;
148
149   /* The virtual cursor position is normally the same as the
150    * actual (strong) cursor position, except in two circumstances:
151    *
152    * a) When the cursor is moved vertically with the keyboard
153    * b) When the text view is scrolled with the keyboard
154    *
155    * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y
156    * In case b), both virtual_cursor_x and virtual_cursor_y are preserved.
157    */
158   gint virtual_cursor_x;   /* -1 means use actual cursor position */
159   gint virtual_cursor_y;   /* -1 means use actual cursor position */
160
161   GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
162   gint first_para_pixels;       /* Offset of top of screen in the first onscreen paragraph */
163
164   GtkTextMark *dnd_mark;
165   guint blink_timeout;
166
167   guint first_validate_idle;        /* Idle to revalidate onscreen portion, runs before resize */
168   guint incremental_validate_idle;  /* Idle to revalidate offscreen portions, runs after redraw */
169
170   GtkIMContext *im_context;
171   GtkWidget *popup_menu;
172
173   gint drag_start_x;
174   gint drag_start_y;
175
176   GSList *children;
177
178   GtkTextPendingScroll *pending_scroll;
179
180   gint pending_place_cursor_button;
181
182   /* Default style settings */
183   gint pixels_above_lines;
184   gint pixels_below_lines;
185   gint pixels_inside_wrap;
186   GtkWrapMode wrap_mode;
187   GtkJustification justify;
188   gint left_margin;
189   gint right_margin;
190   gint indent;
191   PangoTabArray *tabs;
192   guint editable : 1;
193
194   guint overwrite_mode : 1;
195   guint cursor_visible : 1;
196
197   /* if we have reset the IM since the last character entered */  
198   guint need_im_reset : 1;
199
200   guint accepts_tab : 1;
201
202   guint width_changed : 1;
203
204   /* debug flag - means that we've validated onscreen since the
205    * last "invalidate" signal from the layout
206    */
207   guint onscreen_validated : 1;
208
209   guint mouse_cursor_obscured : 1;
210
211   guint scroll_after_paste : 1;
212 };
213
214 struct _GtkTextPendingScroll
215 {
216   GtkTextMark   *mark;
217   gdouble        within_margin;
218   gboolean       use_align;
219   gdouble        xalign;
220   gdouble        yalign;
221 };
222
223 enum
224 {
225   SET_SCROLL_ADJUSTMENTS,
226   POPULATE_POPUP,
227   MOVE_CURSOR,
228   PAGE_HORIZONTALLY,
229   SET_ANCHOR,
230   INSERT_AT_CURSOR,
231   DELETE_FROM_CURSOR,
232   BACKSPACE,
233   CUT_CLIPBOARD,
234   COPY_CLIPBOARD,
235   PASTE_CLIPBOARD,
236   TOGGLE_OVERWRITE,
237   MOVE_VIEWPORT,
238   SELECT_ALL,
239   TOGGLE_CURSOR_VISIBLE,
240   PREEDIT_CHANGED,
241   LAST_SIGNAL
242 };
243
244 enum
245 {
246   PROP_0,
247   PROP_PIXELS_ABOVE_LINES,
248   PROP_PIXELS_BELOW_LINES,
249   PROP_PIXELS_INSIDE_WRAP,
250   PROP_EDITABLE,
251   PROP_WRAP_MODE,
252   PROP_JUSTIFICATION,
253   PROP_LEFT_MARGIN,
254   PROP_RIGHT_MARGIN,
255   PROP_INDENT,
256   PROP_TABS,
257   PROP_CURSOR_VISIBLE,
258   PROP_BUFFER,
259   PROP_OVERWRITE,
260   PROP_ACCEPTS_TAB,
261   PROP_IM_MODULE
262 };
263
264 static void gtk_text_view_finalize             (GObject          *object);
265 static void gtk_text_view_set_property         (GObject         *object,
266                                                 guint            prop_id,
267                                                 const GValue    *value,
268                                                 GParamSpec      *pspec);
269 static void gtk_text_view_get_property         (GObject         *object,
270                                                 guint            prop_id,
271                                                 GValue          *value,
272                                                 GParamSpec      *pspec);
273 static void gtk_text_view_destroy              (GtkWidget        *widget);
274 static void gtk_text_view_size_request         (GtkWidget        *widget,
275                                                 GtkRequisition   *requisition);
276 static void gtk_text_view_size_allocate        (GtkWidget        *widget,
277                                                 GtkAllocation    *allocation);
278 static void gtk_text_view_realize              (GtkWidget        *widget);
279 static void gtk_text_view_unrealize            (GtkWidget        *widget);
280 static void gtk_text_view_style_set            (GtkWidget        *widget,
281                                                 GtkStyle         *previous_style);
282 static void gtk_text_view_direction_changed    (GtkWidget        *widget,
283                                                 GtkTextDirection  previous_direction);
284 static void gtk_text_view_grab_notify          (GtkWidget        *widget,
285                                                 gboolean         was_grabbed);
286 static void gtk_text_view_state_changed        (GtkWidget        *widget,
287                                                 GtkStateType      previous_state);
288
289 static gint gtk_text_view_event                (GtkWidget        *widget,
290                                                 GdkEvent         *event);
291 static gint gtk_text_view_key_press_event      (GtkWidget        *widget,
292                                                 GdkEventKey      *event);
293 static gint gtk_text_view_key_release_event    (GtkWidget        *widget,
294                                                 GdkEventKey      *event);
295 static gint gtk_text_view_button_press_event   (GtkWidget        *widget,
296                                                 GdkEventButton   *event);
297 static gint gtk_text_view_button_release_event (GtkWidget        *widget,
298                                                 GdkEventButton   *event);
299 static gint gtk_text_view_focus_in_event       (GtkWidget        *widget,
300                                                 GdkEventFocus    *event);
301 static gint gtk_text_view_focus_out_event      (GtkWidget        *widget,
302                                                 GdkEventFocus    *event);
303 static gint gtk_text_view_motion_event         (GtkWidget        *widget,
304                                                 GdkEventMotion   *event);
305 static gint gtk_text_view_draw                 (GtkWidget        *widget,
306                                                 cairo_t          *cr);
307 static void gtk_text_view_draw_focus           (GtkWidget        *widget,
308                                                 cairo_t          *cr);
309 static gboolean gtk_text_view_focus            (GtkWidget        *widget,
310                                                 GtkDirectionType  direction);
311 static void gtk_text_view_select_all           (GtkWidget        *widget,
312                                                 gboolean          select);
313
314
315 /* Source side drag signals */
316 static void gtk_text_view_drag_begin       (GtkWidget        *widget,
317                                             GdkDragContext   *context);
318 static void gtk_text_view_drag_end         (GtkWidget        *widget,
319                                             GdkDragContext   *context);
320 static void gtk_text_view_drag_data_get    (GtkWidget        *widget,
321                                             GdkDragContext   *context,
322                                             GtkSelectionData *selection_data,
323                                             guint             info,
324                                             guint             time);
325 static void gtk_text_view_drag_data_delete (GtkWidget        *widget,
326                                             GdkDragContext   *context);
327
328 /* Target side drag signals */
329 static void     gtk_text_view_drag_leave         (GtkWidget        *widget,
330                                                   GdkDragContext   *context,
331                                                   guint             time);
332 static gboolean gtk_text_view_drag_motion        (GtkWidget        *widget,
333                                                   GdkDragContext   *context,
334                                                   gint              x,
335                                                   gint              y,
336                                                   guint             time);
337 static gboolean gtk_text_view_drag_drop          (GtkWidget        *widget,
338                                                   GdkDragContext   *context,
339                                                   gint              x,
340                                                   gint              y,
341                                                   guint             time);
342 static void     gtk_text_view_drag_data_received (GtkWidget        *widget,
343                                                   GdkDragContext   *context,
344                                                   gint              x,
345                                                   gint              y,
346                                                   GtkSelectionData *selection_data,
347                                                   guint             info,
348                                                   guint             time);
349
350 static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
351                                                   GtkAdjustment *hadj,
352                                                   GtkAdjustment *vadj);
353 static gboolean gtk_text_view_popup_menu         (GtkWidget     *widget);
354
355 static void gtk_text_view_move_cursor       (GtkTextView           *text_view,
356                                              GtkMovementStep        step,
357                                              gint                   count,
358                                              gboolean               extend_selection);
359 static gboolean gtk_text_view_move_viewport (GtkTextView           *text_view,
360                                              GtkScrollStep          step,
361                                              gint                   count);
362 static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
363 static gboolean gtk_text_view_scroll_pages (GtkTextView           *text_view,
364                                             gint                   count,
365                                             gboolean               extend_selection);
366 static gboolean gtk_text_view_scroll_hpages(GtkTextView           *text_view,
367                                             gint                   count,
368                                             gboolean               extend_selection);
369 static void gtk_text_view_insert_at_cursor (GtkTextView           *text_view,
370                                             const gchar           *str);
371 static void gtk_text_view_delete_from_cursor (GtkTextView           *text_view,
372                                               GtkDeleteType          type,
373                                               gint                   count);
374 static void gtk_text_view_backspace        (GtkTextView           *text_view);
375 static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
376 static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
377 static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
378 static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
379 static void gtk_text_view_toggle_cursor_visible (GtkTextView      *text_view);
380
381 static void gtk_text_view_unselect         (GtkTextView           *text_view);
382
383 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
384 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
385                                                      GtkTextIter        *iter);
386 static void     gtk_text_view_update_layout_width       (GtkTextView        *text_view);
387 static void     gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
388                                                          GtkTextAttributes *values,
389                                                          GtkStyle           *style);
390 static void     gtk_text_view_ensure_layout          (GtkTextView        *text_view);
391 static void     gtk_text_view_destroy_layout         (GtkTextView        *text_view);
392 static void     gtk_text_view_check_keymap_direction (GtkTextView        *text_view);
393 static void     gtk_text_view_start_selection_drag   (GtkTextView        *text_view,
394                                                       const GtkTextIter  *iter,
395                                                       GdkEventButton     *event);
396 static gboolean gtk_text_view_end_selection_drag     (GtkTextView        *text_view);
397 static void     gtk_text_view_start_selection_dnd    (GtkTextView        *text_view,
398                                                       const GtkTextIter  *iter,
399                                                       GdkEventMotion     *event);
400 static void     gtk_text_view_check_cursor_blink     (GtkTextView        *text_view);
401 static void     gtk_text_view_pend_cursor_blink      (GtkTextView        *text_view);
402 static void     gtk_text_view_stop_cursor_blink      (GtkTextView        *text_view);
403 static void     gtk_text_view_reset_blink_time       (GtkTextView        *text_view);
404
405 static void     gtk_text_view_value_changed                (GtkAdjustment *adj,
406                                                             GtkTextView   *view);
407 static void     gtk_text_view_commit_handler               (GtkIMContext  *context,
408                                                             const gchar   *str,
409                                                             GtkTextView   *text_view);
410 static void     gtk_text_view_commit_text                  (GtkTextView   *text_view,
411                                                             const gchar   *text);
412 static void     gtk_text_view_preedit_changed_handler      (GtkIMContext  *context,
413                                                             GtkTextView   *text_view);
414 static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
415                                                             GtkTextView   *text_view);
416 static gboolean gtk_text_view_delete_surrounding_handler   (GtkIMContext  *context,
417                                                             gint           offset,
418                                                             gint           n_chars,
419                                                             GtkTextView   *text_view);
420
421 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
422                                                   const GtkTextIter *location,
423                                                   GtkTextMark       *mark,
424                                                   gpointer           data);
425 static void gtk_text_view_target_list_notify     (GtkTextBuffer     *buffer,
426                                                   const GParamSpec  *pspec,
427                                                   gpointer           data);
428 static void gtk_text_view_paste_done_handler     (GtkTextBuffer     *buffer,
429                                                   GtkClipboard      *clipboard,
430                                                   gpointer           data);
431 static void gtk_text_view_get_cursor_location    (GtkTextView       *text_view,
432                                                   GdkRectangle      *pos);
433 static void gtk_text_view_get_virtual_cursor_pos (GtkTextView       *text_view,
434                                                   GtkTextIter       *cursor,
435                                                   gint              *x,
436                                                   gint              *y);
437 static void gtk_text_view_set_virtual_cursor_pos (GtkTextView       *text_view,
438                                                   gint               x,
439                                                   gint               y);
440
441 static GtkAdjustment* get_hadjustment            (GtkTextView       *text_view);
442 static GtkAdjustment* get_vadjustment            (GtkTextView       *text_view);
443
444 static void gtk_text_view_do_popup               (GtkTextView       *text_view,
445                                                   GdkEventButton    *event);
446
447 static void cancel_pending_scroll                (GtkTextView   *text_view);
448 static void gtk_text_view_queue_scroll           (GtkTextView   *text_view,
449                                                   GtkTextMark   *mark,
450                                                   gdouble        within_margin,
451                                                   gboolean       use_align,
452                                                   gdouble        xalign,
453                                                   gdouble        yalign);
454
455 static gboolean gtk_text_view_flush_scroll         (GtkTextView *text_view);
456 static void     gtk_text_view_update_adjustments   (GtkTextView *text_view);
457 static void     gtk_text_view_invalidate           (GtkTextView *text_view);
458 static void     gtk_text_view_flush_first_validate (GtkTextView *text_view);
459
460 static void gtk_text_view_update_im_spot_location (GtkTextView *text_view);
461
462 /* Container methods */
463 static void gtk_text_view_add    (GtkContainer *container,
464                                   GtkWidget    *child);
465 static void gtk_text_view_remove (GtkContainer *container,
466                                   GtkWidget    *child);
467 static void gtk_text_view_forall (GtkContainer *container,
468                                   gboolean      include_internals,
469                                   GtkCallback   callback,
470                                   gpointer      callback_data);
471
472 /* FIXME probably need the focus methods. */
473
474 typedef struct _GtkTextViewChild GtkTextViewChild;
475
476 struct _GtkTextViewChild
477 {
478   GtkWidget *widget;
479
480   GtkTextChildAnchor *anchor;
481
482   gint from_top_of_line;
483   gint from_left_of_buffer;
484   
485   /* These are ignored if anchor != NULL */
486   GtkTextWindowType type;
487   gint x;
488   gint y;
489 };
490
491 static GtkTextViewChild* text_view_child_new_anchored      (GtkWidget          *child,
492                                                             GtkTextChildAnchor *anchor,
493                                                             GtkTextLayout      *layout);
494 static GtkTextViewChild* text_view_child_new_window        (GtkWidget          *child,
495                                                             GtkTextWindowType   type,
496                                                             gint                x,
497                                                             gint                y);
498 static void              text_view_child_free              (GtkTextViewChild   *child);
499 static void              text_view_child_set_parent_window (GtkTextView        *text_view,
500                                                             GtkTextViewChild   *child);
501
502 struct _GtkTextWindow
503 {
504   GtkTextWindowType type;
505   GtkWidget *widget;
506   GdkWindow *window;
507   GdkWindow *bin_window;
508   GtkRequisition requisition;
509   GdkRectangle allocation;
510 };
511
512 static GtkTextWindow *text_window_new             (GtkTextWindowType  type,
513                                                    GtkWidget         *widget,
514                                                    gint               width_request,
515                                                    gint               height_request);
516 static void           text_window_free            (GtkTextWindow     *win);
517 static void           text_window_realize         (GtkTextWindow     *win,
518                                                    GtkWidget         *widget);
519 static void           text_window_unrealize       (GtkTextWindow     *win);
520 static void           text_window_size_allocate   (GtkTextWindow     *win,
521                                                    GdkRectangle      *rect);
522 static void           text_window_scroll          (GtkTextWindow     *win,
523                                                    gint               dx,
524                                                    gint               dy);
525 static void           text_window_invalidate_rect (GtkTextWindow     *win,
526                                                    GdkRectangle      *rect);
527 static void           text_window_invalidate_cursors (GtkTextWindow  *win);
528
529 static gint           text_window_get_width       (GtkTextWindow     *win);
530 static gint           text_window_get_height      (GtkTextWindow     *win);
531
532
533 static guint signals[LAST_SIGNAL] = { 0 };
534
535 G_DEFINE_TYPE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER)
536
537 static void
538 add_move_binding (GtkBindingSet  *binding_set,
539                   guint           keyval,
540                   guint           modmask,
541                   GtkMovementStep step,
542                   gint            count)
543 {
544   g_assert ((modmask & GDK_SHIFT_MASK) == 0);
545
546   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
547                                 "move-cursor", 3,
548                                 G_TYPE_ENUM, step,
549                                 G_TYPE_INT, count,
550                                 G_TYPE_BOOLEAN, FALSE);
551
552   /* Selection-extending version */
553   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
554                                 "move-cursor", 3,
555                                 G_TYPE_ENUM, step,
556                                 G_TYPE_INT, count,
557                                 G_TYPE_BOOLEAN, TRUE);
558 }
559
560 static void
561 gtk_text_view_class_init (GtkTextViewClass *klass)
562 {
563   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
564   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
565   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
566   GtkBindingSet *binding_set;
567
568   /* Default handlers and virtual methods
569    */
570   gobject_class->set_property = gtk_text_view_set_property;
571   gobject_class->get_property = gtk_text_view_get_property;
572   gobject_class->finalize = gtk_text_view_finalize;
573
574   widget_class->destroy = gtk_text_view_destroy;
575   widget_class->realize = gtk_text_view_realize;
576   widget_class->unrealize = gtk_text_view_unrealize;
577   widget_class->style_set = gtk_text_view_style_set;
578   widget_class->direction_changed = gtk_text_view_direction_changed;
579   widget_class->grab_notify = gtk_text_view_grab_notify;
580   widget_class->state_changed = gtk_text_view_state_changed;
581   widget_class->size_request = gtk_text_view_size_request;
582   widget_class->size_allocate = gtk_text_view_size_allocate;
583   widget_class->event = gtk_text_view_event;
584   widget_class->key_press_event = gtk_text_view_key_press_event;
585   widget_class->key_release_event = gtk_text_view_key_release_event;
586   widget_class->button_press_event = gtk_text_view_button_press_event;
587   widget_class->button_release_event = gtk_text_view_button_release_event;
588   widget_class->focus_in_event = gtk_text_view_focus_in_event;
589   widget_class->focus_out_event = gtk_text_view_focus_out_event;
590   widget_class->motion_notify_event = gtk_text_view_motion_event;
591   widget_class->draw = gtk_text_view_draw;
592   widget_class->focus = gtk_text_view_focus;
593   widget_class->drag_begin = gtk_text_view_drag_begin;
594   widget_class->drag_end = gtk_text_view_drag_end;
595   widget_class->drag_data_get = gtk_text_view_drag_data_get;
596   widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
597
598   widget_class->drag_leave = gtk_text_view_drag_leave;
599   widget_class->drag_motion = gtk_text_view_drag_motion;
600   widget_class->drag_drop = gtk_text_view_drag_drop;
601   widget_class->drag_data_received = gtk_text_view_drag_data_received;
602
603   widget_class->popup_menu = gtk_text_view_popup_menu;
604   
605   container_class->add = gtk_text_view_add;
606   container_class->remove = gtk_text_view_remove;
607   container_class->forall = gtk_text_view_forall;
608
609   klass->move_cursor = gtk_text_view_move_cursor;
610   klass->set_anchor = gtk_text_view_set_anchor;
611   klass->insert_at_cursor = gtk_text_view_insert_at_cursor;
612   klass->delete_from_cursor = gtk_text_view_delete_from_cursor;
613   klass->backspace = gtk_text_view_backspace;
614   klass->cut_clipboard = gtk_text_view_cut_clipboard;
615   klass->copy_clipboard = gtk_text_view_copy_clipboard;
616   klass->paste_clipboard = gtk_text_view_paste_clipboard;
617   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
618   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
619
620   /*
621    * Properties
622    */
623  
624   g_object_class_install_property (gobject_class,
625                                    PROP_PIXELS_ABOVE_LINES,
626                                    g_param_spec_int ("pixels-above-lines",
627                                                      P_("Pixels Above Lines"),
628                                                      P_("Pixels of blank space above paragraphs"),
629                                                      0,
630                                                      G_MAXINT,
631                                                      0,
632                                                      GTK_PARAM_READWRITE));
633  
634   g_object_class_install_property (gobject_class,
635                                    PROP_PIXELS_BELOW_LINES,
636                                    g_param_spec_int ("pixels-below-lines",
637                                                      P_("Pixels Below Lines"),
638                                                      P_("Pixels of blank space below paragraphs"),
639                                                      0,
640                                                      G_MAXINT,
641                                                      0,
642                                                      GTK_PARAM_READWRITE));
643  
644   g_object_class_install_property (gobject_class,
645                                    PROP_PIXELS_INSIDE_WRAP,
646                                    g_param_spec_int ("pixels-inside-wrap",
647                                                      P_("Pixels Inside Wrap"),
648                                                      P_("Pixels of blank space between wrapped lines in a paragraph"),
649                                                      0,
650                                                      G_MAXINT,
651                                                      0,
652                                                      GTK_PARAM_READWRITE));
653
654   g_object_class_install_property (gobject_class,
655                                    PROP_EDITABLE,
656                                    g_param_spec_boolean ("editable",
657                                                          P_("Editable"),
658                                                          P_("Whether the text can be modified by the user"),
659                                                          TRUE,
660                                                          GTK_PARAM_READWRITE));
661
662   g_object_class_install_property (gobject_class,
663                                    PROP_WRAP_MODE,
664                                    g_param_spec_enum ("wrap-mode",
665                                                       P_("Wrap Mode"),
666                                                       P_("Whether to wrap lines never, at word boundaries, or at character boundaries"),
667                                                       GTK_TYPE_WRAP_MODE,
668                                                       GTK_WRAP_NONE,
669                                                       GTK_PARAM_READWRITE));
670  
671   g_object_class_install_property (gobject_class,
672                                    PROP_JUSTIFICATION,
673                                    g_param_spec_enum ("justification",
674                                                       P_("Justification"),
675                                                       P_("Left, right, or center justification"),
676                                                       GTK_TYPE_JUSTIFICATION,
677                                                       GTK_JUSTIFY_LEFT,
678                                                       GTK_PARAM_READWRITE));
679  
680   g_object_class_install_property (gobject_class,
681                                    PROP_LEFT_MARGIN,
682                                    g_param_spec_int ("left-margin",
683                                                      P_("Left Margin"),
684                                                      P_("Width of the left margin in pixels"),
685                                                      0,
686                                                      G_MAXINT,
687                                                      0,
688                                                      GTK_PARAM_READWRITE));
689
690   g_object_class_install_property (gobject_class,
691                                    PROP_RIGHT_MARGIN,
692                                    g_param_spec_int ("right-margin",
693                                                      P_("Right Margin"),
694                                                      P_("Width of the right margin in pixels"),
695                                                      0,
696                                                      G_MAXINT,
697                                                      0,
698                                                      GTK_PARAM_READWRITE));
699
700   g_object_class_install_property (gobject_class,
701                                    PROP_INDENT,
702                                    g_param_spec_int ("indent",
703                                                      P_("Indent"),
704                                                      P_("Amount to indent the paragraph, in pixels"),
705                                                      G_MININT,
706                                                      G_MAXINT,
707                                                      0,
708                                                      GTK_PARAM_READWRITE));
709
710   g_object_class_install_property (gobject_class,
711                                    PROP_TABS,
712                                    g_param_spec_boxed ("tabs",
713                                                        P_("Tabs"),
714                                                        P_("Custom tabs for this text"),
715                                                        PANGO_TYPE_TAB_ARRAY,
716                                                        GTK_PARAM_READWRITE));
717
718   g_object_class_install_property (gobject_class,
719                                    PROP_CURSOR_VISIBLE,
720                                    g_param_spec_boolean ("cursor-visible",
721                                                          P_("Cursor Visible"),
722                                                          P_("If the insertion cursor is shown"),
723                                                          TRUE,
724                                                          GTK_PARAM_READWRITE));
725
726   g_object_class_install_property (gobject_class,
727                                    PROP_BUFFER,
728                                    g_param_spec_object ("buffer",
729                                                         P_("Buffer"),
730                                                         P_("The buffer which is displayed"),
731                                                         GTK_TYPE_TEXT_BUFFER,
732                                                         GTK_PARAM_READWRITE));
733
734   g_object_class_install_property (gobject_class,
735                                    PROP_OVERWRITE,
736                                    g_param_spec_boolean ("overwrite",
737                                                          P_("Overwrite mode"),
738                                                          P_("Whether entered text overwrites existing contents"),
739                                                          FALSE,
740                                                          GTK_PARAM_READWRITE));
741
742   g_object_class_install_property (gobject_class,
743                                    PROP_ACCEPTS_TAB,
744                                    g_param_spec_boolean ("accepts-tab",
745                                                          P_("Accepts tab"),
746                                                          P_("Whether Tab will result in a tab character being entered"),
747                                                          TRUE,
748                                                          GTK_PARAM_READWRITE));
749
750    /**
751     * GtkTextView:im-module:
752     *
753     * Which IM (input method) module should be used for this entry. 
754     * See #GtkIMContext.
755     *
756     * Setting this to a non-%NULL value overrides the
757     * system-wide IM module setting. See the GtkSettings 
758     * #GtkSettings:gtk-im-module property.
759     *
760     * Since: 2.16
761     */
762    g_object_class_install_property (gobject_class,
763                                     PROP_IM_MODULE,
764                                     g_param_spec_string ("im-module",
765                                                          P_("IM module"),
766                                                          P_("Which IM module should be used"),
767                                                          NULL,
768                                                          GTK_PARAM_READWRITE));
769
770   /*
771    * Style properties
772    */
773   gtk_widget_class_install_style_property (widget_class,
774                                            g_param_spec_boxed ("error-underline-color",
775                                                                P_("Error underline color"),
776                                                                P_("Color with which to draw error-indication underlines"),
777                                                                GDK_TYPE_COLOR,
778                                                                GTK_PARAM_READABLE));
779   
780   /*
781    * Signals
782    */
783
784   /**
785    * GtkTextView::move-cursor: 
786    * @text_view: the object which received the signal
787    * @step: the granularity of the move, as a #GtkMovementStep
788    * @count: the number of @step units to move
789    * @extend_selection: %TRUE if the move should extend the selection
790    *  
791    * The ::move-cursor signal is a 
792    * <link linkend="keybinding-signals">keybinding signal</link> 
793    * which gets emitted when the user initiates a cursor movement. 
794    * If the cursor is not visible in @text_view, this signal causes
795    * the viewport to be moved instead.
796    *
797    * Applications should not connect to it, but may emit it with 
798    * g_signal_emit_by_name() if they need to control the cursor
799    * programmatically.
800    *
801    * The default bindings for this signal come in two variants,
802    * the variant with the Shift modifier extends the selection,
803    * the variant without the Shift modifer does not.
804    * There are too many key combinations to list them all here.
805    * <itemizedlist>
806    * <listitem>Arrow keys move by individual characters/lines</listitem>
807    * <listitem>Ctrl-arrow key combinations move by words/paragraphs</listitem>
808    * <listitem>Home/End keys move to the ends of the buffer</listitem>
809    * <listitem>PageUp/PageDown keys move vertically by pages</listitem>
810    * <listitem>Ctrl-PageUp/PageDown keys move horizontally by pages</listitem>
811    * </itemizedlist>
812    */
813   signals[MOVE_CURSOR] = 
814     g_signal_new (I_("move-cursor"),
815                   G_OBJECT_CLASS_TYPE (gobject_class), 
816                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 
817                   G_STRUCT_OFFSET (GtkTextViewClass, move_cursor),
818                   NULL, NULL, 
819                   _gtk_marshal_VOID__ENUM_INT_BOOLEAN, 
820                   G_TYPE_NONE, 3,
821                   GTK_TYPE_MOVEMENT_STEP, 
822                   G_TYPE_INT, 
823                   G_TYPE_BOOLEAN);
824
825   /**
826    * GtkTextView::move-viewport:
827    * @text_view: the object which received the signal
828    * @step: the granularity of the move, as a #GtkMovementStep
829    * @count: the number of @step units to move
830    *
831    * The ::move-viewport signal is a 
832    * <link linkend="keybinding-signals">keybinding signal</link> 
833    * which can be bound to key combinations to allow the user
834    * to move the viewport, i.e. change what part of the text view
835    * is visible in a containing scrolled window.
836    *
837    * There are no default bindings for this signal.
838    */
839   signals[MOVE_VIEWPORT] =
840     g_signal_new_class_handler (I_("move-viewport"),
841                                 G_OBJECT_CLASS_TYPE (gobject_class),
842                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
843                                 G_CALLBACK (gtk_text_view_move_viewport),
844                                 NULL, NULL,
845                                 _gtk_marshal_VOID__ENUM_INT,
846                                 G_TYPE_NONE, 2,
847                                 GTK_TYPE_SCROLL_STEP,
848                                 G_TYPE_INT);
849
850   /**
851    * GtkTextView::set-anchor:
852    * @text_view: the object which received the signal
853    *
854    * The ::set-anchor signal is a
855    * <link linkend="keybinding-signals">keybinding signal</link>
856    * which gets emitted when the user initiates setting the "anchor" 
857    * mark. The "anchor" mark gets placed at the same position as the
858    * "insert" mark.
859    *
860    * This signal has no default bindings.
861    */   
862   signals[SET_ANCHOR] =
863     g_signal_new (I_("set-anchor"),
864                   G_OBJECT_CLASS_TYPE (gobject_class),
865                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
866                   G_STRUCT_OFFSET (GtkTextViewClass, set_anchor),
867                   NULL, NULL,
868                   _gtk_marshal_VOID__VOID,
869                   G_TYPE_NONE, 0);
870
871   /**
872    * GtkTextView::insert-at-cursor:
873    * @text_view: the object which received the signal
874    * @string: the string to insert
875    *
876    * The ::insert-at-cursor signal is a
877    * <link linkend="keybinding-signals">keybinding signal</link>
878    * which gets emitted when the user initiates the insertion of a 
879    * fixed string at the cursor.
880    *
881    * This signal has no default bindings.
882    */
883   signals[INSERT_AT_CURSOR] =
884     g_signal_new (I_("insert-at-cursor"),
885                   G_OBJECT_CLASS_TYPE (gobject_class),
886                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
887                   G_STRUCT_OFFSET (GtkTextViewClass, insert_at_cursor),
888                   NULL, NULL,
889                   _gtk_marshal_VOID__STRING,
890                   G_TYPE_NONE, 1,
891                   G_TYPE_STRING);
892
893   /**
894    * GtkTextView::delete-from-cursor:
895    * @text_view: the object which received the signal
896    * @type: the granularity of the deletion, as a #GtkDeleteType
897    * @count: the number of @type units to delete
898    *
899    * The ::delete-from-cursor signal is a 
900    * <link linkend="keybinding-signals">keybinding signal</link> 
901    * which gets emitted when the user initiates a text deletion.
902    *
903    * If the @type is %GTK_DELETE_CHARS, GTK+ deletes the selection
904    * if there is one, otherwise it deletes the requested number
905    * of characters.
906    *
907    * The default bindings for this signal are
908    * Delete for deleting a character, Ctrl-Delete for 
909    * deleting a word and Ctrl-Backspace for deleting a word 
910    * backwords.
911    */
912   signals[DELETE_FROM_CURSOR] =
913     g_signal_new (I_("delete-from-cursor"),
914                   G_OBJECT_CLASS_TYPE (gobject_class),
915                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
916                   G_STRUCT_OFFSET (GtkTextViewClass, delete_from_cursor),
917                   NULL, NULL,
918                   _gtk_marshal_VOID__ENUM_INT,
919                   G_TYPE_NONE, 2,
920                   GTK_TYPE_DELETE_TYPE,
921                   G_TYPE_INT);
922
923   /**
924    * GtkTextView::backspace:
925    * @text_view: the object which received the signal
926    *
927    * The ::backspace signal is a 
928    * <link linkend="keybinding-signals">keybinding signal</link> 
929    * which gets emitted when the user asks for it.
930    * 
931    * The default bindings for this signal are
932    * Backspace and Shift-Backspace.
933    */
934   signals[BACKSPACE] =
935     g_signal_new (I_("backspace"),
936                   G_OBJECT_CLASS_TYPE (gobject_class),
937                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
938                   G_STRUCT_OFFSET (GtkTextViewClass, backspace),
939                   NULL, NULL,
940                   _gtk_marshal_VOID__VOID,
941                   G_TYPE_NONE, 0);
942
943   /**
944    * GtkTextView::cut-clipboard:
945    * @text_view: the object which received the signal
946    *
947    * The ::cut-clipboard signal is a 
948    * <link linkend="keybinding-signals">keybinding signal</link> 
949    * which gets emitted to cut the selection to the clipboard.
950    * 
951    * The default bindings for this signal are
952    * Ctrl-x and Shift-Delete.
953    */
954   signals[CUT_CLIPBOARD] =
955     g_signal_new (I_("cut-clipboard"),
956                   G_OBJECT_CLASS_TYPE (gobject_class),
957                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
958                   G_STRUCT_OFFSET (GtkTextViewClass, cut_clipboard),
959                   NULL, NULL,
960                   _gtk_marshal_VOID__VOID,
961                   G_TYPE_NONE, 0);
962
963   /**
964    * GtkTextView::copy-clipboard:
965    * @text_view: the object which received the signal
966    *
967    * The ::copy-clipboard signal is a 
968    * <link linkend="keybinding-signals">keybinding signal</link> 
969    * which gets emitted to copy the selection to the clipboard.
970    * 
971    * The default bindings for this signal are
972    * Ctrl-c and Ctrl-Insert.
973    */
974   signals[COPY_CLIPBOARD] =
975     g_signal_new (I_("copy-clipboard"),
976                   G_OBJECT_CLASS_TYPE (gobject_class),
977                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
978                   G_STRUCT_OFFSET (GtkTextViewClass, copy_clipboard),
979                   NULL, NULL,
980                   _gtk_marshal_VOID__VOID,
981                   G_TYPE_NONE, 0);
982
983   /**
984    * GtkTextView::paste-clipboard:
985    * @text_view: the object which received the signal
986    *
987    * The ::paste-clipboard signal is a 
988    * <link linkend="keybinding-signals">keybinding signal</link> 
989    * which gets emitted to paste the contents of the clipboard 
990    * into the text view.
991    * 
992    * The default bindings for this signal are
993    * Ctrl-v and Shift-Insert.
994    */
995   signals[PASTE_CLIPBOARD] =
996     g_signal_new (I_("paste-clipboard"),
997                   G_OBJECT_CLASS_TYPE (gobject_class),
998                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
999                   G_STRUCT_OFFSET (GtkTextViewClass, paste_clipboard),
1000                   NULL, NULL,
1001                   _gtk_marshal_VOID__VOID,
1002                   G_TYPE_NONE, 0);
1003
1004   /**
1005    * GtkTextView::toggle-overwrite:
1006    * @text_view: the object which received the signal
1007    *
1008    * The ::toggle-overwrite signal is a 
1009    * <link linkend="keybinding-signals">keybinding signal</link> 
1010    * which gets emitted to toggle the overwrite mode of the text view.
1011    * 
1012    * The default bindings for this signal is Insert.
1013    */ 
1014   signals[TOGGLE_OVERWRITE] =
1015     g_signal_new (I_("toggle-overwrite"),
1016                   G_OBJECT_CLASS_TYPE (gobject_class),
1017                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1018                   G_STRUCT_OFFSET (GtkTextViewClass, toggle_overwrite),
1019                   NULL, NULL,
1020                   _gtk_marshal_VOID__VOID,
1021                   G_TYPE_NONE, 0);
1022
1023   /**
1024    * GtkTextView::set-scroll-adjustments
1025    * @horizontal: the horizontal #GtkAdjustment
1026    * @vertical: the vertical #GtkAdjustment
1027    *
1028    * Set the scroll adjustments for the text view. Usually scrolled containers
1029    * like #GtkScrolledWindow will emit this signal to connect two instances
1030    * of #GtkScrollbar to the scroll directions of the #GtkTextView.
1031    */
1032   signals[SET_SCROLL_ADJUSTMENTS] =
1033     g_signal_new (I_("set-scroll-adjustments"),
1034                   G_OBJECT_CLASS_TYPE (gobject_class),
1035                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1036                   G_STRUCT_OFFSET (GtkTextViewClass, set_scroll_adjustments),
1037                   NULL, NULL,
1038                   _gtk_marshal_VOID__OBJECT_OBJECT,
1039                   G_TYPE_NONE, 2,
1040                   GTK_TYPE_ADJUSTMENT,
1041                   GTK_TYPE_ADJUSTMENT);
1042   widget_class->set_scroll_adjustments_signal = signals[SET_SCROLL_ADJUSTMENTS];
1043
1044   /**
1045    * GtkTextView::populate-popup:
1046    * @entry: The text view on which the signal is emitted
1047    * @menu: the menu that is being populated
1048    *
1049    * The ::populate-popup signal gets emitted before showing the 
1050    * context menu of the text view.
1051    *
1052    * If you need to add items to the context menu, connect
1053    * to this signal and append your menuitems to the @menu.
1054    */
1055   signals[POPULATE_POPUP] =
1056     g_signal_new (I_("populate-popup"),
1057                   G_OBJECT_CLASS_TYPE (gobject_class),
1058                   G_SIGNAL_RUN_LAST,
1059                   G_STRUCT_OFFSET (GtkTextViewClass, populate_popup),
1060                   NULL, NULL,
1061                   _gtk_marshal_VOID__OBJECT,
1062                   G_TYPE_NONE, 1,
1063                   GTK_TYPE_MENU);
1064   
1065   /**
1066    * GtkTextView::select-all:
1067    * @text_view: the object which received the signal
1068    * @select: %TRUE to select, %FALSE to unselect
1069    *
1070    * The ::select-all signal is a 
1071    * <link linkend="keybinding-signals">keybinding signal</link> 
1072    * which gets emitted to select or unselect the complete
1073    * contents of the text view.
1074    *
1075    * The default bindings for this signal are Ctrl-a and Ctrl-/ 
1076    * for selecting and Shift-Ctrl-a and Ctrl-\ for unselecting.
1077    */
1078   signals[SELECT_ALL] =
1079     g_signal_new_class_handler (I_("select-all"),
1080                                 G_OBJECT_CLASS_TYPE (gobject_class),
1081                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1082                                 G_CALLBACK (gtk_text_view_select_all),
1083                                 NULL, NULL,
1084                                 _gtk_marshal_VOID__BOOLEAN,
1085                                 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
1086
1087   /**
1088    * GtkTextView::toggle-cursor-visible:
1089    * @text_view: the object which received the signal
1090    *
1091    * The ::toggle-cursor-visible signal is a 
1092    * <link linkend="keybinding-signals">keybinding signal</link> 
1093    * which gets emitted to toggle the visibility of the cursor.
1094    *
1095    * The default binding for this signal is F7.
1096    */ 
1097   signals[TOGGLE_CURSOR_VISIBLE] =
1098     g_signal_new_class_handler (I_("toggle-cursor-visible"),
1099                                 G_OBJECT_CLASS_TYPE (gobject_class),
1100                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1101                                 G_CALLBACK (gtk_text_view_toggle_cursor_visible),
1102                                 NULL, NULL,
1103                                 _gtk_marshal_VOID__VOID,
1104                                 G_TYPE_NONE, 0);
1105
1106   /**
1107    * GtkTextView::preedit-changed:
1108    * @text_view: the object which received the signal
1109    * @preedit: the current preedit string
1110    *
1111    * If an input method is used, the typed text will not immediately
1112    * be committed to the buffer. So if you are interested in the text,
1113    * connect to this signal.
1114    *
1115    * This signal is only emitted if the text at the given position
1116    * is actually editable.
1117    *
1118    * Since: 2.20
1119    */
1120   signals[PREEDIT_CHANGED] =
1121     g_signal_new_class_handler (I_("preedit-changed"),
1122                                 G_OBJECT_CLASS_TYPE (gobject_class),
1123                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1124                                 NULL,
1125                                 NULL, NULL,
1126                                 _gtk_marshal_VOID__STRING,
1127                                 G_TYPE_NONE, 1,
1128                                 G_TYPE_STRING);
1129
1130   /*
1131    * Key bindings
1132    */
1133
1134   binding_set = gtk_binding_set_by_class (klass);
1135   
1136   /* Moving the insertion point */
1137   add_move_binding (binding_set, GDK_KEY_Right, 0,
1138                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1139
1140   add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
1141                     GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1142   
1143   add_move_binding (binding_set, GDK_KEY_Left, 0,
1144                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1145
1146   add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
1147                     GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1148   
1149   add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
1150                     GTK_MOVEMENT_WORDS, 1);
1151
1152   add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1153                     GTK_MOVEMENT_WORDS, 1);
1154   
1155   add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
1156                     GTK_MOVEMENT_WORDS, -1);
1157
1158   add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1159                     GTK_MOVEMENT_WORDS, -1);
1160   
1161   add_move_binding (binding_set, GDK_KEY_Up, 0,
1162                     GTK_MOVEMENT_DISPLAY_LINES, -1);
1163
1164   add_move_binding (binding_set, GDK_KEY_KP_Up, 0,
1165                     GTK_MOVEMENT_DISPLAY_LINES, -1);
1166   
1167   add_move_binding (binding_set, GDK_KEY_Down, 0,
1168                     GTK_MOVEMENT_DISPLAY_LINES, 1);
1169
1170   add_move_binding (binding_set, GDK_KEY_KP_Down, 0,
1171                     GTK_MOVEMENT_DISPLAY_LINES, 1);
1172   
1173   add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
1174                     GTK_MOVEMENT_PARAGRAPHS, -1);
1175
1176   add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
1177                     GTK_MOVEMENT_PARAGRAPHS, -1);
1178   
1179   add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
1180                     GTK_MOVEMENT_PARAGRAPHS, 1);
1181
1182   add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
1183                     GTK_MOVEMENT_PARAGRAPHS, 1);
1184   
1185   add_move_binding (binding_set, GDK_KEY_Home, 0,
1186                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1187
1188   add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
1189                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
1190   
1191   add_move_binding (binding_set, GDK_KEY_End, 0,
1192                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1193
1194   add_move_binding (binding_set, GDK_KEY_KP_End, 0,
1195                     GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
1196   
1197   add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
1198                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1199
1200   add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
1201                     GTK_MOVEMENT_BUFFER_ENDS, -1);
1202   
1203   add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
1204                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1205
1206   add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
1207                     GTK_MOVEMENT_BUFFER_ENDS, 1);
1208   
1209   add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
1210                     GTK_MOVEMENT_PAGES, -1);
1211
1212   add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
1213                     GTK_MOVEMENT_PAGES, -1);
1214   
1215   add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
1216                     GTK_MOVEMENT_PAGES, 1);
1217
1218   add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
1219                     GTK_MOVEMENT_PAGES, 1);
1220
1221   add_move_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1222                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
1223
1224   add_move_binding (binding_set, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK,
1225                     GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
1226   
1227   add_move_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1228                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
1229
1230   add_move_binding (binding_set, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK,
1231                     GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
1232
1233   /* Select all */
1234   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
1235                                 "select-all", 1,
1236                                 G_TYPE_BOOLEAN, TRUE);
1237
1238   gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
1239                                 "select-all", 1,
1240                                 G_TYPE_BOOLEAN, TRUE);
1241   
1242   /* Unselect all */
1243   gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK,
1244                                  "select-all", 1,
1245                                  G_TYPE_BOOLEAN, FALSE);
1246
1247   gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1248                                  "select-all", 1,
1249                                  G_TYPE_BOOLEAN, FALSE);
1250
1251   /* Deleting text */
1252   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0,
1253                                 "delete-from-cursor", 2,
1254                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
1255                                 G_TYPE_INT, 1);
1256
1257   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0,
1258                                 "delete-from-cursor", 2,
1259                                 G_TYPE_ENUM, GTK_DELETE_CHARS,
1260                                 G_TYPE_INT, 1);
1261   
1262   gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1263                                 "backspace", 0);
1264
1265   /* Make this do the same as Backspace, to help with mis-typing */
1266   gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK,
1267                                 "backspace", 0);
1268
1269   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK,
1270                                 "delete-from-cursor", 2,
1271                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
1272                                 G_TYPE_INT, 1);
1273
1274   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK,
1275                                 "delete-from-cursor", 2,
1276                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
1277                                 G_TYPE_INT, 1);
1278   
1279   gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK,
1280                                 "delete-from-cursor", 2,
1281                                 G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
1282                                 G_TYPE_INT, -1);
1283
1284   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1285                                 "delete-from-cursor", 2,
1286                                 G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
1287                                 G_TYPE_INT, 1);
1288
1289   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1290                                 "delete-from-cursor", 2,
1291                                 G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
1292                                 G_TYPE_INT, 1);
1293
1294   gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1295                                 "delete-from-cursor", 2,
1296                                 G_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
1297                                 G_TYPE_INT, -1);
1298
1299   /* Cut/copy/paste */
1300
1301   gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK,
1302                                 "cut-clipboard", 0);
1303   gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
1304                                 "copy-clipboard", 0);
1305   gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
1306                                 "paste-clipboard", 0);
1307
1308   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK,
1309                                 "cut-clipboard", 0);
1310   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK,
1311                                 "copy-clipboard", 0);
1312   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
1313                                 "paste-clipboard", 0);
1314
1315   /* Overwrite */
1316   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0,
1317                                 "toggle-overwrite", 0);
1318   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, 0,
1319                                 "toggle-overwrite", 0);
1320
1321   /* Caret mode */
1322   gtk_binding_entry_add_signal (binding_set, GDK_KEY_F7, 0,
1323                                 "toggle-cursor-visible", 0);
1324
1325   /* Control-tab focus motion */
1326   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, GDK_CONTROL_MASK,
1327                                 "move-focus", 1,
1328                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
1329   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, GDK_CONTROL_MASK,
1330                                 "move-focus", 1,
1331                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
1332   
1333   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1334                                 "move-focus", 1,
1335                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
1336   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
1337                                 "move-focus", 1,
1338                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
1339
1340   g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate));
1341 }
1342
1343 static void
1344 gtk_text_view_init (GtkTextView *text_view)
1345 {
1346   GtkWidget *widget = GTK_WIDGET (text_view);
1347   GtkTargetList *target_list;
1348   GtkTextViewPrivate *priv;
1349
1350   text_view->priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
1351   priv = text_view->priv;
1352
1353   gtk_widget_set_can_focus (widget, TRUE);
1354
1355   /* Set up default style */
1356   priv->wrap_mode = GTK_WRAP_NONE;
1357   priv->pixels_above_lines = 0;
1358   priv->pixels_below_lines = 0;
1359   priv->pixels_inside_wrap = 0;
1360   priv->justify = GTK_JUSTIFY_LEFT;
1361   priv->left_margin = 0;
1362   priv->right_margin = 0;
1363   priv->indent = 0;
1364   priv->tabs = NULL;
1365   priv->editable = TRUE;
1366
1367   priv->scroll_after_paste = TRUE;
1368
1369   gtk_drag_dest_set (widget, 0, NULL, 0,
1370                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
1371
1372   target_list = gtk_target_list_new (NULL, 0);
1373   gtk_drag_dest_set_target_list (widget, target_list);
1374   gtk_target_list_unref (target_list);
1375
1376   priv->virtual_cursor_x = -1;
1377   priv->virtual_cursor_y = -1;
1378
1379   /* This object is completely private. No external entity can gain a reference
1380    * to it; so we create it here and destroy it in finalize ().
1381    */
1382   priv->im_context = gtk_im_multicontext_new ();
1383
1384   g_signal_connect (priv->im_context, "commit",
1385                     G_CALLBACK (gtk_text_view_commit_handler), text_view);
1386   g_signal_connect (priv->im_context, "preedit-changed",
1387                     G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view);
1388   g_signal_connect (priv->im_context, "retrieve-surrounding",
1389                     G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view);
1390   g_signal_connect (priv->im_context, "delete-surrounding",
1391                     G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view);
1392
1393   priv->cursor_visible = TRUE;
1394
1395   priv->accepts_tab = TRUE;
1396
1397   priv->text_window = text_window_new (GTK_TEXT_WINDOW_TEXT,
1398                                        widget, 200, 200);
1399
1400   priv->drag_start_x = -1;
1401   priv->drag_start_y = -1;
1402
1403   priv->pending_place_cursor_button = 0;
1404
1405   /* We handle all our own redrawing */
1406   gtk_widget_set_redraw_on_allocate (widget, FALSE);
1407 }
1408
1409 /**
1410  * gtk_text_view_new:
1411  *
1412  * Creates a new #GtkTextView. If you don't call gtk_text_view_set_buffer()
1413  * before using the text view, an empty default buffer will be created
1414  * for you. Get the buffer with gtk_text_view_get_buffer(). If you want
1415  * to specify your own buffer, consider gtk_text_view_new_with_buffer().
1416  *
1417  * Return value: a new #GtkTextView
1418  **/
1419 GtkWidget*
1420 gtk_text_view_new (void)
1421 {
1422   return g_object_new (GTK_TYPE_TEXT_VIEW, NULL);
1423 }
1424
1425 /**
1426  * gtk_text_view_new_with_buffer:
1427  * @buffer: a #GtkTextBuffer
1428  *
1429  * Creates a new #GtkTextView widget displaying the buffer
1430  * @buffer. One buffer can be shared among many widgets.
1431  * @buffer may be %NULL to create a default buffer, in which case
1432  * this function is equivalent to gtk_text_view_new(). The
1433  * text view adds its own reference count to the buffer; it does not
1434  * take over an existing reference.
1435  *
1436  * Return value: a new #GtkTextView.
1437  **/
1438 GtkWidget*
1439 gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
1440 {
1441   GtkTextView *text_view;
1442
1443   text_view = (GtkTextView*)gtk_text_view_new ();
1444
1445   gtk_text_view_set_buffer (text_view, buffer);
1446
1447   return GTK_WIDGET (text_view);
1448 }
1449
1450 /**
1451  * gtk_text_view_set_buffer:
1452  * @text_view: a #GtkTextView
1453  * @buffer: (allow-none): a #GtkTextBuffer
1454  *
1455  * Sets @buffer as the buffer being displayed by @text_view. The previous
1456  * buffer displayed by the text view is unreferenced, and a reference is
1457  * added to @buffer. If you owned a reference to @buffer before passing it
1458  * to this function, you must remove that reference yourself; #GtkTextView
1459  * will not "adopt" it.
1460  **/
1461 void
1462 gtk_text_view_set_buffer (GtkTextView   *text_view,
1463                           GtkTextBuffer *buffer)
1464 {
1465   GtkTextViewPrivate *priv;
1466
1467   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1468   g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
1469
1470   priv = text_view->priv;
1471
1472   if (priv->buffer == buffer)
1473     return;
1474
1475   if (priv->buffer != NULL)
1476     {
1477       /* Destroy all anchored children */
1478       GSList *tmp_list;
1479       GSList *copy;
1480
1481       copy = g_slist_copy (priv->children);
1482       tmp_list = copy;
1483       while (tmp_list != NULL)
1484         {
1485           GtkTextViewChild *vc = tmp_list->data;
1486
1487           if (vc->anchor)
1488             {
1489               gtk_widget_destroy (vc->widget);
1490               /* vc may now be invalid! */
1491             }
1492
1493           tmp_list = g_slist_next (tmp_list);
1494         }
1495
1496       g_slist_free (copy);
1497
1498       g_signal_handlers_disconnect_by_func (priv->buffer,
1499                                             gtk_text_view_mark_set_handler,
1500                                             text_view);
1501       g_signal_handlers_disconnect_by_func (priv->buffer,
1502                                             gtk_text_view_target_list_notify,
1503                                             text_view);
1504       g_signal_handlers_disconnect_by_func (priv->buffer,
1505                                             gtk_text_view_paste_done_handler,
1506                                             text_view);
1507
1508       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
1509         {
1510           GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
1511                                                               GDK_SELECTION_PRIMARY);
1512           gtk_text_buffer_remove_selection_clipboard (priv->buffer, clipboard);
1513         }
1514
1515       if (priv->layout)
1516         gtk_text_layout_set_buffer (priv->layout, NULL);
1517
1518       g_object_unref (priv->buffer);
1519       priv->dnd_mark = NULL;
1520       priv->first_para_mark = NULL;
1521       cancel_pending_scroll (text_view);
1522     }
1523
1524   priv->buffer = buffer;
1525
1526   if (priv->layout)
1527     gtk_text_layout_set_buffer (priv->layout, buffer);
1528
1529   if (buffer != NULL)
1530     {
1531       GtkTextIter start;
1532
1533       g_object_ref (buffer);
1534
1535       gtk_text_buffer_get_iter_at_offset (priv->buffer, &start, 0);
1536
1537       priv->dnd_mark = gtk_text_buffer_create_mark (priv->buffer,
1538                                                     "gtk_drag_target",
1539                                                     &start, FALSE);
1540
1541       priv->first_para_mark = gtk_text_buffer_create_mark (priv->buffer,
1542                                                            NULL,
1543                                                            &start, TRUE);
1544
1545       priv->first_para_pixels = 0;
1546
1547
1548       g_signal_connect (priv->buffer, "mark-set",
1549                         G_CALLBACK (gtk_text_view_mark_set_handler),
1550                         text_view);
1551       g_signal_connect (priv->buffer, "notify::paste-target-list",
1552                         G_CALLBACK (gtk_text_view_target_list_notify),
1553                         text_view);
1554       g_signal_connect (priv->buffer, "paste-done",
1555                         G_CALLBACK (gtk_text_view_paste_done_handler),
1556                         text_view);
1557
1558       gtk_text_view_target_list_notify (priv->buffer, NULL, text_view);
1559
1560       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
1561         {
1562           GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
1563                                                               GDK_SELECTION_PRIMARY);
1564           gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
1565         }
1566     }
1567
1568   g_object_notify (G_OBJECT (text_view), "buffer");
1569   
1570   if (gtk_widget_get_visible (GTK_WIDGET (text_view)))
1571     gtk_widget_queue_draw (GTK_WIDGET (text_view));
1572
1573   DV(g_print ("Invalidating due to set_buffer\n"));
1574   gtk_text_view_invalidate (text_view);
1575 }
1576
1577 static GtkTextBuffer*
1578 get_buffer (GtkTextView *text_view)
1579 {
1580   if (text_view->priv->buffer == NULL)
1581     {
1582       GtkTextBuffer *b;
1583       b = gtk_text_buffer_new (NULL);
1584       gtk_text_view_set_buffer (text_view, b);
1585       g_object_unref (b);
1586     }
1587
1588   return text_view->priv->buffer;
1589 }
1590
1591 /**
1592  * gtk_text_view_get_buffer:
1593  * @text_view: a #GtkTextView
1594  *
1595  * Returns the #GtkTextBuffer being displayed by this text view.
1596  * The reference count on the buffer is not incremented; the caller
1597  * of this function won't own a new reference.
1598  *
1599  * Return value: (transfer none): a #GtkTextBuffer
1600  **/
1601 GtkTextBuffer*
1602 gtk_text_view_get_buffer (GtkTextView *text_view)
1603 {
1604   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
1605
1606   return get_buffer (text_view);
1607 }
1608
1609 /**
1610  * gtk_text_view_get_iter_at_location:
1611  * @text_view: a #GtkTextView
1612  * @iter: (out): a #GtkTextIter
1613  * @x: x position, in buffer coordinates
1614  * @y: y position, in buffer coordinates
1615  *
1616  * Retrieves the iterator at buffer coordinates @x and @y. Buffer
1617  * coordinates are coordinates for the entire buffer, not just the
1618  * currently-displayed portion.  If you have coordinates from an
1619  * event, you have to convert those to buffer coordinates with
1620  * gtk_text_view_window_to_buffer_coords().
1621  **/
1622 void
1623 gtk_text_view_get_iter_at_location (GtkTextView *text_view,
1624                                     GtkTextIter *iter,
1625                                     gint         x,
1626                                     gint         y)
1627 {
1628   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1629   g_return_if_fail (iter != NULL);
1630
1631   gtk_text_view_ensure_layout (text_view);
1632   
1633   gtk_text_layout_get_iter_at_pixel (text_view->priv->layout,
1634                                      iter, x, y);
1635 }
1636
1637 /**
1638  * gtk_text_view_get_iter_at_position:
1639  * @text_view: a #GtkTextView
1640  * @iter: (out): a #GtkTextIter
1641  * @trailing: (out) (allow-none): if non-%NULL, location to store an integer indicating where
1642  *    in the grapheme the user clicked. It will either be
1643  *    zero, or the number of characters in the grapheme. 
1644  *    0 represents the trailing edge of the grapheme.
1645  * @x: x position, in buffer coordinates
1646  * @y: y position, in buffer coordinates
1647  *
1648  * Retrieves the iterator pointing to the character at buffer 
1649  * coordinates @x and @y. Buffer coordinates are coordinates for 
1650  * the entire buffer, not just the currently-displayed portion.  
1651  * If you have coordinates from an event, you have to convert 
1652  * those to buffer coordinates with 
1653  * gtk_text_view_window_to_buffer_coords().
1654  *
1655  * Note that this is different from gtk_text_view_get_iter_at_location(),
1656  * which returns cursor locations, i.e. positions <emphasis>between</emphasis>
1657  * characters.
1658  *
1659  * Since: 2.6
1660  **/
1661 void
1662 gtk_text_view_get_iter_at_position (GtkTextView *text_view,
1663                                     GtkTextIter *iter,
1664                                     gint        *trailing,
1665                                     gint         x,
1666                                     gint         y)
1667 {
1668   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1669   g_return_if_fail (iter != NULL);
1670
1671   gtk_text_view_ensure_layout (text_view);
1672   
1673   gtk_text_layout_get_iter_at_position (text_view->priv->layout,
1674                                         iter, trailing, x, y);
1675 }
1676
1677 /**
1678  * gtk_text_view_get_iter_location:
1679  * @text_view: a #GtkTextView
1680  * @iter: a #GtkTextIter
1681  * @location: (out): bounds of the character at @iter
1682  *
1683  * Gets a rectangle which roughly contains the character at @iter.
1684  * The rectangle position is in buffer coordinates; use
1685  * gtk_text_view_buffer_to_window_coords() to convert these
1686  * coordinates to coordinates for one of the windows in the text view.
1687  **/
1688 void
1689 gtk_text_view_get_iter_location (GtkTextView       *text_view,
1690                                  const GtkTextIter *iter,
1691                                  GdkRectangle      *location)
1692 {
1693   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1694   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1695
1696   gtk_text_view_ensure_layout (text_view);
1697   
1698   gtk_text_layout_get_iter_location (text_view->priv->layout, iter, location);
1699 }
1700
1701 /**
1702  * gtk_text_view_get_line_yrange:
1703  * @text_view: a #GtkTextView
1704  * @iter: a #GtkTextIter
1705  * @y: (out): return location for a y coordinate
1706  * @height: (out): return location for a height
1707  *
1708  * Gets the y coordinate of the top of the line containing @iter,
1709  * and the height of the line. The coordinate is a buffer coordinate;
1710  * convert to window coordinates with gtk_text_view_buffer_to_window_coords().
1711  **/
1712 void
1713 gtk_text_view_get_line_yrange (GtkTextView       *text_view,
1714                                const GtkTextIter *iter,
1715                                gint              *y,
1716                                gint              *height)
1717 {
1718   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1719   g_return_if_fail (gtk_text_iter_get_buffer (iter) == get_buffer (text_view));
1720
1721   gtk_text_view_ensure_layout (text_view);
1722   
1723   gtk_text_layout_get_line_yrange (text_view->priv->layout,
1724                                    iter,
1725                                    y,
1726                                    height);
1727 }
1728
1729 /**
1730  * gtk_text_view_get_line_at_y:
1731  * @text_view: a #GtkTextView
1732  * @target_iter: (out): a #GtkTextIter
1733  * @y: a y coordinate
1734  * @line_top: (out): return location for top coordinate of the line
1735  *
1736  * Gets the #GtkTextIter at the start of the line containing
1737  * the coordinate @y. @y is in buffer coordinates, convert from
1738  * window coordinates with gtk_text_view_window_to_buffer_coords().
1739  * If non-%NULL, @line_top will be filled with the coordinate of the top
1740  * edge of the line.
1741  **/
1742 void
1743 gtk_text_view_get_line_at_y (GtkTextView *text_view,
1744                              GtkTextIter *target_iter,
1745                              gint         y,
1746                              gint        *line_top)
1747 {
1748   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
1749
1750   gtk_text_view_ensure_layout (text_view);
1751   
1752   gtk_text_layout_get_line_at_y (text_view->priv->layout,
1753                                  target_iter,
1754                                  y,
1755                                  line_top);
1756 }
1757
1758 static gboolean
1759 set_adjustment_clamped (GtkAdjustment *adj, gdouble val)
1760 {
1761   DV (g_print ("  Setting adj to raw value %g\n", val));
1762   
1763   /* We don't really want to clamp to upper; we want to clamp to
1764      upper - page_size which is the highest value the scrollbar
1765      will let us reach. */
1766   if (val > (adj->upper - adj->page_size))
1767     val = adj->upper - adj->page_size;
1768
1769   if (val < adj->lower)
1770     val = adj->lower;
1771
1772   if (val != adj->value)
1773     {
1774       DV (g_print ("  Setting adj to clamped value %g\n", val));
1775       gtk_adjustment_set_value (adj, val);
1776       return TRUE;
1777     }
1778   else
1779     return FALSE;
1780 }
1781
1782 /**
1783  * gtk_text_view_scroll_to_iter:
1784  * @text_view: a #GtkTextView
1785  * @iter: a #GtkTextIter
1786  * @within_margin: margin as a [0.0,0.5) fraction of screen size
1787  * @use_align: whether to use alignment arguments (if %FALSE, 
1788  *    just get the mark onscreen)
1789  * @xalign: horizontal alignment of mark within visible area
1790  * @yalign: vertical alignment of mark within visible area
1791  *
1792  * Scrolls @text_view so that @iter is on the screen in the position
1793  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
1794  * left or top, 1.0 indicates right or bottom, 0.5 means center. 
1795  * If @use_align is %FALSE, the text scrolls the minimal distance to 
1796  * get the mark onscreen, possibly not scrolling at all. The effective 
1797  * screen for purposes of this function is reduced by a margin of size 
1798  * @within_margin.
1799  *
1800  * Note that this function uses the currently-computed height of the
1801  * lines in the text buffer. Line heights are computed in an idle 
1802  * handler; so this function may not have the desired effect if it's 
1803  * called before the height computations. To avoid oddness, consider 
1804  * using gtk_text_view_scroll_to_mark() which saves a point to be 
1805  * scrolled to after line validation.
1806  *
1807  * Return value: %TRUE if scrolling occurred
1808  **/
1809 gboolean
1810 gtk_text_view_scroll_to_iter (GtkTextView   *text_view,
1811                               GtkTextIter   *iter,
1812                               gdouble        within_margin,
1813                               gboolean       use_align,
1814                               gdouble        xalign,
1815                               gdouble        yalign)
1816 {
1817   GdkRectangle rect;
1818   GdkRectangle screen;
1819   gint screen_bottom;
1820   gint screen_right;
1821   gint scroll_dest;
1822   GtkWidget *widget;
1823   gboolean retval = FALSE;
1824   gint scroll_inc;
1825   gint screen_xoffset, screen_yoffset;
1826   gint current_x_scroll, current_y_scroll;
1827
1828   /* FIXME why don't we do the validate-at-scroll-destination thing
1829    * from flush_scroll in this function? I think it wasn't done before
1830    * because changed_handler was screwed up, but I could be wrong.
1831    */
1832   
1833   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
1834   g_return_val_if_fail (iter != NULL, FALSE);
1835   g_return_val_if_fail (within_margin >= 0.0 && within_margin < 0.5, FALSE);
1836   g_return_val_if_fail (xalign >= 0.0 && xalign <= 1.0, FALSE);
1837   g_return_val_if_fail (yalign >= 0.0 && yalign <= 1.0, FALSE);
1838   
1839   widget = GTK_WIDGET (text_view);
1840
1841   DV(g_print(G_STRLOC"\n"));
1842   
1843   gtk_text_layout_get_iter_location (text_view->priv->layout,
1844                                      iter,
1845                                      &rect);
1846
1847   DV (g_print (" target rect %d,%d  %d x %d\n", rect.x, rect.y, rect.width, rect.height));
1848   
1849   current_x_scroll = text_view->priv->xoffset;
1850   current_y_scroll = text_view->priv->yoffset;
1851
1852   screen.x = current_x_scroll;
1853   screen.y = current_y_scroll;
1854   screen.width = SCREEN_WIDTH (widget);
1855   screen.height = SCREEN_HEIGHT (widget);
1856   
1857   screen_xoffset = screen.width * within_margin;
1858   screen_yoffset = screen.height * within_margin;
1859   
1860   screen.x += screen_xoffset;
1861   screen.y += screen_yoffset;
1862   screen.width -= screen_xoffset * 2;
1863   screen.height -= screen_yoffset * 2;
1864
1865   /* paranoia check */
1866   if (screen.width < 1)
1867     screen.width = 1;
1868   if (screen.height < 1)
1869     screen.height = 1;
1870   
1871   /* The -1 here ensures that we leave enough space to draw the cursor
1872    * when this function is used for horizontal scrolling. 
1873    */
1874   screen_right = screen.x + screen.width - 1;
1875   screen_bottom = screen.y + screen.height;
1876   
1877   /* The alignment affects the point in the target character that we
1878    * choose to align. If we're doing right/bottom alignment, we align
1879    * the right/bottom edge of the character the mark is at; if we're
1880    * doing left/top we align the left/top edge of the character; if
1881    * we're doing center alignment we align the center of the
1882    * character.
1883    */
1884   
1885   /* Vertical scroll */
1886
1887   scroll_inc = 0;
1888   scroll_dest = current_y_scroll;
1889   
1890   if (use_align)
1891     {      
1892       scroll_dest = rect.y + (rect.height * yalign) - (screen.height * yalign);
1893       
1894       /* if scroll_dest < screen.y, we move a negative increment (up),
1895        * else a positive increment (down)
1896        */
1897       scroll_inc = scroll_dest - screen.y + screen_yoffset;
1898     }
1899   else
1900     {
1901       /* move minimum to get onscreen */
1902       if (rect.y < screen.y)
1903         {
1904           scroll_dest = rect.y;
1905           scroll_inc = scroll_dest - screen.y - screen_yoffset;
1906         }
1907       else if ((rect.y + rect.height) > screen_bottom)
1908         {
1909           scroll_dest = rect.y + rect.height;
1910           scroll_inc = scroll_dest - screen_bottom + screen_yoffset;
1911         }
1912     }  
1913   
1914   if (scroll_inc != 0)
1915     {
1916       retval = set_adjustment_clamped (get_vadjustment (text_view),
1917                                        current_y_scroll + scroll_inc);
1918
1919       DV (g_print (" vert increment %d\n", scroll_inc));
1920     }
1921
1922   /* Horizontal scroll */
1923   
1924   scroll_inc = 0;
1925   scroll_dest = current_x_scroll;
1926   
1927   if (use_align)
1928     {      
1929       scroll_dest = rect.x + (rect.width * xalign) - (screen.width * xalign);
1930
1931       /* if scroll_dest < screen.y, we move a negative increment (left),
1932        * else a positive increment (right)
1933        */
1934       scroll_inc = scroll_dest - screen.x + screen_xoffset;
1935     }
1936   else
1937     {
1938       /* move minimum to get onscreen */
1939       if (rect.x < screen.x)
1940         {
1941           scroll_dest = rect.x;
1942           scroll_inc = scroll_dest - screen.x - screen_xoffset;
1943         }
1944       else if ((rect.x + rect.width) > screen_right)
1945         {
1946           scroll_dest = rect.x + rect.width;
1947           scroll_inc = scroll_dest - screen_right + screen_xoffset;
1948         }
1949     }
1950   
1951   if (scroll_inc != 0)
1952     {
1953       retval = set_adjustment_clamped (get_hadjustment (text_view),
1954                                        current_x_scroll + scroll_inc);
1955
1956       DV (g_print (" horiz increment %d\n", scroll_inc));
1957     }
1958   
1959   if (retval)
1960     {
1961       DV(g_print (">Actually scrolled ("G_STRLOC")\n"));
1962     }
1963   else
1964     {
1965       DV(g_print (">Didn't end up scrolling ("G_STRLOC")\n"));
1966     }
1967   
1968   return retval;
1969 }
1970
1971 static void
1972 free_pending_scroll (GtkTextPendingScroll *scroll)
1973 {
1974   if (!gtk_text_mark_get_deleted (scroll->mark))
1975     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (scroll->mark),
1976                                  scroll->mark);
1977   g_object_unref (scroll->mark);
1978   g_free (scroll);
1979 }
1980
1981 static void
1982 cancel_pending_scroll (GtkTextView *text_view)
1983 {
1984   if (text_view->priv->pending_scroll)
1985     {
1986       free_pending_scroll (text_view->priv->pending_scroll);
1987       text_view->priv->pending_scroll = NULL;
1988     }
1989 }
1990
1991 static void
1992 gtk_text_view_queue_scroll (GtkTextView   *text_view,
1993                             GtkTextMark   *mark,
1994                             gdouble        within_margin,
1995                             gboolean       use_align,
1996                             gdouble        xalign,
1997                             gdouble        yalign)
1998 {
1999   GtkTextIter iter;
2000   GtkTextPendingScroll *scroll;
2001
2002   DV(g_print(G_STRLOC"\n"));
2003   
2004   scroll = g_new (GtkTextPendingScroll, 1);
2005
2006   scroll->within_margin = within_margin;
2007   scroll->use_align = use_align;
2008   scroll->xalign = xalign;
2009   scroll->yalign = yalign;
2010   
2011   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
2012
2013   scroll->mark = gtk_text_buffer_create_mark (get_buffer (text_view),
2014                                               NULL,
2015                                               &iter,
2016                                               gtk_text_mark_get_left_gravity (mark));
2017
2018   g_object_ref (scroll->mark);
2019   
2020   cancel_pending_scroll (text_view);
2021
2022   text_view->priv->pending_scroll = scroll;
2023 }
2024
2025 static gboolean
2026 gtk_text_view_flush_scroll (GtkTextView *text_view)
2027 {
2028   GtkAllocation allocation;
2029   GtkTextIter iter;
2030   GtkTextPendingScroll *scroll;
2031   gboolean retval;
2032   GtkWidget *widget;
2033
2034   widget = GTK_WIDGET (text_view);
2035   
2036   DV(g_print(G_STRLOC"\n"));
2037   
2038   if (text_view->priv->pending_scroll == NULL)
2039     {
2040       DV (g_print ("in flush scroll, no pending scroll\n"));
2041       return FALSE;
2042     }
2043
2044   scroll = text_view->priv->pending_scroll;
2045
2046   /* avoid recursion */
2047   text_view->priv->pending_scroll = NULL;
2048   
2049   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, scroll->mark);
2050
2051   /* Validate area around the scroll destination, so the adjustment
2052    * can meaningfully point into that area. We must validate
2053    * enough area to be sure that after we scroll, everything onscreen
2054    * is valid; otherwise, validation will maintain the first para
2055    * in one place, but may push the target iter off the bottom of
2056    * the screen.
2057    */
2058   DV(g_print (">Validating scroll destination ("G_STRLOC")\n"));
2059   gtk_widget_get_allocation (widget, &allocation);
2060   gtk_text_layout_validate_yrange (text_view->priv->layout, &iter,
2061                                    -(allocation.height * 2),
2062                                    allocation.height * 2);
2063
2064   DV(g_print (">Done validating scroll destination ("G_STRLOC")\n"));
2065
2066   /* Ensure we have updated width/height */
2067   gtk_text_view_update_adjustments (text_view);
2068   
2069   retval = gtk_text_view_scroll_to_iter (text_view,
2070                                          &iter,
2071                                          scroll->within_margin,
2072                                          scroll->use_align,
2073                                          scroll->xalign,
2074                                          scroll->yalign);
2075
2076   free_pending_scroll (scroll);
2077
2078   return retval;
2079 }
2080
2081 static void
2082 gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gdouble upper)
2083 {  
2084   if (upper != adj->upper)
2085     {
2086       gdouble min = MAX (0.0, upper - adj->page_size);
2087       gboolean value_changed = FALSE;
2088
2089       adj->upper = upper;
2090
2091       if (adj->value > min)
2092         {
2093           adj->value = min;
2094           value_changed = TRUE;
2095         }
2096
2097       gtk_adjustment_changed (adj);
2098       DV(g_print(">Changed adj upper to %g ("G_STRLOC")\n", upper));
2099       
2100       if (value_changed)
2101         {
2102           DV(g_print(">Changed adj value because upper decreased ("G_STRLOC")\n"));
2103           gtk_adjustment_value_changed (adj);
2104         }
2105     }
2106 }
2107
2108 static void
2109 gtk_text_view_update_adjustments (GtkTextView *text_view)
2110 {
2111   GtkTextViewPrivate *priv;
2112   gint width = 0, height = 0;
2113
2114   DV(g_print(">Updating adjustments ("G_STRLOC")\n"));
2115
2116   priv = text_view->priv;
2117
2118   if (priv->layout)
2119     gtk_text_layout_get_size (priv->layout, &width, &height);
2120
2121   /* Make room for the cursor after the last character in the widest line */
2122   width += SPACE_FOR_CURSOR;
2123
2124   if (priv->width != width || priv->height != height)
2125     {
2126       if (priv->width != width)
2127         priv->width_changed = TRUE;
2128
2129       priv->width = width;
2130       priv->height = height;
2131
2132       gtk_text_view_set_adjustment_upper (get_hadjustment (text_view),
2133                                           MAX (SCREEN_WIDTH (text_view), width));
2134       gtk_text_view_set_adjustment_upper (get_vadjustment (text_view),
2135                                           MAX (SCREEN_HEIGHT (text_view), height));
2136       
2137       /* hadj/vadj exist since we called get_hadjustment/get_vadjustment above */
2138
2139       /* Set up the step sizes; we'll say that a page is
2140          our allocation minus one step, and a step is
2141          1/10 of our allocation. */
2142       priv->hadjustment->step_increment =
2143         SCREEN_WIDTH (text_view) / 10.0;
2144       priv->hadjustment->page_increment =
2145         SCREEN_WIDTH (text_view) * 0.9;
2146       
2147       priv->vadjustment->step_increment =
2148         SCREEN_HEIGHT (text_view) / 10.0;
2149       priv->vadjustment->page_increment =
2150         SCREEN_HEIGHT (text_view) * 0.9;
2151
2152       gtk_adjustment_changed (get_hadjustment (text_view));
2153       gtk_adjustment_changed (get_vadjustment (text_view));
2154     }
2155 }
2156
2157 static void
2158 gtk_text_view_update_layout_width (GtkTextView *text_view)
2159 {
2160   DV(g_print(">Updating layout width ("G_STRLOC")\n"));
2161   
2162   gtk_text_view_ensure_layout (text_view);
2163
2164   gtk_text_layout_set_screen_width (text_view->priv->layout,
2165                                     MAX (1, SCREEN_WIDTH (text_view) - SPACE_FOR_CURSOR));
2166 }
2167
2168 static void
2169 gtk_text_view_update_im_spot_location (GtkTextView *text_view)
2170 {
2171   GdkRectangle area;
2172
2173   if (text_view->priv->layout == NULL)
2174     return;
2175   
2176   gtk_text_view_get_cursor_location (text_view, &area);
2177
2178   area.x -= text_view->priv->xoffset;
2179   area.y -= text_view->priv->yoffset;
2180     
2181   /* Width returned by Pango indicates direction of cursor,
2182    * by it's sign more than the size of cursor.
2183    */
2184   area.width = 0;
2185
2186   gtk_im_context_set_cursor_location (text_view->priv->im_context, &area);
2187 }
2188
2189 static gboolean
2190 do_update_im_spot_location (gpointer text_view)
2191 {
2192   GtkTextViewPrivate *priv;
2193
2194   priv = GTK_TEXT_VIEW (text_view)->priv;
2195   priv->im_spot_idle = 0;
2196
2197   gtk_text_view_update_im_spot_location (text_view);
2198   return FALSE;
2199 }
2200
2201 static void
2202 queue_update_im_spot_location (GtkTextView *text_view)
2203 {
2204   GtkTextViewPrivate *priv;
2205
2206   priv = text_view->priv;
2207
2208   /* Use priority a little higher than GTK_TEXT_VIEW_PRIORITY_VALIDATE,
2209    * so we don't wait until the entire buffer has been validated. */
2210   if (!priv->im_spot_idle)
2211     priv->im_spot_idle = gdk_threads_add_idle_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE - 1,
2212                                                     do_update_im_spot_location,
2213                                                     text_view,
2214                                                     NULL);
2215 }
2216
2217 static void
2218 flush_update_im_spot_location (GtkTextView *text_view)
2219 {
2220   GtkTextViewPrivate *priv;
2221
2222   priv = text_view->priv;
2223
2224   if (priv->im_spot_idle)
2225     {
2226       g_source_remove (priv->im_spot_idle);
2227       priv->im_spot_idle = 0;
2228       gtk_text_view_update_im_spot_location (text_view);
2229     }
2230 }
2231
2232 /**
2233  * gtk_text_view_scroll_to_mark:
2234  * @text_view: a #GtkTextView
2235  * @mark: a #GtkTextMark
2236  * @within_margin: margin as a [0.0,0.5) fraction of screen size
2237  * @use_align: whether to use alignment arguments (if %FALSE, just 
2238  *    get the mark onscreen)
2239  * @xalign: horizontal alignment of mark within visible area
2240  * @yalign: vertical alignment of mark within visible area
2241  *
2242  * Scrolls @text_view so that @mark is on the screen in the position
2243  * indicated by @xalign and @yalign. An alignment of 0.0 indicates
2244  * left or top, 1.0 indicates right or bottom, 0.5 means center. 
2245  * If @use_align is %FALSE, the text scrolls the minimal distance to 
2246  * get the mark onscreen, possibly not scrolling at all. The effective 
2247  * screen for purposes of this function is reduced by a margin of size 
2248  * @within_margin.
2249  **/
2250 void
2251 gtk_text_view_scroll_to_mark (GtkTextView *text_view,
2252                               GtkTextMark *mark,
2253                               gdouble      within_margin,
2254                               gboolean     use_align,
2255                               gdouble      xalign,
2256                               gdouble      yalign)
2257 {  
2258   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2259   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2260   g_return_if_fail (within_margin >= 0.0 && within_margin < 0.5);
2261   g_return_if_fail (xalign >= 0.0 && xalign <= 1.0);
2262   g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
2263
2264   /* We need to verify that the buffer contains the mark, otherwise this
2265    * can lead to data structure corruption later on.
2266    */
2267   g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark));
2268
2269   gtk_text_view_queue_scroll (text_view, mark,
2270                               within_margin,
2271                               use_align,
2272                               xalign,
2273                               yalign);
2274
2275   /* If no validation is pending, we need to go ahead and force an
2276    * immediate scroll.
2277    */
2278   if (text_view->priv->layout &&
2279       gtk_text_layout_is_valid (text_view->priv->layout))
2280     gtk_text_view_flush_scroll (text_view);
2281 }
2282
2283 /**
2284  * gtk_text_view_scroll_mark_onscreen:
2285  * @text_view: a #GtkTextView
2286  * @mark: a mark in the buffer for @text_view
2287  * 
2288  * Scrolls @text_view the minimum distance such that @mark is contained
2289  * within the visible area of the widget.
2290  **/
2291 void
2292 gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view,
2293                                     GtkTextMark *mark)
2294 {
2295   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2296   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2297
2298   /* We need to verify that the buffer contains the mark, otherwise this
2299    * can lead to data structure corruption later on.
2300    */
2301   g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark));
2302
2303   gtk_text_view_scroll_to_mark (text_view, mark, 0.0, FALSE, 0.0, 0.0);
2304 }
2305
2306 static gboolean
2307 clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
2308 {
2309   GdkRectangle visible_rect;
2310   gtk_text_view_get_visible_rect (text_view, &visible_rect);
2311
2312   return gtk_text_layout_clamp_iter_to_vrange (text_view->priv->layout, iter,
2313                                                visible_rect.y,
2314                                                visible_rect.y + visible_rect.height);
2315 }
2316
2317 /**
2318  * gtk_text_view_move_mark_onscreen:
2319  * @text_view: a #GtkTextView
2320  * @mark: a #GtkTextMark
2321  *
2322  * Moves a mark within the buffer so that it's
2323  * located within the currently-visible text area.
2324  *
2325  * Return value: %TRUE if the mark moved (wasn't already onscreen)
2326  **/
2327 gboolean
2328 gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
2329                                   GtkTextMark *mark)
2330 {
2331   GtkTextIter iter;
2332
2333   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2334   g_return_val_if_fail (mark != NULL, FALSE);
2335
2336   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, mark);
2337
2338   if (clamp_iter_onscreen (text_view, &iter))
2339     {
2340       gtk_text_buffer_move_mark (get_buffer (text_view), mark, &iter);
2341       return TRUE;
2342     }
2343   else
2344     return FALSE;
2345 }
2346
2347 /**
2348  * gtk_text_view_get_visible_rect:
2349  * @text_view: a #GtkTextView
2350  * @visible_rect: rectangle to fill
2351  *
2352  * Fills @visible_rect with the currently-visible
2353  * region of the buffer, in buffer coordinates. Convert to window coordinates
2354  * with gtk_text_view_buffer_to_window_coords().
2355  **/
2356 void
2357 gtk_text_view_get_visible_rect (GtkTextView  *text_view,
2358                                 GdkRectangle *visible_rect)
2359 {
2360   GtkWidget *widget;
2361
2362   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2363
2364   widget = GTK_WIDGET (text_view);
2365
2366   if (visible_rect)
2367     {
2368       visible_rect->x = text_view->priv->xoffset;
2369       visible_rect->y = text_view->priv->yoffset;
2370       visible_rect->width = SCREEN_WIDTH (widget);
2371       visible_rect->height = SCREEN_HEIGHT (widget);
2372
2373       DV(g_print(" visible rect: %d,%d %d x %d\n",
2374                  visible_rect->x,
2375                  visible_rect->y,
2376                  visible_rect->width,
2377                  visible_rect->height));
2378     }
2379 }
2380
2381 /**
2382  * gtk_text_view_set_wrap_mode:
2383  * @text_view: a #GtkTextView
2384  * @wrap_mode: a #GtkWrapMode
2385  *
2386  * Sets the line wrapping for the view.
2387  **/
2388 void
2389 gtk_text_view_set_wrap_mode (GtkTextView *text_view,
2390                              GtkWrapMode  wrap_mode)
2391 {
2392   GtkTextViewPrivate *priv;
2393
2394   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2395
2396   priv = text_view->priv;
2397
2398   if (priv->wrap_mode != wrap_mode)
2399     {
2400       priv->wrap_mode = wrap_mode;
2401
2402       if (priv->layout)
2403         {
2404           priv->layout->default_style->wrap_mode = wrap_mode;
2405           gtk_text_layout_default_style_changed (priv->layout);
2406         }
2407     }
2408
2409   g_object_notify (G_OBJECT (text_view), "wrap-mode");
2410 }
2411
2412 /**
2413  * gtk_text_view_get_wrap_mode:
2414  * @text_view: a #GtkTextView
2415  *
2416  * Gets the line wrapping for the view.
2417  *
2418  * Return value: the line wrap setting
2419  **/
2420 GtkWrapMode
2421 gtk_text_view_get_wrap_mode (GtkTextView *text_view)
2422 {
2423   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAP_NONE);
2424
2425   return text_view->priv->wrap_mode;
2426 }
2427
2428 /**
2429  * gtk_text_view_set_editable:
2430  * @text_view: a #GtkTextView
2431  * @setting: whether it's editable
2432  *
2433  * Sets the default editability of the #GtkTextView. You can override
2434  * this default setting with tags in the buffer, using the "editable"
2435  * attribute of tags.
2436  **/
2437 void
2438 gtk_text_view_set_editable (GtkTextView *text_view,
2439                             gboolean     setting)
2440 {
2441   GtkTextViewPrivate *priv;
2442
2443   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2444
2445   priv = text_view->priv;
2446   setting = setting != FALSE;
2447
2448   if (priv->editable != setting)
2449     {
2450       if (!setting)
2451         {
2452           gtk_text_view_reset_im_context(text_view);
2453           if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
2454             gtk_im_context_focus_out (priv->im_context);
2455         }
2456
2457       priv->editable = setting;
2458
2459       if (setting && gtk_widget_has_focus (GTK_WIDGET (text_view)))
2460         gtk_im_context_focus_in (priv->im_context);
2461
2462       if (priv->layout)
2463         {
2464           gtk_text_layout_set_overwrite_mode (priv->layout,
2465                                               priv->overwrite_mode && priv->editable);
2466           priv->layout->default_style->editable = priv->editable;
2467           gtk_text_layout_default_style_changed (priv->layout);
2468         }
2469
2470       g_object_notify (G_OBJECT (text_view), "editable");
2471     }
2472 }
2473
2474 /**
2475  * gtk_text_view_get_editable:
2476  * @text_view: a #GtkTextView
2477  *
2478  * Returns the default editability of the #GtkTextView. Tags in the
2479  * buffer may override this setting for some ranges of text.
2480  *
2481  * Return value: whether text is editable by default
2482  **/
2483 gboolean
2484 gtk_text_view_get_editable (GtkTextView *text_view)
2485 {
2486   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2487
2488   return text_view->priv->editable;
2489 }
2490
2491 /**
2492  * gtk_text_view_set_pixels_above_lines:
2493  * @text_view: a #GtkTextView
2494  * @pixels_above_lines: pixels above paragraphs
2495  * 
2496  * Sets the default number of blank pixels above paragraphs in @text_view.
2497  * Tags in the buffer for @text_view may override the defaults.
2498  **/
2499 void
2500 gtk_text_view_set_pixels_above_lines (GtkTextView *text_view,
2501                                       gint         pixels_above_lines)
2502 {
2503   GtkTextViewPrivate *priv;
2504
2505   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2506
2507   priv = text_view->priv;
2508
2509   if (priv->pixels_above_lines != pixels_above_lines)
2510     {
2511       priv->pixels_above_lines = pixels_above_lines;
2512
2513       if (priv->layout)
2514         {
2515           priv->layout->default_style->pixels_above_lines = pixels_above_lines;
2516           gtk_text_layout_default_style_changed (priv->layout);
2517         }
2518
2519       g_object_notify (G_OBJECT (text_view), "pixels-above-lines");
2520     }
2521 }
2522
2523 /**
2524  * gtk_text_view_get_pixels_above_lines:
2525  * @text_view: a #GtkTextView
2526  * 
2527  * Gets the default number of pixels to put above paragraphs.
2528  * 
2529  * Return value: default number of pixels above paragraphs
2530  **/
2531 gint
2532 gtk_text_view_get_pixels_above_lines (GtkTextView *text_view)
2533 {
2534   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2535
2536   return text_view->priv->pixels_above_lines;
2537 }
2538
2539 /**
2540  * gtk_text_view_set_pixels_below_lines:
2541  * @text_view: a #GtkTextView
2542  * @pixels_below_lines: pixels below paragraphs 
2543  *
2544  * Sets the default number of pixels of blank space
2545  * to put below paragraphs in @text_view. May be overridden
2546  * by tags applied to @text_view's buffer. 
2547  **/
2548 void
2549 gtk_text_view_set_pixels_below_lines (GtkTextView *text_view,
2550                                       gint         pixels_below_lines)
2551 {
2552   GtkTextViewPrivate *priv;
2553
2554   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2555
2556   priv = text_view->priv;
2557
2558   if (priv->pixels_below_lines != pixels_below_lines)
2559     {
2560       priv->pixels_below_lines = pixels_below_lines;
2561
2562       if (priv->layout)
2563         {
2564           priv->layout->default_style->pixels_below_lines = pixels_below_lines;
2565           gtk_text_layout_default_style_changed (priv->layout);
2566         }
2567
2568       g_object_notify (G_OBJECT (text_view), "pixels-below-lines");
2569     }
2570 }
2571
2572 /**
2573  * gtk_text_view_get_pixels_below_lines:
2574  * @text_view: a #GtkTextView
2575  * 
2576  * Gets the value set by gtk_text_view_set_pixels_below_lines().
2577  * 
2578  * Return value: default number of blank pixels below paragraphs
2579  **/
2580 gint
2581 gtk_text_view_get_pixels_below_lines (GtkTextView *text_view)
2582 {
2583   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2584
2585   return text_view->priv->pixels_below_lines;
2586 }
2587
2588 /**
2589  * gtk_text_view_set_pixels_inside_wrap:
2590  * @text_view: a #GtkTextView
2591  * @pixels_inside_wrap: default number of pixels between wrapped lines
2592  *
2593  * Sets the default number of pixels of blank space to leave between
2594  * display/wrapped lines within a paragraph. May be overridden by
2595  * tags in @text_view's buffer.
2596  **/
2597 void
2598 gtk_text_view_set_pixels_inside_wrap (GtkTextView *text_view,
2599                                       gint         pixels_inside_wrap)
2600 {
2601   GtkTextViewPrivate *priv;
2602
2603   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2604
2605   priv = text_view->priv;
2606
2607   if (priv->pixels_inside_wrap != pixels_inside_wrap)
2608     {
2609       priv->pixels_inside_wrap = pixels_inside_wrap;
2610
2611       if (priv->layout)
2612         {
2613           priv->layout->default_style->pixels_inside_wrap = pixels_inside_wrap;
2614           gtk_text_layout_default_style_changed (priv->layout);
2615         }
2616
2617       g_object_notify (G_OBJECT (text_view), "pixels-inside-wrap");
2618     }
2619 }
2620
2621 /**
2622  * gtk_text_view_get_pixels_inside_wrap:
2623  * @text_view: a #GtkTextView
2624  * 
2625  * Gets the value set by gtk_text_view_set_pixels_inside_wrap().
2626  * 
2627  * Return value: default number of pixels of blank space between wrapped lines
2628  **/
2629 gint
2630 gtk_text_view_get_pixels_inside_wrap (GtkTextView *text_view)
2631 {
2632   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2633
2634   return text_view->priv->pixels_inside_wrap;
2635 }
2636
2637 /**
2638  * gtk_text_view_set_justification:
2639  * @text_view: a #GtkTextView
2640  * @justification: justification
2641  *
2642  * Sets the default justification of text in @text_view.
2643  * Tags in the view's buffer may override the default.
2644  * 
2645  **/
2646 void
2647 gtk_text_view_set_justification (GtkTextView     *text_view,
2648                                  GtkJustification justification)
2649 {
2650   GtkTextViewPrivate *priv;
2651
2652   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2653
2654   priv = text_view->priv;
2655
2656   if (priv->justify != justification)
2657     {
2658       priv->justify = justification;
2659
2660       if (priv->layout)
2661         {
2662           priv->layout->default_style->justification = justification;
2663           gtk_text_layout_default_style_changed (priv->layout);
2664         }
2665
2666       g_object_notify (G_OBJECT (text_view), "justification");
2667     }
2668 }
2669
2670 /**
2671  * gtk_text_view_get_justification:
2672  * @text_view: a #GtkTextView
2673  * 
2674  * Gets the default justification of paragraphs in @text_view.
2675  * Tags in the buffer may override the default.
2676  * 
2677  * Return value: default justification
2678  **/
2679 GtkJustification
2680 gtk_text_view_get_justification (GtkTextView *text_view)
2681 {
2682   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_JUSTIFY_LEFT);
2683
2684   return text_view->priv->justify;
2685 }
2686
2687 /**
2688  * gtk_text_view_set_left_margin:
2689  * @text_view: a #GtkTextView
2690  * @left_margin: left margin in pixels
2691  * 
2692  * Sets the default left margin for text in @text_view.
2693  * Tags in the buffer may override the default.
2694  **/
2695 void
2696 gtk_text_view_set_left_margin (GtkTextView *text_view,
2697                                gint         left_margin)
2698 {
2699   GtkTextViewPrivate *priv;
2700
2701   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2702
2703   priv = text_view->priv;
2704
2705   if (priv->left_margin != left_margin)
2706     {
2707       priv->left_margin = left_margin;
2708
2709       if (priv->layout)
2710         {
2711           priv->layout->default_style->left_margin = left_margin;
2712           gtk_text_layout_default_style_changed (priv->layout);
2713         }
2714
2715       g_object_notify (G_OBJECT (text_view), "left-margin");
2716     }
2717 }
2718
2719 /**
2720  * gtk_text_view_get_left_margin:
2721  * @text_view: a #GtkTextView
2722  * 
2723  * Gets the default left margin size of paragraphs in the @text_view.
2724  * Tags in the buffer may override the default.
2725  * 
2726  * Return value: left margin in pixels
2727  **/
2728 gint
2729 gtk_text_view_get_left_margin (GtkTextView *text_view)
2730 {
2731   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2732
2733   return text_view->priv->left_margin;
2734 }
2735
2736 /**
2737  * gtk_text_view_set_right_margin:
2738  * @text_view: a #GtkTextView
2739  * @right_margin: right margin in pixels
2740  *
2741  * Sets the default right margin for text in the text view.
2742  * Tags in the buffer may override the default.
2743  **/
2744 void
2745 gtk_text_view_set_right_margin (GtkTextView *text_view,
2746                                 gint         right_margin)
2747 {
2748   GtkTextViewPrivate *priv = text_view->priv;
2749
2750   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2751
2752   if (priv->right_margin != right_margin)
2753     {
2754       priv->right_margin = right_margin;
2755
2756       if (priv->layout)
2757         {
2758           priv->layout->default_style->right_margin = right_margin;
2759           gtk_text_layout_default_style_changed (priv->layout);
2760         }
2761
2762       g_object_notify (G_OBJECT (text_view), "right-margin");
2763     }
2764 }
2765
2766 /**
2767  * gtk_text_view_get_right_margin:
2768  * @text_view: a #GtkTextView
2769  * 
2770  * Gets the default right margin for text in @text_view. Tags
2771  * in the buffer may override the default.
2772  * 
2773  * Return value: right margin in pixels
2774  **/
2775 gint
2776 gtk_text_view_get_right_margin (GtkTextView *text_view)
2777 {
2778   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2779
2780   return text_view->priv->right_margin;
2781 }
2782
2783 /**
2784  * gtk_text_view_set_indent:
2785  * @text_view: a #GtkTextView
2786  * @indent: indentation in pixels
2787  *
2788  * Sets the default indentation for paragraphs in @text_view.
2789  * Tags in the buffer may override the default.
2790  **/
2791 void
2792 gtk_text_view_set_indent (GtkTextView *text_view,
2793                           gint         indent)
2794 {
2795   GtkTextViewPrivate *priv;
2796
2797   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2798
2799   priv = text_view->priv;
2800
2801   if (priv->indent != indent)
2802     {
2803       priv->indent = indent;
2804
2805       if (priv->layout)
2806         {
2807           priv->layout->default_style->indent = indent;
2808           gtk_text_layout_default_style_changed (priv->layout);
2809         }
2810
2811       g_object_notify (G_OBJECT (text_view), "indent");
2812     }
2813 }
2814
2815 /**
2816  * gtk_text_view_get_indent:
2817  * @text_view: a #GtkTextView
2818  * 
2819  * Gets the default indentation of paragraphs in @text_view.
2820  * Tags in the view's buffer may override the default.
2821  * The indentation may be negative.
2822  * 
2823  * Return value: number of pixels of indentation
2824  **/
2825 gint
2826 gtk_text_view_get_indent (GtkTextView *text_view)
2827 {
2828   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
2829
2830   return text_view->priv->indent;
2831 }
2832
2833 /**
2834  * gtk_text_view_set_tabs:
2835  * @text_view: a #GtkTextView
2836  * @tabs: tabs as a #PangoTabArray
2837  *
2838  * Sets the default tab stops for paragraphs in @text_view.
2839  * Tags in the buffer may override the default.
2840  **/
2841 void
2842 gtk_text_view_set_tabs (GtkTextView   *text_view,
2843                         PangoTabArray *tabs)
2844 {
2845   GtkTextViewPrivate *priv;
2846
2847   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2848
2849   priv = text_view->priv;
2850
2851   if (priv->tabs)
2852     pango_tab_array_free (priv->tabs);
2853
2854   priv->tabs = tabs ? pango_tab_array_copy (tabs) : NULL;
2855
2856   if (priv->layout)
2857     {
2858       /* some unkosher futzing in internal struct details... */
2859       if (priv->layout->default_style->tabs)
2860         pango_tab_array_free (priv->layout->default_style->tabs);
2861
2862       priv->layout->default_style->tabs =
2863         priv->tabs ? pango_tab_array_copy (priv->tabs) : NULL;
2864
2865       gtk_text_layout_default_style_changed (priv->layout);
2866     }
2867
2868   g_object_notify (G_OBJECT (text_view), "tabs");
2869 }
2870
2871 /**
2872  * gtk_text_view_get_tabs:
2873  * @text_view: a #GtkTextView
2874  * 
2875  * Gets the default tabs for @text_view. Tags in the buffer may
2876  * override the defaults. The returned array will be %NULL if
2877  * "standard" (8-space) tabs are used. Free the return value
2878  * with pango_tab_array_free().
2879  * 
2880  * Return value: copy of default tab array, or %NULL if "standard" 
2881  *    tabs are used; must be freed with pango_tab_array_free().
2882  **/
2883 PangoTabArray*
2884 gtk_text_view_get_tabs (GtkTextView *text_view)
2885 {
2886   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
2887
2888   return text_view->priv->tabs ? pango_tab_array_copy (text_view->priv->tabs) : NULL;
2889 }
2890
2891 static void
2892 gtk_text_view_toggle_cursor_visible (GtkTextView *text_view)
2893 {
2894   gtk_text_view_set_cursor_visible (text_view, !text_view->priv->cursor_visible);
2895 }
2896
2897 /**
2898  * gtk_text_view_set_cursor_visible:
2899  * @text_view: a #GtkTextView
2900  * @setting: whether to show the insertion cursor
2901  *
2902  * Toggles whether the insertion point is displayed. A buffer with no editable
2903  * text probably shouldn't have a visible cursor, so you may want to turn
2904  * the cursor off.
2905  **/
2906 void
2907 gtk_text_view_set_cursor_visible (GtkTextView *text_view,
2908                                   gboolean     setting)
2909 {
2910   GtkTextViewPrivate *priv;
2911
2912   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
2913
2914   priv = text_view->priv;
2915   setting = (setting != FALSE);
2916
2917   if (priv->cursor_visible != setting)
2918     {
2919       priv->cursor_visible = setting;
2920
2921       if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
2922         {
2923           if (priv->layout)
2924             {
2925               gtk_text_layout_set_cursor_visible (priv->layout, setting);
2926               gtk_text_view_check_cursor_blink (text_view);
2927             }
2928         }
2929
2930       g_object_notify (G_OBJECT (text_view), "cursor-visible");
2931     }
2932 }
2933
2934 /**
2935  * gtk_text_view_get_cursor_visible:
2936  * @text_view: a #GtkTextView
2937  *
2938  * Find out whether the cursor is being displayed.
2939  *
2940  * Return value: whether the insertion mark is visible
2941  **/
2942 gboolean
2943 gtk_text_view_get_cursor_visible (GtkTextView *text_view)
2944 {
2945   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2946
2947   return text_view->priv->cursor_visible;
2948 }
2949
2950
2951 /**
2952  * gtk_text_view_place_cursor_onscreen:
2953  * @text_view: a #GtkTextView
2954  *
2955  * Moves the cursor to the currently visible region of the
2956  * buffer, it it isn't there already.
2957  *
2958  * Return value: %TRUE if the cursor had to be moved.
2959  **/
2960 gboolean
2961 gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
2962 {
2963   GtkTextIter insert;
2964
2965   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
2966
2967   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
2968                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
2969
2970   if (clamp_iter_onscreen (text_view, &insert))
2971     {
2972       gtk_text_buffer_place_cursor (get_buffer (text_view), &insert);
2973       return TRUE;
2974     }
2975   else
2976     return FALSE;
2977 }
2978
2979 static void
2980 gtk_text_view_remove_validate_idles (GtkTextView *text_view)
2981 {
2982   GtkTextViewPrivate *priv = text_view->priv;
2983
2984   if (priv->first_validate_idle != 0)
2985     {
2986       DV (g_print ("Removing first validate idle: %s\n", G_STRLOC));
2987       g_source_remove (priv->first_validate_idle);
2988       priv->first_validate_idle = 0;
2989     }
2990
2991   if (priv->incremental_validate_idle != 0)
2992     {
2993       g_source_remove (priv->incremental_validate_idle);
2994       priv->incremental_validate_idle = 0;
2995     }
2996 }
2997
2998 static void
2999 gtk_text_view_destroy (GtkWidget *widget)
3000 {
3001   GtkTextView *text_view;
3002   GtkTextViewPrivate *priv;
3003
3004   text_view = GTK_TEXT_VIEW (widget);
3005   priv = text_view->priv;
3006
3007   gtk_text_view_remove_validate_idles (text_view);
3008   gtk_text_view_set_buffer (text_view, NULL);
3009   gtk_text_view_destroy_layout (text_view);
3010
3011   if (text_view->priv->scroll_timeout)
3012     {
3013       g_source_remove (text_view->priv->scroll_timeout);
3014       text_view->priv->scroll_timeout = 0;
3015     }
3016
3017   if (priv->im_spot_idle)
3018     {
3019       g_source_remove (priv->im_spot_idle);
3020       priv->im_spot_idle = 0;
3021     }
3022
3023   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->destroy (widget);
3024 }
3025
3026 static void
3027 gtk_text_view_finalize (GObject *object)
3028 {
3029   GtkTextView *text_view;
3030   GtkTextViewPrivate *priv;
3031
3032   text_view = GTK_TEXT_VIEW (object);
3033   priv = text_view->priv;
3034
3035   g_assert (priv->buffer == NULL);
3036
3037   gtk_text_view_destroy_layout (text_view);
3038   gtk_text_view_set_buffer (text_view, NULL);
3039   
3040   cancel_pending_scroll (text_view);
3041
3042   if (priv->tabs)
3043     pango_tab_array_free (priv->tabs);
3044   
3045   if (priv->hadjustment)
3046     g_object_unref (priv->hadjustment);
3047   if (priv->vadjustment)
3048     g_object_unref (priv->vadjustment);
3049
3050   text_window_free (priv->text_window);
3051
3052   if (priv->left_window)
3053     text_window_free (priv->left_window);
3054
3055   if (priv->top_window)
3056     text_window_free (priv->top_window);
3057
3058   if (priv->right_window)
3059     text_window_free (priv->right_window);
3060
3061   if (priv->bottom_window)
3062     text_window_free (priv->bottom_window);
3063
3064   g_object_unref (priv->im_context);
3065
3066   g_free (priv->im_module);
3067
3068   G_OBJECT_CLASS (gtk_text_view_parent_class)->finalize (object);
3069 }
3070
3071 static void
3072 gtk_text_view_set_property (GObject         *object,
3073                             guint            prop_id,
3074                             const GValue    *value,
3075                             GParamSpec      *pspec)
3076 {
3077   GtkTextView *text_view;
3078   GtkTextViewPrivate *priv;
3079
3080   text_view = GTK_TEXT_VIEW (object);
3081   priv = text_view->priv;
3082
3083   switch (prop_id)
3084     {
3085     case PROP_PIXELS_ABOVE_LINES:
3086       gtk_text_view_set_pixels_above_lines (text_view, g_value_get_int (value));
3087       break;
3088
3089     case PROP_PIXELS_BELOW_LINES:
3090       gtk_text_view_set_pixels_below_lines (text_view, g_value_get_int (value));
3091       break;
3092
3093     case PROP_PIXELS_INSIDE_WRAP:
3094       gtk_text_view_set_pixels_inside_wrap (text_view, g_value_get_int (value));
3095       break;
3096
3097     case PROP_EDITABLE:
3098       gtk_text_view_set_editable (text_view, g_value_get_boolean (value));
3099       break;
3100
3101     case PROP_WRAP_MODE:
3102       gtk_text_view_set_wrap_mode (text_view, g_value_get_enum (value));
3103       break;
3104       
3105     case PROP_JUSTIFICATION:
3106       gtk_text_view_set_justification (text_view, g_value_get_enum (value));
3107       break;
3108
3109     case PROP_LEFT_MARGIN:
3110       gtk_text_view_set_left_margin (text_view, g_value_get_int (value));
3111       break;
3112
3113     case PROP_RIGHT_MARGIN:
3114       gtk_text_view_set_right_margin (text_view, g_value_get_int (value));
3115       break;
3116
3117     case PROP_INDENT:
3118       gtk_text_view_set_indent (text_view, g_value_get_int (value));
3119       break;
3120
3121     case PROP_TABS:
3122       gtk_text_view_set_tabs (text_view, g_value_get_boxed (value));
3123       break;
3124
3125     case PROP_CURSOR_VISIBLE:
3126       gtk_text_view_set_cursor_visible (text_view, g_value_get_boolean (value));
3127       break;
3128
3129     case PROP_OVERWRITE:
3130       gtk_text_view_set_overwrite (text_view, g_value_get_boolean (value));
3131       break;
3132
3133     case PROP_BUFFER:
3134       gtk_text_view_set_buffer (text_view, GTK_TEXT_BUFFER (g_value_get_object (value)));
3135       break;
3136
3137     case PROP_ACCEPTS_TAB:
3138       gtk_text_view_set_accepts_tab (text_view, g_value_get_boolean (value));
3139       break;
3140       
3141     case PROP_IM_MODULE:
3142       g_free (priv->im_module);
3143       priv->im_module = g_value_dup_string (value);
3144       if (GTK_IS_IM_MULTICONTEXT (priv->im_context))
3145         gtk_im_multicontext_set_context_id (GTK_IM_MULTICONTEXT (priv->im_context), priv->im_module);
3146       break;
3147
3148     default:
3149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3150       break;
3151     }
3152 }
3153
3154 static void
3155 gtk_text_view_get_property (GObject         *object,
3156                             guint            prop_id,
3157                             GValue          *value,
3158                             GParamSpec      *pspec)
3159 {
3160   GtkTextView *text_view;
3161   GtkTextViewPrivate *priv;
3162
3163   text_view = GTK_TEXT_VIEW (object);
3164   priv = text_view->priv;
3165
3166   switch (prop_id)
3167     {
3168     case PROP_PIXELS_ABOVE_LINES:
3169       g_value_set_int (value, priv->pixels_above_lines);
3170       break;
3171
3172     case PROP_PIXELS_BELOW_LINES:
3173       g_value_set_int (value, priv->pixels_below_lines);
3174       break;
3175
3176     case PROP_PIXELS_INSIDE_WRAP:
3177       g_value_set_int (value, priv->pixels_inside_wrap);
3178       break;
3179
3180     case PROP_EDITABLE:
3181       g_value_set_boolean (value, priv->editable);
3182       break;
3183       
3184     case PROP_WRAP_MODE:
3185       g_value_set_enum (value, priv->wrap_mode);
3186       break;
3187
3188     case PROP_JUSTIFICATION:
3189       g_value_set_enum (value, priv->justify);
3190       break;
3191
3192     case PROP_LEFT_MARGIN:
3193       g_value_set_int (value, priv->left_margin);
3194       break;
3195
3196     case PROP_RIGHT_MARGIN:
3197       g_value_set_int (value, priv->right_margin);
3198       break;
3199
3200     case PROP_INDENT:
3201       g_value_set_int (value, priv->indent);
3202       break;
3203
3204     case PROP_TABS:
3205       g_value_set_boxed (value, priv->tabs);
3206       break;
3207
3208     case PROP_CURSOR_VISIBLE:
3209       g_value_set_boolean (value, priv->cursor_visible);
3210       break;
3211
3212     case PROP_BUFFER:
3213       g_value_set_object (value, get_buffer (text_view));
3214       break;
3215
3216     case PROP_OVERWRITE:
3217       g_value_set_boolean (value, priv->overwrite_mode);
3218       break;
3219
3220     case PROP_ACCEPTS_TAB:
3221       g_value_set_boolean (value, priv->accepts_tab);
3222       break;
3223       
3224     case PROP_IM_MODULE:
3225       g_value_set_string (value, priv->im_module);
3226       break;
3227
3228     default:
3229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3230       break;
3231     }
3232 }
3233
3234 static void
3235 gtk_text_view_size_request (GtkWidget      *widget,
3236                             GtkRequisition *requisition)
3237 {
3238   GtkTextView *text_view;
3239   GtkTextViewPrivate *priv;
3240   GSList *tmp_list;
3241   gint focus_edge_width;
3242   gint focus_width;
3243   guint border_width;
3244   gboolean interior_focus;
3245
3246   text_view = GTK_TEXT_VIEW (widget);
3247   priv = text_view->priv;
3248
3249   gtk_widget_style_get (widget,
3250                         "interior-focus", &interior_focus,
3251                         "focus-line-width", &focus_width,
3252                         NULL);
3253
3254   if (interior_focus)
3255     focus_edge_width = 0;
3256   else
3257     focus_edge_width = focus_width;
3258
3259   if (priv->layout)
3260     {
3261       priv->text_window->requisition.width = priv->layout->width;
3262       priv->text_window->requisition.height = priv->layout->height;
3263     }
3264   else
3265     {
3266       priv->text_window->requisition.width = 0;
3267       priv->text_window->requisition.height = 0;
3268     }
3269   
3270   requisition->width = priv->text_window->requisition.width + focus_edge_width * 2;
3271   requisition->height = priv->text_window->requisition.height + focus_edge_width * 2;
3272
3273   if (priv->left_window)
3274     requisition->width += priv->left_window->requisition.width;
3275
3276   if (priv->right_window)
3277     requisition->width += priv->right_window->requisition.width;
3278
3279   if (priv->top_window)
3280     requisition->height += priv->top_window->requisition.height;
3281
3282   if (priv->bottom_window)
3283     requisition->height += priv->bottom_window->requisition.height;
3284
3285   border_width = gtk_container_get_border_width (GTK_CONTAINER (text_view));
3286   requisition->width += border_width * 2;
3287   requisition->height += border_width * 2;
3288   
3289   tmp_list = priv->children;
3290   while (tmp_list != NULL)
3291     {
3292       GtkTextViewChild *child = tmp_list->data;
3293
3294       if (child->anchor)
3295         {
3296           GtkRequisition child_req;
3297           GtkRequisition old_req;
3298
3299           gtk_widget_get_preferred_size (child->widget, &old_req, NULL);
3300
3301           gtk_widget_get_preferred_size (child->widget, &child_req, NULL);
3302
3303           /* Invalidate layout lines if required */
3304           if (priv->layout &&
3305               (old_req.width != child_req.width ||
3306                old_req.height != child_req.height))
3307             gtk_text_child_anchor_queue_resize (child->anchor,
3308                                                 priv->layout);
3309         }
3310       else
3311         {
3312           GtkRequisition child_req;
3313
3314           gtk_widget_get_preferred_size (child->widget,
3315                                          &child_req, NULL);
3316         }
3317
3318       tmp_list = g_slist_next (tmp_list);
3319     }
3320
3321   /* Cache the requested size of the text view so we can 
3322    * compare it in the changed_handler() */
3323   priv->cached_size_request = *requisition;
3324 }
3325
3326 static void
3327 gtk_text_view_compute_child_allocation (GtkTextView      *text_view,
3328                                         GtkTextViewChild *vc,
3329                                         GtkAllocation    *allocation)
3330 {
3331   gint buffer_y;
3332   GtkTextIter iter;
3333   GtkRequisition req;
3334   
3335   gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
3336                                             &iter,
3337                                             vc->anchor);
3338
3339   gtk_text_layout_get_line_yrange (text_view->priv->layout, &iter,
3340                                    &buffer_y, NULL);
3341
3342   buffer_y += vc->from_top_of_line;
3343
3344   allocation->x = vc->from_left_of_buffer - text_view->priv->xoffset;
3345   allocation->y = buffer_y - text_view->priv->yoffset;
3346
3347   gtk_widget_get_preferred_size (vc->widget, &req, NULL);
3348   allocation->width = req.width;
3349   allocation->height = req.height;
3350 }
3351
3352 static void
3353 gtk_text_view_update_child_allocation (GtkTextView      *text_view,
3354                                        GtkTextViewChild *vc)
3355 {
3356   GtkAllocation allocation;
3357
3358   gtk_text_view_compute_child_allocation (text_view, vc, &allocation);
3359   
3360   gtk_widget_size_allocate (vc->widget, &allocation);
3361
3362 #if 0
3363   g_print ("allocation for %p allocated to %d,%d yoffset = %d\n",
3364            vc->widget,
3365            vc->widget->allocation.x,
3366            vc->widget->allocation.y,
3367            text_view->priv->yoffset);
3368 #endif
3369 }
3370
3371 static void
3372 gtk_text_view_child_allocated (GtkTextLayout *layout,
3373                                GtkWidget     *child,
3374                                gint           x,
3375                                gint           y,
3376                                gpointer       data)
3377 {
3378   GtkTextViewChild *vc = NULL;
3379   GtkTextView *text_view = data;
3380   
3381   /* x,y is the position of the child from the top of the line, and
3382    * from the left of the buffer. We have to translate that into text
3383    * window coordinates, then size_allocate the child.
3384    */
3385
3386   vc = g_object_get_data (G_OBJECT (child),
3387                           "gtk-text-view-child");
3388
3389   g_assert (vc != NULL);
3390
3391   DV (g_print ("child allocated at %d,%d\n", x, y));
3392   
3393   vc->from_left_of_buffer = x;
3394   vc->from_top_of_line = y;
3395
3396   gtk_text_view_update_child_allocation (text_view, vc);
3397 }
3398
3399 static void
3400 gtk_text_view_allocate_children (GtkTextView *text_view)
3401 {
3402   GSList *tmp_list;
3403
3404   DV(g_print(G_STRLOC"\n"));
3405   
3406   tmp_list = text_view->priv->children;
3407   while (tmp_list != NULL)
3408     {
3409       GtkTextViewChild *child = tmp_list->data;
3410
3411       g_assert (child != NULL);
3412           
3413       if (child->anchor)
3414         {
3415           /* We need to force-validate the regions containing
3416            * children.
3417            */
3418           GtkTextIter child_loc;
3419           gtk_text_buffer_get_iter_at_child_anchor (get_buffer (text_view),
3420                                                     &child_loc,
3421                                                     child->anchor);
3422
3423           /* Since anchored children are only ever allocated from
3424            * gtk_text_layout_get_line_display() we have to make sure
3425            * that the display line caching in the layout doesn't 
3426            * get in the way. Invalidating the layout around the anchor
3427            * achieves this.
3428            */ 
3429           if (_gtk_widget_get_alloc_needed (child->widget))
3430             {
3431               GtkTextIter end = child_loc;
3432               gtk_text_iter_forward_char (&end);
3433               gtk_text_layout_invalidate (text_view->priv->layout, &child_loc, &end);
3434             }
3435
3436           gtk_text_layout_validate_yrange (text_view->priv->layout,
3437                                            &child_loc,
3438                                            0, 1);
3439         }
3440       else
3441         {
3442           GtkAllocation allocation;
3443           GtkRequisition child_req;
3444              
3445           allocation.x = child->x;
3446           allocation.y = child->y;
3447
3448           gtk_widget_get_preferred_size (child->widget, &child_req, NULL);
3449
3450           allocation.width = child_req.width;
3451           allocation.height = child_req.height;
3452           
3453           gtk_widget_size_allocate (child->widget, &allocation);          
3454         }
3455
3456       tmp_list = g_slist_next (tmp_list);
3457     }
3458 }
3459
3460 static void
3461 gtk_text_view_size_allocate (GtkWidget *widget,
3462                              GtkAllocation *allocation)
3463 {
3464   GtkAllocation widget_allocation;
3465   GtkTextView *text_view;
3466   GtkTextViewPrivate *priv;
3467   GtkTextIter first_para;
3468   gint y;
3469   gint width, height;
3470   GdkRectangle text_rect;
3471   GdkRectangle left_rect;
3472   GdkRectangle right_rect;
3473   GdkRectangle top_rect;
3474   GdkRectangle bottom_rect;
3475   gint focus_edge_width;
3476   gint focus_width;
3477   guint border_width;
3478   gboolean interior_focus;
3479   gboolean size_changed;
3480   
3481   text_view = GTK_TEXT_VIEW (widget);
3482   priv = text_view->priv;
3483
3484   DV(g_print(G_STRLOC"\n"));
3485
3486   gtk_widget_get_allocation (widget, &widget_allocation);
3487   size_changed =
3488     widget_allocation.width != allocation->width ||
3489     widget_allocation.height != allocation->height;
3490
3491   border_width = gtk_container_get_border_width (GTK_CONTAINER (text_view));
3492
3493   gtk_widget_set_allocation (widget, allocation);
3494
3495   if (gtk_widget_get_realized (widget))
3496     {
3497       gdk_window_move_resize (gtk_widget_get_window (widget),
3498                               allocation->x, allocation->y,
3499                               allocation->width, allocation->height);
3500     }
3501
3502   /* distribute width/height among child windows. Ensure all
3503    * windows get at least a 1x1 allocation.
3504    */
3505
3506   gtk_widget_style_get (widget,
3507                         "interior-focus", &interior_focus,
3508                         "focus-line-width", &focus_width,
3509                         NULL);
3510
3511   if (interior_focus)
3512     focus_edge_width = 0;
3513   else
3514     focus_edge_width = focus_width;
3515   
3516   width = allocation->width - focus_edge_width * 2 - border_width * 2;
3517
3518   if (priv->left_window)
3519     left_rect.width = priv->left_window->requisition.width;
3520   else
3521     left_rect.width = 0;
3522
3523   width -= left_rect.width;
3524
3525   if (priv->right_window)
3526     right_rect.width = priv->right_window->requisition.width;
3527   else
3528     right_rect.width = 0;
3529
3530   width -= right_rect.width;
3531
3532   text_rect.width = MAX (1, width);
3533
3534   top_rect.width = text_rect.width;
3535   bottom_rect.width = text_rect.width;
3536
3537
3538   height = allocation->height - focus_edge_width * 2 - border_width * 2;
3539
3540   if (priv->top_window)
3541     top_rect.height = priv->top_window->requisition.height;
3542   else
3543     top_rect.height = 0;
3544
3545   height -= top_rect.height;
3546
3547   if (priv->bottom_window)
3548     bottom_rect.height = priv->bottom_window->requisition.height;
3549   else
3550     bottom_rect.height = 0;
3551
3552   height -= bottom_rect.height;
3553
3554   text_rect.height = MAX (1, height);
3555
3556   left_rect.height = text_rect.height;
3557   right_rect.height = text_rect.height;
3558
3559   /* Origins */
3560   left_rect.x = focus_edge_width + border_width;
3561   top_rect.y = focus_edge_width + border_width;
3562
3563   text_rect.x = left_rect.x + left_rect.width;
3564   text_rect.y = top_rect.y + top_rect.height;
3565
3566   left_rect.y = text_rect.y;
3567   right_rect.y = text_rect.y;
3568
3569   top_rect.x = text_rect.x;
3570   bottom_rect.x = text_rect.x;
3571
3572   right_rect.x = text_rect.x + text_rect.width;
3573   bottom_rect.y = text_rect.y + text_rect.height;
3574
3575   text_window_size_allocate (priv->text_window,
3576                              &text_rect);
3577
3578   if (priv->left_window)
3579     text_window_size_allocate (priv->left_window,
3580                                &left_rect);
3581
3582   if (priv->right_window)
3583     text_window_size_allocate (priv->right_window,
3584                                &right_rect);
3585
3586   if (priv->top_window)
3587     text_window_size_allocate (priv->top_window,
3588                                &top_rect);
3589
3590   if (priv->bottom_window)
3591     text_window_size_allocate (priv->bottom_window,
3592                                &bottom_rect);
3593
3594   gtk_text_view_update_layout_width (text_view);
3595   
3596   /* Note that this will do some layout validation */
3597   gtk_text_view_allocate_children (text_view);
3598
3599   /* Ensure h/v adj exist */
3600   get_hadjustment (text_view);
3601   get_vadjustment (text_view);
3602
3603   priv->hadjustment->page_size = SCREEN_WIDTH (text_view);
3604   priv->hadjustment->page_increment = SCREEN_WIDTH (text_view) * 0.9;
3605   priv->hadjustment->step_increment = SCREEN_WIDTH (text_view) * 0.1;
3606   priv->hadjustment->lower = 0;
3607   priv->hadjustment->upper = MAX (SCREEN_WIDTH (text_view),
3608                                   priv->width);
3609
3610   if (priv->hadjustment->value > priv->hadjustment->upper - priv->hadjustment->page_size)
3611     gtk_adjustment_set_value (priv->hadjustment, MAX (0, priv->hadjustment->upper - priv->hadjustment->page_size));
3612
3613   gtk_adjustment_changed (priv->hadjustment);
3614
3615   priv->vadjustment->page_size = SCREEN_HEIGHT (text_view);
3616   priv->vadjustment->page_increment = SCREEN_HEIGHT (text_view) * 0.9;
3617   priv->vadjustment->step_increment = SCREEN_HEIGHT (text_view) * 0.1;
3618   priv->vadjustment->lower = 0;
3619   priv->vadjustment->upper = MAX (SCREEN_HEIGHT (text_view),
3620                                   priv->height);
3621
3622   /* Now adjust the value of the adjustment to keep the cursor at the
3623    * same place in the buffer
3624    */
3625   gtk_text_view_get_first_para_iter (text_view, &first_para);
3626   gtk_text_layout_get_line_yrange (priv->layout, &first_para, &y, NULL);
3627
3628   y += priv->first_para_pixels;
3629
3630   if (y > priv->vadjustment->upper - priv->vadjustment->page_size)
3631     y = MAX (0, priv->vadjustment->upper - priv->vadjustment->page_size);
3632
3633   if (y != priv->yoffset)
3634     gtk_adjustment_set_value (priv->vadjustment, y);
3635
3636   gtk_adjustment_changed (priv->vadjustment);
3637
3638   /* The GTK resize loop processes all the pending exposes right
3639    * after doing the resize stuff, so the idle sizer won't have a
3640    * chance to run. So we do the work here. 
3641    */
3642   gtk_text_view_flush_first_validate (text_view);
3643
3644   /* widget->window doesn't get auto-redrawn as the layout is computed, so has to
3645    * be invalidated
3646    */
3647   if (size_changed && gtk_widget_get_realized (widget))
3648     gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
3649 }
3650
3651 static void
3652 gtk_text_view_get_first_para_iter (GtkTextView *text_view,
3653                                    GtkTextIter *iter)
3654 {
3655   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), iter,
3656                                     text_view->priv->first_para_mark);
3657 }
3658
3659 static void
3660 gtk_text_view_validate_onscreen (GtkTextView *text_view)
3661 {
3662   GtkWidget *widget;
3663   GtkTextViewPrivate *priv;
3664
3665   widget = GTK_WIDGET (text_view);
3666   priv = text_view->priv;
3667   
3668   DV(g_print(">Validating onscreen ("G_STRLOC")\n"));
3669   
3670   if (SCREEN_HEIGHT (widget) > 0)
3671     {
3672       GtkTextIter first_para;
3673
3674       /* Be sure we've validated the stuff onscreen; if we
3675        * scrolled, these calls won't have any effect, because
3676        * they were called in the recursive validate_onscreen
3677        */
3678       gtk_text_view_get_first_para_iter (text_view, &first_para);
3679
3680       gtk_text_layout_validate_yrange (priv->layout,
3681                                        &first_para,
3682                                        0,
3683                                        priv->first_para_pixels +
3684                                        SCREEN_HEIGHT (widget));
3685     }
3686
3687   priv->onscreen_validated = TRUE;
3688
3689   DV(g_print(">Done validating onscreen, onscreen_validated = TRUE ("G_STRLOC")\n"));
3690   
3691   /* This can have the odd side effect of triggering a scroll, which should
3692    * flip "onscreen_validated" back to FALSE, but should also get us
3693    * back into this function to turn it on again.
3694    */
3695   gtk_text_view_update_adjustments (text_view);
3696
3697   g_assert (priv->onscreen_validated);
3698 }
3699
3700 static void
3701 gtk_text_view_flush_first_validate (GtkTextView *text_view)
3702 {
3703   GtkTextViewPrivate *priv = text_view->priv;
3704
3705   if (priv->first_validate_idle == 0)
3706     return;
3707
3708   /* Do this first, which means that if an "invalidate"
3709    * occurs during any of this process, a new first_validate_callback
3710    * will be installed, and we'll start again.
3711    */
3712   DV (g_print ("removing first validate in %s\n", G_STRLOC));
3713   g_source_remove (priv->first_validate_idle);
3714   priv->first_validate_idle = 0;
3715   
3716   /* be sure we have up-to-date screen size set on the
3717    * layout.
3718    */
3719   gtk_text_view_update_layout_width (text_view);
3720
3721   /* Bail out if we invalidated stuff; scrolling right away will just
3722    * confuse the issue.
3723    */
3724   if (priv->first_validate_idle != 0)
3725     {
3726       DV(g_print(">Width change forced requeue ("G_STRLOC")\n"));
3727     }
3728   else
3729     {
3730       /* scroll to any marks, if that's pending. This can jump us to
3731        * the validation codepath used for scrolling onscreen, if so we
3732        * bail out.  It won't jump if already in that codepath since
3733        * value_changed is not recursive, so also validate if
3734        * necessary.
3735        */
3736       if (!gtk_text_view_flush_scroll (text_view) ||
3737           !priv->onscreen_validated)
3738         gtk_text_view_validate_onscreen (text_view);
3739       
3740       DV(g_print(">Leaving first validate idle ("G_STRLOC")\n"));
3741       
3742       g_assert (priv->onscreen_validated);
3743     }
3744 }
3745
3746 static gboolean
3747 first_validate_callback (gpointer data)
3748 {
3749   GtkTextView *text_view = data;
3750
3751   /* Note that some of this code is duplicated at the end of size_allocate,
3752    * keep in sync with that.
3753    */
3754   
3755   DV(g_print(G_STRLOC"\n"));
3756
3757   gtk_text_view_flush_first_validate (text_view);
3758   
3759   return FALSE;
3760 }
3761
3762 static gboolean
3763 incremental_validate_callback (gpointer data)
3764 {
3765   GtkTextView *text_view = data;
3766   gboolean result = TRUE;
3767
3768   DV(g_print(G_STRLOC"\n"));
3769   
3770   gtk_text_layout_validate (text_view->priv->layout, 2000);
3771
3772   gtk_text_view_update_adjustments (text_view);
3773   
3774   if (gtk_text_layout_is_valid (text_view->priv->layout))
3775     {
3776       text_view->priv->incremental_validate_idle = 0;
3777       result = FALSE;
3778     }
3779
3780   return result;
3781 }
3782
3783 static void
3784 gtk_text_view_invalidate (GtkTextView *text_view)
3785 {
3786   GtkTextViewPrivate *priv = text_view->priv;
3787
3788   DV (g_print (">Invalidate, onscreen_validated = %d now FALSE ("G_STRLOC")\n",
3789                priv->onscreen_validated));
3790
3791   priv->onscreen_validated = FALSE;
3792
3793   /* We'll invalidate when the layout is created */
3794   if (priv->layout == NULL)
3795     return;
3796   
3797   if (!priv->first_validate_idle)
3798     {
3799       priv->first_validate_idle = gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, first_validate_callback, text_view, NULL);
3800       DV (g_print (G_STRLOC": adding first validate idle %d\n",
3801                    priv->first_validate_idle));
3802     }
3803       
3804   if (!priv->incremental_validate_idle)
3805     {
3806       priv->incremental_validate_idle = gdk_threads_add_idle_full (GTK_TEXT_VIEW_PRIORITY_VALIDATE, incremental_validate_callback, text_view, NULL);
3807       DV (g_print (G_STRLOC": adding incremental validate idle %d\n",
3808                    priv->incremental_validate_idle));
3809     }
3810 }
3811
3812 static void
3813 invalidated_handler (GtkTextLayout *layout,
3814                      gpointer       data)
3815 {
3816   GtkTextView *text_view;
3817
3818   text_view = GTK_TEXT_VIEW (data);
3819
3820   DV (g_print ("Invalidating due to layout invalidate signal\n"));
3821   gtk_text_view_invalidate (text_view);
3822 }
3823
3824 static void
3825 changed_handler (GtkTextLayout     *layout,
3826                  gint               start_y,
3827                  gint               old_height,
3828                  gint               new_height,
3829                  gpointer           data)
3830 {
3831   GtkTextView *text_view;
3832   GtkTextViewPrivate *priv;
3833   GtkWidget *widget;
3834   GdkRectangle visible_rect;
3835   GdkRectangle redraw_rect;
3836   
3837   text_view = GTK_TEXT_VIEW (data);
3838   priv = text_view->priv;
3839   widget = GTK_WIDGET (data);
3840   
3841   DV(g_print(">Lines Validated ("G_STRLOC")\n"));
3842
3843   if (gtk_widget_get_realized (widget))
3844     {      
3845       gtk_text_view_get_visible_rect (text_view, &visible_rect);
3846
3847       redraw_rect.x = visible_rect.x;
3848       redraw_rect.width = visible_rect.width;
3849       redraw_rect.y = start_y;
3850
3851       if (old_height == new_height)
3852         redraw_rect.height = old_height;
3853       else if (start_y + old_height > visible_rect.y)
3854         redraw_rect.height = MAX (0, visible_rect.y + visible_rect.height - start_y);
3855       else
3856         redraw_rect.height = 0;
3857         
3858       if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
3859         {
3860           /* text_window_invalidate_rect() takes buffer coordinates */
3861           text_window_invalidate_rect (priv->text_window,
3862                                        &redraw_rect);
3863
3864           DV(g_print(" invalidated rect: %d,%d %d x %d\n",
3865                      redraw_rect.x,
3866                      redraw_rect.y,
3867                      redraw_rect.width,
3868                      redraw_rect.height));
3869           
3870           if (priv->left_window)
3871             text_window_invalidate_rect (priv->left_window,
3872                                          &redraw_rect);
3873           if (priv->right_window)
3874             text_window_invalidate_rect (priv->right_window,
3875                                          &redraw_rect);
3876           if (priv->top_window)
3877             text_window_invalidate_rect (priv->top_window,
3878                                          &redraw_rect);
3879           if (priv->bottom_window)
3880             text_window_invalidate_rect (priv->bottom_window,
3881                                          &redraw_rect);
3882
3883           queue_update_im_spot_location (text_view);
3884         }
3885     }
3886   
3887   if (old_height != new_height)
3888     {
3889       gboolean yoffset_changed = FALSE;
3890       GSList *tmp_list;
3891       int new_first_para_top;
3892       int old_first_para_top;
3893       GtkTextIter first;
3894       
3895       /* If the bottom of the old area was above the top of the
3896        * screen, we need to scroll to keep the current top of the
3897        * screen in place.  Remember that first_para_pixels is the
3898        * position of the top of the screen in coordinates relative to
3899        * the first paragraph onscreen.
3900        *
3901        * In short we are adding the height delta of the portion of the
3902        * changed region above first_para_mark to priv->yoffset.
3903        */
3904       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &first,
3905                                         priv->first_para_mark);
3906
3907       gtk_text_layout_get_line_yrange (layout, &first, &new_first_para_top, NULL);
3908
3909       old_first_para_top = priv->yoffset - priv->first_para_pixels;
3910
3911       if (new_first_para_top != old_first_para_top)
3912         {
3913           priv->yoffset += new_first_para_top - old_first_para_top;
3914           
3915           get_vadjustment (text_view)->value = priv->yoffset;
3916           yoffset_changed = TRUE;
3917         }
3918
3919       if (yoffset_changed)
3920         {
3921           DV(g_print ("Changing scroll position (%s)\n", G_STRLOC));
3922           gtk_adjustment_value_changed (get_vadjustment (text_view));
3923         }
3924
3925       /* FIXME be smarter about which anchored widgets we update */
3926
3927       tmp_list = priv->children;
3928       while (tmp_list != NULL)
3929         {
3930           GtkTextViewChild *child = tmp_list->data;
3931
3932           if (child->anchor)
3933             gtk_text_view_update_child_allocation (text_view, child);
3934
3935           tmp_list = g_slist_next (tmp_list);
3936         }
3937     }
3938
3939   {
3940     GtkRequisition old_req = priv->cached_size_request;
3941     GtkRequisition new_req;
3942
3943     /* Use this instead of gtk_widget_size_request wrapper
3944      * to avoid the optimization which just returns widget->requisition
3945      * if a resize hasn't been queued.
3946      */
3947     GTK_WIDGET_GET_CLASS (widget)->size_request (widget, &new_req);
3948
3949     if (old_req.width != new_req.width ||
3950         old_req.height != new_req.height)
3951       {
3952         gtk_widget_queue_resize_no_redraw (widget);
3953       }
3954   }
3955 }
3956
3957 static void
3958 gtk_text_view_realize (GtkWidget *widget)
3959 {
3960   GtkAllocation allocation;
3961   GtkTextView *text_view;
3962   GtkTextViewPrivate *priv;
3963   GdkWindow *window;
3964   GdkWindowAttr attributes;
3965   gint attributes_mask;
3966   GSList *tmp_list;
3967   
3968   text_view = GTK_TEXT_VIEW (widget);
3969   priv = text_view->priv;
3970
3971   gtk_widget_set_realized (widget, TRUE);
3972
3973   gtk_widget_get_allocation (widget, &allocation);
3974
3975   attributes.window_type = GDK_WINDOW_CHILD;
3976   attributes.x = allocation.x;
3977   attributes.y = allocation.y;
3978   attributes.width = allocation.width;
3979   attributes.height = allocation.height;
3980   attributes.wclass = GDK_INPUT_OUTPUT;
3981   attributes.visual = gtk_widget_get_visual (widget);
3982   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
3983
3984   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3985
3986   window = gdk_window_new (gtk_widget_get_parent_window (widget),
3987                            &attributes, attributes_mask);
3988   gtk_widget_set_window (widget, window);
3989   gdk_window_set_user_data (window, widget);
3990
3991   /* must come before text_window_realize calls */
3992   gtk_widget_style_attach (widget);
3993
3994   gdk_window_set_background (window,
3995                              &gtk_widget_get_style (widget)->bg[gtk_widget_get_state (widget)]);
3996
3997   text_window_realize (priv->text_window, widget);
3998
3999   if (priv->left_window)
4000     text_window_realize (priv->left_window, widget);
4001
4002   if (priv->top_window)
4003     text_window_realize (priv->top_window, widget);
4004
4005   if (priv->right_window)
4006     text_window_realize (priv->right_window, widget);
4007
4008   if (priv->bottom_window)
4009     text_window_realize (priv->bottom_window, widget);
4010
4011   gtk_text_view_ensure_layout (text_view);
4012
4013   if (priv->buffer)
4014     {
4015       GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
4016                                                           GDK_SELECTION_PRIMARY);
4017       gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard);
4018     }
4019
4020   tmp_list = priv->children;
4021   while (tmp_list != NULL)
4022     {
4023       GtkTextViewChild *vc = tmp_list->data;
4024       
4025       text_view_child_set_parent_window (text_view, vc);
4026       
4027       tmp_list = tmp_list->next;
4028     }
4029
4030   /* Ensure updating the spot location. */
4031   gtk_text_view_update_im_spot_location (text_view);
4032 }
4033
4034 static void
4035 gtk_text_view_unrealize (GtkWidget *widget)
4036 {
4037   GtkTextView *text_view;
4038   GtkTextViewPrivate *priv;
4039
4040   text_view = GTK_TEXT_VIEW (widget);
4041   priv = text_view->priv;
4042
4043   if (priv->buffer)
4044     {
4045       GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
4046                                                           GDK_SELECTION_PRIMARY);
4047       gtk_text_buffer_remove_selection_clipboard (priv->buffer, clipboard);
4048     }
4049
4050   gtk_text_view_remove_validate_idles (text_view);
4051
4052   if (priv->popup_menu)
4053     {
4054       gtk_widget_destroy (priv->popup_menu);
4055       priv->popup_menu = NULL;
4056     }
4057
4058   text_window_unrealize (priv->text_window);
4059
4060   if (priv->left_window)
4061     text_window_unrealize (priv->left_window);
4062
4063   if (priv->top_window)
4064     text_window_unrealize (priv->top_window);
4065
4066   if (priv->right_window)
4067     text_window_unrealize (priv->right_window);
4068
4069   if (priv->bottom_window)
4070     text_window_unrealize (priv->bottom_window);
4071
4072   gtk_text_view_destroy_layout (text_view);
4073
4074   GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget);
4075 }
4076
4077 static void
4078 gtk_text_view_set_background (GtkTextView *text_view)
4079 {
4080   GtkStyle *style;
4081   GtkStateType state;
4082   GtkWidget *widget;
4083   GtkTextViewPrivate *priv;
4084
4085   widget = GTK_WIDGET (text_view);
4086   priv = text_view->priv;
4087
4088   style = gtk_widget_get_style (widget);
4089   state = gtk_widget_get_state (widget);
4090
4091   gdk_window_set_background (gtk_widget_get_window (widget),
4092                              &style->bg[state]);
4093
4094   gdk_window_set_background (priv->text_window->bin_window,
4095                              &style->base[state]);
4096
4097   if (priv->left_window)
4098     gdk_window_set_background (priv->left_window->bin_window,
4099                                &style->bg[state]);
4100   if (priv->right_window)
4101     gdk_window_set_background (priv->right_window->bin_window,
4102                                &style->bg[state]);
4103
4104   if (priv->top_window)
4105     gdk_window_set_background (priv->top_window->bin_window,
4106                                &style->bg[state]);
4107
4108   if (priv->bottom_window)
4109     gdk_window_set_background (priv->bottom_window->bin_window,
4110                                &style->bg[state]);
4111 }
4112
4113 static void
4114 gtk_text_view_style_set (GtkWidget *widget,
4115                          GtkStyle  *previous_style)
4116 {
4117   GtkTextView *text_view;
4118   GtkTextViewPrivate *priv;
4119   PangoContext *ltr_context, *rtl_context;
4120
4121   text_view = GTK_TEXT_VIEW (widget);
4122   priv = text_view->priv;
4123
4124   if (gtk_widget_get_realized (widget))
4125     {
4126       gtk_text_view_set_background (text_view);
4127     }
4128
4129   if (priv->layout && previous_style)
4130     {
4131       gtk_text_view_set_attributes_from_style (text_view,
4132                                                priv->layout->default_style,
4133                                                gtk_widget_get_style (widget));
4134
4135       ltr_context = gtk_widget_create_pango_context (widget);
4136       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
4137       rtl_context = gtk_widget_create_pango_context (widget);
4138       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
4139
4140       gtk_text_layout_set_contexts (priv->layout, ltr_context, rtl_context);
4141
4142       g_object_unref (ltr_context);
4143       g_object_unref (rtl_context);
4144     }
4145 }
4146
4147 static void
4148 gtk_text_view_direction_changed (GtkWidget        *widget,
4149                                  GtkTextDirection  previous_direction)
4150 {
4151   GtkTextViewPrivate *priv = GTK_TEXT_VIEW (widget)->priv;
4152
4153   if (priv->layout)
4154     {
4155       priv->layout->default_style->direction = gtk_widget_get_direction (widget);
4156
4157       gtk_text_layout_default_style_changed (priv->layout);
4158     }
4159 }
4160
4161 static void
4162 gtk_text_view_state_changed (GtkWidget      *widget,
4163                              GtkStateType    previous_state)
4164 {
4165   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
4166   GdkCursor *cursor;
4167
4168   if (gtk_widget_get_realized (widget))
4169     {
4170       gtk_text_view_set_background (text_view);
4171
4172       if (gtk_widget_is_sensitive (widget))
4173         cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
4174       else
4175         cursor = NULL;
4176
4177       gdk_window_set_cursor (text_view->priv->text_window->bin_window, cursor);
4178
4179       if (cursor)
4180       gdk_cursor_unref (cursor);
4181
4182       text_view->priv->mouse_cursor_obscured = FALSE;
4183     }
4184
4185   if (!gtk_widget_is_sensitive (widget))
4186     {
4187       /* Clear any selection */
4188       gtk_text_view_unselect (text_view);
4189     }
4190   
4191   gtk_widget_queue_draw (widget);
4192 }
4193
4194 static void
4195 set_invisible_cursor (GdkWindow *window)
4196 {
4197   GdkDisplay *display;
4198   GdkCursor *cursor;
4199
4200   display = gdk_window_get_display (window);
4201   cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR);
4202  
4203   gdk_window_set_cursor (window, cursor);
4204   
4205   gdk_cursor_unref (cursor);
4206 }
4207
4208 static void
4209 gtk_text_view_obscure_mouse_cursor (GtkTextView *text_view)
4210 {
4211   if (text_view->priv->mouse_cursor_obscured)
4212     return;
4213
4214   set_invisible_cursor (text_view->priv->text_window->bin_window);
4215   
4216   text_view->priv->mouse_cursor_obscured = TRUE;
4217 }
4218
4219 static void
4220 gtk_text_view_unobscure_mouse_cursor (GtkTextView *text_view)
4221 {
4222   if (text_view->priv->mouse_cursor_obscured)
4223     {
4224       GdkCursor *cursor;
4225       
4226       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)),
4227                                            GDK_XTERM);
4228       gdk_window_set_cursor (text_view->priv->text_window->bin_window, cursor);
4229       gdk_cursor_unref (cursor);
4230       text_view->priv->mouse_cursor_obscured = FALSE;
4231     }
4232 }
4233
4234 static void
4235 gtk_text_view_grab_notify (GtkWidget *widget,
4236                            gboolean   was_grabbed)
4237 {
4238   GtkTextViewPrivate *priv;
4239
4240   priv = GTK_TEXT_VIEW (widget)->priv;
4241
4242   if (priv->grab_device &&
4243       gtk_widget_device_is_shadowed (widget, priv->grab_device))
4244     {
4245       gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
4246       gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
4247     }
4248 }
4249
4250
4251 /*
4252  * Events
4253  */
4254
4255 static gboolean
4256 get_event_coordinates (GdkEvent *event, gint *x, gint *y)
4257 {
4258   if (event)
4259     switch (event->type)
4260       {
4261       case GDK_MOTION_NOTIFY:
4262         *x = event->motion.x;
4263         *y = event->motion.y;
4264         return TRUE;
4265         break;
4266
4267       case GDK_BUTTON_PRESS:
4268       case GDK_2BUTTON_PRESS:
4269       case GDK_3BUTTON_PRESS:
4270       case GDK_BUTTON_RELEASE:
4271         *x = event->button.x;
4272         *y = event->button.y;
4273         return TRUE;
4274         break;
4275
4276       case GDK_KEY_PRESS:
4277       case GDK_KEY_RELEASE:
4278       case GDK_ENTER_NOTIFY:
4279       case GDK_LEAVE_NOTIFY:
4280       case GDK_PROPERTY_NOTIFY:
4281       case GDK_SELECTION_CLEAR:
4282       case GDK_SELECTION_REQUEST:
4283       case GDK_SELECTION_NOTIFY:
4284       case GDK_PROXIMITY_IN:
4285       case GDK_PROXIMITY_OUT:
4286       case GDK_DRAG_ENTER:
4287       case GDK_DRAG_LEAVE:
4288       case GDK_DRAG_MOTION:
4289       case GDK_DRAG_STATUS:
4290       case GDK_DROP_START:
4291       case GDK_DROP_FINISHED:
4292       default:
4293         return FALSE;
4294         break;
4295       }
4296
4297   return FALSE;
4298 }
4299
4300 static gint
4301 emit_event_on_tags (GtkWidget   *widget,
4302                     GdkEvent    *event,
4303                     GtkTextIter *iter)
4304 {
4305   GSList *tags;
4306   GSList *tmp;
4307   gboolean retval = FALSE;
4308
4309   tags = gtk_text_iter_get_tags (iter);
4310
4311   tmp = tags;
4312   while (tmp != NULL)
4313     {
4314       GtkTextTag *tag = tmp->data;
4315
4316       if (gtk_text_tag_event (tag, G_OBJECT (widget), event, iter))
4317         {
4318           retval = TRUE;
4319           break;
4320         }
4321
4322       tmp = g_slist_next (tmp);
4323     }
4324
4325   g_slist_free (tags);
4326
4327   return retval;
4328 }
4329
4330 static gint
4331 gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
4332 {
4333   GtkTextView *text_view;
4334   GtkTextViewPrivate *priv;
4335   gint x = 0, y = 0;
4336
4337   text_view = GTK_TEXT_VIEW (widget);
4338   priv = text_view->priv;
4339
4340   if (priv->layout == NULL ||
4341       get_buffer (text_view) == NULL)
4342     return FALSE;
4343
4344   if (event->any.window != priv->text_window->bin_window)
4345     return FALSE;
4346
4347   if (get_event_coordinates (event, &x, &y))
4348     {
4349       GtkTextIter iter;
4350
4351       x += priv->xoffset;
4352       y += priv->yoffset;
4353
4354       /* FIXME this is slow and we do it twice per event.
4355        * My favorite solution is to have GtkTextLayout cache
4356        * the last couple lookups.
4357        */
4358       gtk_text_layout_get_iter_at_pixel (priv->layout,
4359                                          &iter,
4360                                          x, y);
4361
4362       return emit_event_on_tags (widget, event, &iter);
4363     }
4364   else if (event->type == GDK_KEY_PRESS ||
4365            event->type == GDK_KEY_RELEASE)
4366     {
4367       GtkTextIter iter;
4368
4369       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter,
4370                                         gtk_text_buffer_get_insert (get_buffer (text_view)));
4371
4372       return emit_event_on_tags (widget, event, &iter);
4373     }
4374   else
4375     return FALSE;
4376 }
4377
4378 static gint
4379 gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
4380 {
4381   GtkTextView *text_view;
4382   GtkTextViewPrivate *priv;
4383   GtkTextMark *insert;
4384   GtkTextIter iter;
4385   gboolean can_insert;
4386   gboolean retval = FALSE;
4387   gboolean obscure = FALSE;
4388
4389   text_view = GTK_TEXT_VIEW (widget);
4390   priv = text_view->priv;
4391   
4392   if (priv->layout == NULL ||
4393       get_buffer (text_view) == NULL)
4394     return FALSE;
4395
4396   /* Make sure input method knows where it is */
4397   flush_update_im_spot_location (text_view);
4398
4399   insert = gtk_text_buffer_get_insert (get_buffer (text_view));
4400   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
4401   can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
4402   if (gtk_im_context_filter_keypress (priv->im_context, event))
4403     {
4404       priv->need_im_reset = TRUE;
4405       if (!can_insert)
4406         gtk_text_view_reset_im_context (text_view);
4407       obscure = can_insert;
4408       retval = TRUE;
4409     }
4410   /* Binding set */
4411   else if (GTK_WIDGET_CLASS (gtk_text_view_parent_class)->key_press_event (widget, event))
4412     {
4413       retval = TRUE;
4414     }
4415   /* use overall editability not can_insert, more predictable for users */
4416   else if (priv->editable &&
4417            (event->keyval == GDK_KEY_Return ||
4418             event->keyval == GDK_KEY_ISO_Enter ||
4419             event->keyval == GDK_KEY_KP_Enter))
4420     {
4421       /* this won't actually insert the newline if the cursor isn't
4422        * editable
4423        */
4424       gtk_text_view_reset_im_context (text_view);
4425       gtk_text_view_commit_text (text_view, "\n");
4426
4427       obscure = TRUE;
4428       retval = TRUE;
4429     }
4430   /* Pass through Tab as literal tab, unless Control is held down */
4431   else if ((event->keyval == GDK_KEY_Tab ||
4432             event->keyval == GDK_KEY_KP_Tab ||
4433             event->keyval == GDK_KEY_ISO_Left_Tab) &&
4434            !(event->state & GDK_CONTROL_MASK))
4435     {
4436       /* If the text widget isn't editable overall, or if the application
4437        * has turned off "accepts_tab", move the focus instead
4438        */
4439       if (priv->accepts_tab && priv->editable)
4440         {
4441           gtk_text_view_reset_im_context (text_view);
4442           gtk_text_view_commit_text (text_view, "\t");
4443           obscure = TRUE;
4444         }
4445       else
4446         g_signal_emit_by_name (text_view, "move-focus",
4447                                (event->state & GDK_SHIFT_MASK) ?
4448                                GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
4449
4450       retval = TRUE;
4451     }
4452   else
4453     retval = FALSE;
4454
4455   if (obscure)
4456     gtk_text_view_obscure_mouse_cursor (text_view);
4457   
4458   gtk_text_view_reset_blink_time (text_view);
4459   gtk_text_view_pend_cursor_blink (text_view);
4460
4461   return retval;
4462 }
4463
4464 static gint
4465 gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
4466 {
4467   GtkTextView *text_view;
4468   GtkTextViewPrivate *priv;
4469   GtkTextMark *insert;
4470   GtkTextIter iter;
4471
4472   text_view = GTK_TEXT_VIEW (widget);
4473   priv = text_view->priv;
4474
4475   if (priv->layout == NULL || get_buffer (text_view) == NULL)
4476     return FALSE;
4477       
4478   insert = gtk_text_buffer_get_insert (get_buffer (text_view));
4479   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
4480   if (gtk_text_iter_can_insert (&iter, priv->editable) &&
4481       gtk_im_context_filter_keypress (priv->im_context, event))
4482     {
4483       priv->need_im_reset = TRUE;
4484       return TRUE;
4485     }
4486   else
4487     return GTK_WIDGET_CLASS (gtk_text_view_parent_class)->key_release_event (widget, event);
4488 }
4489
4490 static gint
4491 gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
4492 {
4493   GtkTextView *text_view;
4494   GtkTextViewPrivate *priv;
4495
4496   text_view = GTK_TEXT_VIEW (widget);
4497   priv = text_view->priv;
4498
4499   gtk_widget_grab_focus (widget);
4500
4501   if (event->window != priv->text_window->bin_window)
4502     {
4503       /* Remove selection if any. */
4504       gtk_text_view_unselect (text_view);
4505       return FALSE;
4506     }
4507
4508   gtk_text_view_reset_blink_time (text_view);
4509
4510 #if 0
4511   /* debug hack */
4512   if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
4513     _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
4514   else if (event->button == 3)
4515     gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
4516 #endif
4517
4518   if (event->type == GDK_BUTTON_PRESS)
4519     {
4520       gtk_text_view_reset_im_context (text_view);
4521
4522       if (event->button == 1)
4523         {
4524           /* If we're in the selection, start a drag copy/move of the
4525            * selection; otherwise, start creating a new selection.
4526            */
4527           GtkTextIter iter;
4528           GtkTextIter start, end;
4529
4530           gtk_text_layout_get_iter_at_pixel (priv->layout,
4531                                              &iter,
4532                                              event->x + priv->xoffset,
4533                                              event->y + priv->yoffset);
4534
4535           if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
4536                                                     &start, &end) &&
4537               gtk_text_iter_in_range (&iter, &start, &end) &&
4538               !(event->state & GDK_SHIFT_MASK))
4539             {
4540               priv->drag_start_x = event->x;
4541               priv->drag_start_y = event->y;
4542               priv->pending_place_cursor_button = event->button;
4543             }
4544           else
4545             {
4546               gtk_text_view_start_selection_drag (text_view, &iter, event);
4547             }
4548
4549           return TRUE;
4550         }
4551       else if (event->button == 2)
4552         {
4553           GtkTextIter iter;
4554
4555           /* We do not want to scroll back to the insert iter when we paste
4556              with the middle button */
4557           priv->scroll_after_paste = FALSE;
4558
4559           gtk_text_layout_get_iter_at_pixel (priv->layout,
4560                                              &iter,
4561                                              event->x + priv->xoffset,
4562                                              event->y + priv->yoffset);
4563
4564           gtk_text_buffer_paste_clipboard (get_buffer (text_view),
4565                                            gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY),
4566                                            &iter,
4567                                            priv->editable);
4568           return TRUE;
4569         }
4570       else if (event->button == 3)
4571         {
4572           gtk_text_view_do_popup (text_view, event);
4573           return TRUE;
4574         }
4575     }
4576   else if ((event->type == GDK_2BUTTON_PRESS ||
4577             event->type == GDK_3BUTTON_PRESS) &&
4578            event->button == 1) 
4579     {
4580       GtkTextIter iter;
4581
4582       gtk_text_view_end_selection_drag (text_view);
4583
4584       gtk_text_layout_get_iter_at_pixel (priv->layout,
4585                                          &iter,
4586                                          event->x + priv->xoffset,
4587                                          event->y + priv->yoffset);
4588       
4589       gtk_text_view_start_selection_drag (text_view, &iter, event);
4590       return TRUE;
4591     }
4592   
4593   return FALSE;
4594 }
4595
4596 static gint
4597 gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
4598 {
4599   GtkTextView *text_view;
4600   GtkTextViewPrivate *priv;
4601
4602   text_view = GTK_TEXT_VIEW (widget);
4603   priv = text_view->priv;
4604
4605   if (event->window != priv->text_window->bin_window)
4606     return FALSE;
4607
4608   if (event->button == 1)
4609     {
4610       if (priv->drag_start_x >= 0)
4611         {
4612           priv->drag_start_x = -1;
4613           priv->drag_start_y = -1;
4614         }
4615
4616       if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget)))
4617         return TRUE;
4618       else if (priv->pending_place_cursor_button == event->button)
4619         {
4620           GtkTextIter iter;
4621
4622           /* Unselect everything; we clicked inside selection, but
4623            * didn't move by the drag threshold, so just clear selection
4624            * and place cursor.
4625            */
4626           gtk_text_layout_get_iter_at_pixel (priv->layout,
4627                                              &iter,
4628                                              event->x + priv->xoffset,
4629                                              event->y + priv->yoffset);
4630
4631           gtk_text_buffer_place_cursor (get_buffer (text_view), &iter);
4632           gtk_text_view_check_cursor_blink (text_view);
4633           
4634           priv->pending_place_cursor_button = 0;
4635           
4636           return FALSE;
4637         }
4638     }
4639
4640   return FALSE;
4641 }
4642
4643 static void
4644 keymap_direction_changed (GdkKeymap   *keymap,
4645                           GtkTextView *text_view)
4646 {
4647   gtk_text_view_check_keymap_direction (text_view);
4648 }
4649
4650 static gint
4651 gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
4652 {
4653   GtkTextView *text_view;
4654   GtkTextViewPrivate *priv;
4655
4656   text_view = GTK_TEXT_VIEW (widget);
4657   priv = text_view->priv;
4658
4659   gtk_widget_queue_draw (widget);
4660
4661   DV(g_print (G_STRLOC": focus_in_event\n"));
4662
4663   gtk_text_view_reset_blink_time (text_view);
4664
4665   if (priv->cursor_visible && priv->layout)
4666     {
4667       gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
4668       gtk_text_view_check_cursor_blink (text_view);
4669     }
4670
4671   g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
4672                     "direction-changed",
4673                     G_CALLBACK (keymap_direction_changed), text_view);
4674   gtk_text_view_check_keymap_direction (text_view);
4675
4676   if (priv->editable)
4677     {
4678       priv->need_im_reset = TRUE;
4679       gtk_im_context_focus_in (priv->im_context);
4680     }
4681
4682   return FALSE;
4683 }
4684
4685 static gint
4686 gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
4687 {
4688   GtkTextView *text_view;
4689   GtkTextViewPrivate *priv;
4690
4691   text_view = GTK_TEXT_VIEW (widget);
4692   priv = text_view->priv;
4693
4694   gtk_text_view_end_selection_drag (text_view);
4695
4696   gtk_widget_queue_draw (widget);
4697
4698   DV(g_print (G_STRLOC": focus_out_event\n"));
4699   
4700   if (priv->cursor_visible && priv->layout)
4701     {
4702       gtk_text_view_check_cursor_blink (text_view);
4703       gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
4704     }
4705
4706   g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
4707                                         keymap_direction_changed,
4708                                         text_view);
4709
4710   if (priv->editable)
4711     {
4712       priv->need_im_reset = TRUE;
4713       gtk_im_context_focus_out (priv->im_context);
4714     }
4715
4716   return FALSE;
4717 }
4718
4719 static gint
4720 gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
4721 {
4722   GtkTextView *text_view;
4723   GtkTextViewPrivate *priv;
4724
4725   text_view = GTK_TEXT_VIEW (widget);
4726   priv = text_view->priv;
4727
4728   gtk_text_view_unobscure_mouse_cursor (text_view);
4729
4730   if (event->window == priv->text_window->bin_window &&
4731       priv->drag_start_x >= 0)
4732     {
4733       gint x = event->x;
4734       gint y = event->y;
4735
4736       gdk_event_request_motions (event);
4737
4738       if (gtk_drag_check_threshold (widget,
4739                                     priv->drag_start_x, 
4740                                     priv->drag_start_y,
4741                                     x, y))
4742         {
4743           GtkTextIter iter;
4744           gint buffer_x, buffer_y;
4745
4746           gtk_text_view_window_to_buffer_coords (text_view,
4747                                                  GTK_TEXT_WINDOW_TEXT,
4748                                                  priv->drag_start_x,
4749                                                  priv->drag_start_y,
4750                                                  &buffer_x,
4751                                                  &buffer_y);
4752
4753           gtk_text_layout_get_iter_at_pixel (priv->layout,
4754                                              &iter,
4755                                              buffer_x, buffer_y);
4756
4757           gtk_text_view_start_selection_dnd (text_view, &iter, event);
4758           return TRUE;
4759         }
4760     }
4761
4762   return FALSE;
4763 }
4764
4765 static void
4766 gtk_text_view_paint (GtkWidget      *widget,
4767                      cairo_t        *cr)
4768 {
4769   GtkTextView *text_view;
4770   GtkTextViewPrivate *priv;
4771   GList *child_exposes;
4772   GList *tmp_list;
4773   
4774   text_view = GTK_TEXT_VIEW (widget);
4775   priv = text_view->priv;
4776
4777   g_return_if_fail (priv->layout != NULL);
4778   g_return_if_fail (priv->xoffset >= 0);
4779   g_return_if_fail (priv->yoffset >= 0);
4780
4781   while (priv->first_validate_idle != 0)
4782     {
4783       DV (g_print (G_STRLOC": first_validate_idle: %d\n",
4784                    priv->first_validate_idle));
4785       gtk_text_view_flush_first_validate (text_view);
4786     }
4787
4788   if (!priv->onscreen_validated)
4789     {
4790       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.");
4791       g_assert_not_reached ();
4792     }
4793   
4794 #if 0
4795   printf ("painting %d,%d  %d x %d\n",
4796           area->x, area->y,
4797           area->width, area->height);
4798 #endif
4799
4800   child_exposes = NULL;
4801
4802   cairo_save (cr);
4803   cairo_translate (cr, -priv->xoffset, -priv->yoffset);
4804
4805   gtk_text_layout_draw (priv->layout,
4806                         widget,
4807                         cr,
4808                         &child_exposes);
4809
4810   cairo_restore (cr);
4811
4812   tmp_list = child_exposes;
4813   while (tmp_list != NULL)
4814     {
4815       GtkWidget *child = tmp_list->data;
4816   
4817       gtk_container_propagate_draw (GTK_CONTAINER (text_view),
4818                                     child,
4819                                     cr);
4820
4821       g_object_unref (child);
4822       
4823       tmp_list = tmp_list->next;
4824     }
4825
4826   g_list_free (child_exposes);
4827 }
4828
4829 static gboolean
4830 gtk_text_view_draw (GtkWidget *widget,
4831                     cairo_t   *cr)
4832 {
4833   GSList *tmp_list;
4834   GdkWindow *window;
4835   
4836   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
4837     gtk_text_view_draw_focus (widget, cr);
4838
4839   window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget),
4840                                      GTK_TEXT_WINDOW_TEXT);
4841   if (gtk_cairo_should_draw_window (cr, window))
4842     {
4843       DV(g_print (">Exposed ("G_STRLOC")\n"));
4844       cairo_save (cr);
4845       gtk_cairo_transform_to_window (cr, widget, window);
4846       gtk_text_view_paint (widget, cr);
4847       cairo_restore (cr);
4848     }
4849
4850   /* Propagate exposes to all unanchored children. 
4851    * Anchored children are handled in gtk_text_view_paint(). 
4852    */
4853   tmp_list = GTK_TEXT_VIEW (widget)->priv->children;
4854   while (tmp_list != NULL)
4855     {
4856       GtkTextViewChild *vc = tmp_list->data;
4857
4858       /* propagate_draw checks that event->window matches
4859        * child->window
4860        */
4861       if (!vc->anchor)
4862         gtk_container_propagate_draw (GTK_CONTAINER (widget),
4863                                       vc->widget,
4864                                       cr);
4865       
4866       tmp_list = tmp_list->next;
4867     }
4868   
4869   return FALSE;
4870 }
4871
4872 static void
4873 gtk_text_view_draw_focus (GtkWidget *widget,
4874                           cairo_t   *cr)
4875 {
4876   gboolean interior_focus;
4877
4878   /* We clear the focus if we are in interior focus mode. */
4879   gtk_widget_style_get (widget,
4880                         "interior-focus", &interior_focus,
4881                         NULL);
4882   
4883   if (gtk_widget_has_focus (widget) && !interior_focus)
4884     {          
4885       gtk_paint_focus (gtk_widget_get_style (widget), cr,
4886                        gtk_widget_get_state (widget),
4887                        widget, "textview",
4888                        0, 0,
4889                        gtk_widget_get_allocated_width (widget),
4890                        gtk_widget_get_allocated_height (widget));
4891     }
4892 }
4893
4894 static gboolean
4895 gtk_text_view_focus (GtkWidget        *widget,
4896                      GtkDirectionType  direction)
4897 {
4898   GtkContainer *container;
4899   gboolean result;
4900   
4901   container = GTK_CONTAINER (widget);  
4902
4903   if (!gtk_widget_is_focus (widget) &&
4904       gtk_container_get_focus_child (container) == NULL)
4905     {
4906       gtk_widget_grab_focus (widget);
4907       return TRUE;
4908     }
4909   else
4910     {
4911       /*
4912        * Unset CAN_FOCUS flag so that gtk_container_focus() allows
4913        * children to get the focus
4914        */
4915       gtk_widget_set_can_focus (widget, FALSE);
4916       result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction);
4917       gtk_widget_set_can_focus (widget, TRUE);
4918
4919       return result;
4920     }
4921 }
4922
4923 /*
4924  * Container
4925  */
4926
4927 static void
4928 gtk_text_view_add (GtkContainer *container,
4929                    GtkWidget    *child)
4930 {
4931   /* This is pretty random. */
4932   gtk_text_view_add_child_in_window (GTK_TEXT_VIEW (container),
4933                                      child,
4934                                      GTK_TEXT_WINDOW_WIDGET,
4935                                      0, 0);
4936 }
4937
4938 static void
4939 gtk_text_view_remove (GtkContainer *container,
4940                       GtkWidget    *child)
4941 {
4942   GtkTextView *text_view;
4943   GtkTextViewPrivate *priv;
4944   GtkTextViewChild *vc;
4945   GSList *iter;
4946
4947   text_view = GTK_TEXT_VIEW (container);
4948   priv = text_view->priv;
4949
4950   vc = NULL;
4951   iter = priv->children;
4952
4953   while (iter != NULL)
4954     {
4955       vc = iter->data;
4956
4957       if (vc->widget == child)
4958         break;
4959
4960       iter = g_slist_next (iter);
4961     }
4962
4963   g_assert (iter != NULL); /* be sure we had the child in the list */
4964
4965   priv->children = g_slist_remove (priv->children, vc);
4966
4967   gtk_widget_unparent (vc->widget);
4968
4969   text_view_child_free (vc);
4970 }
4971
4972 static void
4973 gtk_text_view_forall (GtkContainer *container,
4974                       gboolean      include_internals,
4975                       GtkCallback   callback,
4976                       gpointer      callback_data)
4977 {
4978   GSList *iter;
4979   GtkTextView *text_view;
4980   GSList *copy;
4981
4982   g_return_if_fail (GTK_IS_TEXT_VIEW (container));
4983   g_return_if_fail (callback != NULL);
4984
4985   text_view = GTK_TEXT_VIEW (container);
4986
4987   copy = g_slist_copy (text_view->priv->children);
4988   iter = copy;
4989
4990   while (iter != NULL)
4991     {
4992       GtkTextViewChild *vc = iter->data;
4993
4994       (* callback) (vc->widget, callback_data);
4995
4996       iter = g_slist_next (iter);
4997     }
4998
4999   g_slist_free (copy);
5000 }
5001
5002 #define CURSOR_ON_MULTIPLIER 2
5003 #define CURSOR_OFF_MULTIPLIER 1
5004 #define CURSOR_PEND_MULTIPLIER 3
5005 #define CURSOR_DIVIDER 3
5006
5007 static gboolean
5008 cursor_blinks (GtkTextView *text_view)
5009 {
5010   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5011   gboolean blink;
5012
5013 #ifdef DEBUG_VALIDATION_AND_SCROLLING
5014   return FALSE;
5015 #endif
5016   if (gtk_get_debug_flags () & GTK_DEBUG_UPDATES)
5017     return FALSE;
5018
5019   g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
5020
5021   if (!blink)
5022     return FALSE;
5023
5024   if (text_view->priv->editable)
5025     {
5026       GtkTextMark *insert;
5027       GtkTextIter iter;
5028       
5029       insert = gtk_text_buffer_get_insert (get_buffer (text_view));
5030       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, insert);
5031       
5032       if (gtk_text_iter_editable (&iter, text_view->priv->editable))
5033         return blink;
5034     }
5035
5036   return FALSE;
5037 }
5038
5039 static gint
5040 get_cursor_time (GtkTextView *text_view)
5041 {
5042   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5043   gint time;
5044
5045   g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
5046
5047   return time;
5048 }
5049
5050 static gint
5051 get_cursor_blink_timeout (GtkTextView *text_view)
5052 {
5053   GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
5054   gint time;
5055
5056   g_object_get (settings, "gtk-cursor-blink-timeout", &time, NULL);
5057
5058   return time;
5059 }
5060
5061
5062 /*
5063  * Blink!
5064  */
5065
5066 static gint
5067 blink_cb (gpointer data)
5068 {
5069   GtkTextView *text_view;
5070   GtkTextViewPrivate *priv;
5071   gboolean visible;
5072   gint blink_timeout;
5073
5074   text_view = GTK_TEXT_VIEW (data);
5075   priv = text_view->priv;
5076
5077   if (!gtk_widget_has_focus (GTK_WIDGET (text_view)))
5078     {
5079       g_warning ("GtkTextView - did not receive focus-out-event. If you\n"
5080                  "connect a handler to this signal, it must return\n"
5081                  "FALSE so the text view gets the event as well");
5082
5083       gtk_text_view_check_cursor_blink (text_view);
5084
5085       return FALSE;
5086     }
5087
5088   g_assert (priv->layout);
5089   g_assert (priv->cursor_visible);
5090
5091   visible = gtk_text_layout_get_cursor_visible (priv->layout);
5092
5093   blink_timeout = get_cursor_blink_timeout (text_view);
5094   if (priv->blink_time > 1000 * blink_timeout &&
5095       blink_timeout < G_MAXINT/1000) 
5096     {
5097       /* we've blinked enough without the user doing anything, stop blinking */
5098       visible = 0;
5099       priv->blink_timeout = 0;
5100     } 
5101   else if (visible)
5102     priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
5103                                                    blink_cb,
5104                                                    text_view);
5105   else 
5106     {
5107       priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
5108                                                      blink_cb,
5109                                                      text_view);
5110       priv->blink_time += get_cursor_time (text_view);
5111     }
5112
5113   /* Block changed_handler while changing the layout's cursor visibility
5114    * because it would expose the whole paragraph. Instead, we expose
5115    * the cursor's area(s) manually below.
5116    */
5117   g_signal_handlers_block_by_func (priv->layout,
5118                                    changed_handler,
5119                                    text_view);
5120   gtk_text_layout_set_cursor_visible (priv->layout, !visible);
5121   g_signal_handlers_unblock_by_func (priv->layout,
5122                                      changed_handler,
5123                                      text_view);
5124
5125   text_window_invalidate_cursors (priv->text_window);
5126
5127   /* Remove ourselves */
5128   return FALSE;
5129 }
5130
5131
5132 static void
5133 gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
5134 {
5135   if (text_view->priv->blink_timeout)
5136     { 
5137       g_source_remove (text_view->priv->blink_timeout);
5138       text_view->priv->blink_timeout = 0;
5139     }
5140 }
5141
5142 static void
5143 gtk_text_view_check_cursor_blink (GtkTextView *text_view)
5144 {
5145   GtkTextViewPrivate *priv = text_view->priv;
5146
5147   if (priv->layout != NULL &&
5148       priv->cursor_visible &&
5149       gtk_widget_has_focus (GTK_WIDGET (text_view)))
5150     {
5151       if (cursor_blinks (text_view))
5152         {
5153           if (priv->blink_timeout == 0)
5154             {
5155               gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
5156               
5157               priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
5158                                                              blink_cb,
5159                                                              text_view);
5160             }
5161         }
5162       else
5163         {
5164           gtk_text_view_stop_cursor_blink (text_view);
5165           gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
5166         }
5167     }
5168   else
5169     {
5170       gtk_text_view_stop_cursor_blink (text_view);
5171       gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
5172     }
5173 }
5174
5175 static void
5176 gtk_text_view_pend_cursor_blink (GtkTextView *text_view)
5177 {
5178   GtkTextViewPrivate *priv = text_view->priv;
5179
5180   if (priv->layout != NULL &&
5181       priv->cursor_visible &&
5182       gtk_widget_has_focus (GTK_WIDGET (text_view)) &&
5183       cursor_blinks (text_view))
5184     {
5185       gtk_text_view_stop_cursor_blink (text_view);
5186       gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
5187       
5188       priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (text_view) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
5189                                                      blink_cb,
5190                                                      text_view);
5191     }
5192 }
5193
5194 static void
5195 gtk_text_view_reset_blink_time (GtkTextView *text_view)
5196 {
5197   GtkTextViewPrivate *priv = text_view->priv;
5198
5199   priv->blink_time = 0;
5200 }
5201
5202
5203 /*
5204  * Key binding handlers
5205  */
5206
5207 static gboolean
5208 gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
5209                                   GtkTextIter *newplace,
5210                                   gint         count)
5211 {
5212   gboolean ret = TRUE;
5213
5214   while (count < 0)
5215     {
5216       ret = gtk_text_layout_move_iter_to_previous_line (text_view->priv->layout, newplace);
5217       count++;
5218     }
5219
5220   while (count > 0)
5221     {
5222       ret = gtk_text_layout_move_iter_to_next_line (text_view->priv->layout, newplace);
5223       count--;
5224     }
5225
5226   return ret;
5227 }
5228
5229 static void
5230 move_cursor (GtkTextView       *text_view,
5231              const GtkTextIter *new_location,
5232              gboolean           extend_selection)
5233 {
5234   if (extend_selection)
5235     gtk_text_buffer_move_mark_by_name (get_buffer (text_view),
5236                                        "insert",
5237                                        new_location);
5238   else
5239       gtk_text_buffer_place_cursor (get_buffer (text_view),
5240                                     new_location);
5241   gtk_text_view_check_cursor_blink (text_view);
5242 }
5243
5244 static void
5245 gtk_text_view_move_cursor_internal (GtkTextView     *text_view,
5246                                     GtkMovementStep  step,
5247                                     gint             count,
5248                                     gboolean         extend_selection)
5249 {
5250   GtkTextViewPrivate *priv;
5251   GtkTextIter insert;
5252   GtkTextIter newplace;
5253   gboolean cancel_selection = FALSE;
5254   gint cursor_x_pos = 0;
5255   GtkDirectionType leave_direction = -1;
5256
5257   priv = text_view->priv;
5258
5259   if (!priv->cursor_visible) 
5260     {
5261       GtkScrollStep scroll_step;
5262
5263       switch (step) 
5264         {
5265         case GTK_MOVEMENT_VISUAL_POSITIONS:
5266           leave_direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
5267           /* fall through */
5268         case GTK_MOVEMENT_LOGICAL_POSITIONS:
5269         case GTK_MOVEMENT_WORDS:
5270           scroll_step = GTK_SCROLL_HORIZONTAL_STEPS;
5271           break;
5272         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5273           scroll_step = GTK_SCROLL_HORIZONTAL_ENDS;
5274           break;          
5275         case GTK_MOVEMENT_DISPLAY_LINES:
5276           leave_direction = count > 0 ? GTK_DIR_DOWN : GTK_DIR_UP;
5277           /* fall through */
5278         case GTK_MOVEMENT_PARAGRAPHS:
5279         case GTK_MOVEMENT_PARAGRAPH_ENDS:
5280           scroll_step = GTK_SCROLL_STEPS;
5281           break;
5282         case GTK_MOVEMENT_PAGES:
5283           scroll_step = GTK_SCROLL_PAGES;
5284           break;
5285         case GTK_MOVEMENT_HORIZONTAL_PAGES:
5286           scroll_step = GTK_SCROLL_HORIZONTAL_PAGES;
5287           break;
5288         case GTK_MOVEMENT_BUFFER_ENDS:
5289           scroll_step = GTK_SCROLL_ENDS;
5290           break;
5291         default:
5292           scroll_step = GTK_SCROLL_PAGES;
5293           break;
5294         }
5295
5296       if (!gtk_text_view_move_viewport (text_view, scroll_step, count))
5297         {
5298           if (leave_direction != -1 &&
5299               !gtk_widget_keynav_failed (GTK_WIDGET (text_view),
5300                                          leave_direction))
5301             {
5302               g_signal_emit_by_name (text_view, "move-focus", leave_direction);
5303             }
5304         }
5305
5306       return;
5307     }
5308
5309   gtk_text_view_reset_im_context (text_view);
5310
5311   if (step == GTK_MOVEMENT_PAGES)
5312     {
5313       if (!gtk_text_view_scroll_pages (text_view, count, extend_selection))
5314         gtk_widget_error_bell (GTK_WIDGET (text_view));
5315
5316       gtk_text_view_check_cursor_blink (text_view);
5317       gtk_text_view_pend_cursor_blink (text_view);
5318       return;
5319     }
5320   else if (step == GTK_MOVEMENT_HORIZONTAL_PAGES)
5321     {
5322       if (!gtk_text_view_scroll_hpages (text_view, count, extend_selection))
5323         gtk_widget_error_bell (GTK_WIDGET (text_view));
5324
5325       gtk_text_view_check_cursor_blink (text_view);
5326       gtk_text_view_pend_cursor_blink (text_view);
5327       return;
5328     }
5329
5330   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
5331                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
5332
5333   if (! extend_selection)
5334     {
5335       GtkTextIter sel_bound;
5336
5337       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &sel_bound,
5338                                         gtk_text_buffer_get_selection_bound (get_buffer (text_view)));
5339
5340       /* if we move forward, assume the cursor is at the end of the selection;
5341        * if we move backward, assume the cursor is at the start
5342        */
5343       if (count > 0)
5344         gtk_text_iter_order (&sel_bound, &insert);
5345       else
5346         gtk_text_iter_order (&insert, &sel_bound);
5347
5348       /* if we actually have a selection, just move *to* the beginning/end
5349        * of the selection and not *from* there on LOGICAL_POSITIONS
5350        * and VISUAL_POSITIONS movement
5351        */
5352       if (! gtk_text_iter_equal (&sel_bound, &insert))
5353         cancel_selection = TRUE;
5354     }
5355
5356   newplace = insert;
5357
5358   if (step == GTK_MOVEMENT_DISPLAY_LINES)
5359     gtk_text_view_get_virtual_cursor_pos (text_view, &insert, &cursor_x_pos, NULL);
5360
5361   switch (step)
5362     {
5363     case GTK_MOVEMENT_LOGICAL_POSITIONS:
5364       if (! cancel_selection)
5365         gtk_text_iter_forward_visible_cursor_positions (&newplace, count);
5366       break;
5367
5368     case GTK_MOVEMENT_VISUAL_POSITIONS:
5369       if (! cancel_selection)
5370         gtk_text_layout_move_iter_visually (priv->layout,
5371                                             &newplace, count);
5372       break;
5373
5374     case GTK_MOVEMENT_WORDS:
5375       if (count < 0)
5376         gtk_text_iter_backward_visible_word_starts (&newplace, -count);
5377       else if (count > 0) 
5378         {
5379           if (!gtk_text_iter_forward_visible_word_ends (&newplace, count))
5380             gtk_text_iter_forward_to_line_end (&newplace);
5381         }
5382       break;
5383
5384     case GTK_MOVEMENT_DISPLAY_LINES:
5385       if (count < 0)
5386         {
5387           leave_direction = GTK_DIR_UP;
5388
5389           if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
5390             gtk_text_layout_move_iter_to_x (priv->layout, &newplace, cursor_x_pos);
5391           else
5392             gtk_text_iter_set_line_offset (&newplace, 0);
5393         }
5394       if (count > 0)
5395         {
5396           leave_direction = GTK_DIR_DOWN;
5397
5398           if (gtk_text_view_move_iter_by_lines (text_view, &newplace, count))
5399             gtk_text_layout_move_iter_to_x (priv->layout, &newplace, cursor_x_pos);
5400           else
5401             gtk_text_iter_forward_to_line_end (&newplace);
5402         }
5403       break;
5404
5405     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
5406       if (count > 1)
5407         gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
5408       else if (count < -1)
5409         gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
5410
5411       if (count != 0)
5412         gtk_text_layout_move_iter_to_line_end (priv->layout, &newplace, count);
5413       break;
5414
5415     case GTK_MOVEMENT_PARAGRAPHS:
5416       if (count > 0)
5417         {
5418           if (!gtk_text_iter_ends_line (&newplace))
5419             {
5420               gtk_text_iter_forward_to_line_end (&newplace);
5421               --count;
5422             }
5423           gtk_text_iter_forward_visible_lines (&newplace, count);
5424           gtk_text_iter_forward_to_line_end (&newplace);
5425         }
5426       else if (count < 0)
5427         {
5428           if (gtk_text_iter_get_line_offset (&newplace) > 0)
5429             gtk_text_iter_set_line_offset (&newplace, 0);
5430           gtk_text_iter_forward_visible_lines (&newplace, count);
5431           gtk_text_iter_set_line_offset (&newplace, 0);
5432         }
5433       break;
5434
5435     case GTK_MOVEMENT_PARAGRAPH_ENDS:
5436       if (count > 0)
5437         {
5438           if (!gtk_text_iter_ends_line (&newplace))
5439             gtk_text_iter_forward_to_line_end (&newplace);
5440         }
5441       else if (count < 0)
5442         {
5443           gtk_text_iter_set_line_offset (&newplace, 0);
5444         }
5445       break;
5446
5447     case GTK_MOVEMENT_BUFFER_ENDS:
5448       if (count > 0)
5449         gtk_text_buffer_get_end_iter (get_buffer (text_view), &newplace);
5450       else if (count < 0)
5451         gtk_text_buffer_get_iter_at_offset (get_buffer (text_view), &newplace, 0);
5452      break;
5453       
5454     default:
5455       break;
5456     }
5457
5458   /* call move_cursor() even if the cursor hasn't moved, since it 
5459      cancels the selection
5460   */
5461   move_cursor (text_view, &newplace, extend_selection);
5462
5463   if (!gtk_text_iter_equal (&insert, &newplace))
5464     {
5465       DV(g_print (G_STRLOC": scrolling onscreen\n"));
5466       gtk_text_view_scroll_mark_onscreen (text_view,
5467                                           gtk_text_buffer_get_insert (get_buffer (text_view)));
5468
5469       if (step == GTK_MOVEMENT_DISPLAY_LINES)
5470         gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
5471     }
5472   else if (leave_direction != -1)
5473     {
5474       if (!gtk_widget_keynav_failed (GTK_WIDGET (text_view),
5475                                      leave_direction))
5476         {
5477           g_signal_emit_by_name (text_view, "move-focus", leave_direction);
5478         }
5479     }
5480   else if (! cancel_selection)
5481     {
5482       gtk_widget_error_bell (GTK_WIDGET (text_view));
5483     }
5484
5485   gtk_text_view_check_cursor_blink (text_view);
5486   gtk_text_view_pend_cursor_blink (text_view);
5487 }
5488
5489 static void
5490 gtk_text_view_move_cursor (GtkTextView     *text_view,
5491                            GtkMovementStep  step,
5492                            gint             count,
5493                            gboolean         extend_selection)
5494 {
5495   gtk_text_view_move_cursor_internal (text_view, step, count, extend_selection);
5496 }
5497
5498 static gboolean
5499 gtk_text_view_move_viewport (GtkTextView     *text_view,
5500                              GtkScrollStep    step,
5501                              gint             count)
5502 {
5503   GtkAdjustment *adjustment;
5504   gdouble increment;
5505   
5506   switch (step) 
5507     {
5508     case GTK_SCROLL_STEPS:
5509     case GTK_SCROLL_PAGES:
5510     case GTK_SCROLL_ENDS:
5511       adjustment = get_vadjustment (text_view);
5512       break;
5513     case GTK_SCROLL_HORIZONTAL_STEPS:
5514     case GTK_SCROLL_HORIZONTAL_PAGES:
5515     case GTK_SCROLL_HORIZONTAL_ENDS:
5516       adjustment = get_hadjustment (text_view);
5517       break;
5518     default:
5519       adjustment = get_vadjustment (text_view);
5520       break;
5521     }
5522
5523   switch (step) 
5524     {
5525     case GTK_SCROLL_STEPS:
5526     case GTK_SCROLL_HORIZONTAL_STEPS:
5527       increment = adjustment->step_increment;
5528       break;
5529     case GTK_SCROLL_PAGES:
5530     case GTK_SCROLL_HORIZONTAL_PAGES:
5531       increment = adjustment->page_increment;
5532       break;
5533     case GTK_SCROLL_ENDS:
5534     case GTK_SCROLL_HORIZONTAL_ENDS:
5535       increment = adjustment->upper - adjustment->lower;
5536       break;
5537     default:
5538       increment = 0.0;
5539       break;
5540     }
5541
5542   return set_adjustment_clamped (adjustment, adjustment->value + count * increment);
5543 }
5544
5545 static void
5546 gtk_text_view_set_anchor (GtkTextView *text_view)
5547 {
5548   GtkTextIter insert;
5549
5550   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
5551                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
5552
5553   gtk_text_buffer_create_mark (get_buffer (text_view), "anchor", &insert, TRUE);
5554 }
5555
5556 static gboolean
5557 gtk_text_view_scroll_pages (GtkTextView *text_view,
5558                             gint         count,
5559                             gboolean     extend_selection)
5560 {
5561   GtkTextViewPrivate *priv;
5562   GtkAdjustment *adj;
5563   gint cursor_x_pos, cursor_y_pos;
5564   GtkTextMark *insert_mark;
5565   GtkTextIter old_insert;
5566   GtkTextIter new_insert;
5567   GtkTextIter anchor;
5568   gdouble newval;
5569   gdouble oldval;
5570   gint y0, y1;
5571
5572   priv = text_view->priv;
5573
5574   g_return_val_if_fail (priv->vadjustment != NULL, FALSE);
5575   
5576   adj = priv->vadjustment;
5577
5578   insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view));
5579
5580   /* Make sure we start from the current cursor position, even
5581    * if it was offscreen, but don't queue more scrolls if we're
5582    * already behind.
5583    */
5584   if (priv->pending_scroll)
5585     cancel_pending_scroll (text_view);
5586   else
5587     gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
5588
5589   /* Validate the region that will be brought into view by the cursor motion
5590    */
5591   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5592                                     &old_insert, insert_mark);
5593
5594   if (count < 0)
5595     {
5596       gtk_text_view_get_first_para_iter (text_view, &anchor);
5597       y0 = adj->page_size;
5598       y1 = adj->page_size + count * adj->page_increment;
5599     }
5600   else
5601     {
5602       gtk_text_view_get_first_para_iter (text_view, &anchor);
5603       y0 = count * adj->page_increment + adj->page_size;
5604       y1 = 0;
5605     }
5606
5607   gtk_text_layout_validate_yrange (priv->layout, &anchor, y0, y1);
5608   /* FIXME do we need to update the adjustment ranges here? */
5609
5610   new_insert = old_insert;
5611
5612   if (count < 0 && adj->value <= (adj->lower + 1e-12))
5613     {
5614       /* already at top, just be sure we are at offset 0 */
5615       gtk_text_buffer_get_start_iter (get_buffer (text_view), &new_insert);
5616       move_cursor (text_view, &new_insert, extend_selection);
5617     }
5618   else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12))
5619     {
5620       /* already at bottom, just be sure we are at the end */
5621       gtk_text_buffer_get_end_iter (get_buffer (text_view), &new_insert);
5622       move_cursor (text_view, &new_insert, extend_selection);
5623     }
5624   else
5625     {
5626       gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos);
5627
5628       oldval = adj->value;
5629       newval = adj->value;
5630
5631       newval += count * adj->page_increment;
5632
5633       set_adjustment_clamped (adj, newval);
5634       cursor_y_pos += adj->value - oldval;
5635
5636       gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos);
5637       clamp_iter_onscreen (text_view, &new_insert);
5638       move_cursor (text_view, &new_insert, extend_selection);
5639
5640       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
5641     }
5642   
5643   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
5644    * only guarantees 1 pixel onscreen.
5645    */
5646   DV(g_print (G_STRLOC": scrolling onscreen\n"));
5647   gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
5648
5649   return !gtk_text_iter_equal (&old_insert, &new_insert);
5650 }
5651
5652 static gboolean
5653 gtk_text_view_scroll_hpages (GtkTextView *text_view,
5654                              gint         count,
5655                              gboolean     extend_selection)
5656 {
5657   GtkTextViewPrivate *priv;
5658   GtkAdjustment *adj;
5659   gint cursor_x_pos, cursor_y_pos;
5660   GtkTextMark *insert_mark;
5661   GtkTextIter old_insert;
5662   GtkTextIter new_insert;
5663   gdouble newval;
5664   gdouble oldval;
5665   gint y, height;
5666
5667   priv = text_view->priv;
5668
5669   g_return_val_if_fail (priv->hadjustment != NULL, FALSE);
5670
5671   adj = priv->hadjustment;
5672
5673   insert_mark = gtk_text_buffer_get_insert (get_buffer (text_view));
5674
5675   /* Make sure we start from the current cursor position, even
5676    * if it was offscreen, but don't queue more scrolls if we're
5677    * already behind.
5678    */
5679   if (priv->pending_scroll)
5680     cancel_pending_scroll (text_view);
5681   else
5682     gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
5683
5684   /* Validate the line that we're moving within.
5685    */
5686   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5687                                     &old_insert, insert_mark);
5688
5689   gtk_text_layout_get_line_yrange (priv->layout, &old_insert, &y, &height);
5690   gtk_text_layout_validate_yrange (priv->layout, &old_insert, y, y + height);
5691   /* FIXME do we need to update the adjustment ranges here? */
5692
5693   new_insert = old_insert;
5694
5695   if (count < 0 && adj->value <= (adj->lower + 1e-12))
5696     {
5697       /* already at far left, just be sure we are at offset 0 */
5698       gtk_text_iter_set_line_offset (&new_insert, 0);
5699       move_cursor (text_view, &new_insert, extend_selection);
5700     }
5701   else if (count > 0 && adj->value >= (adj->upper - adj->page_size - 1e-12))
5702     {
5703       /* already at far right, just be sure we are at the end */
5704       if (!gtk_text_iter_ends_line (&new_insert))
5705           gtk_text_iter_forward_to_line_end (&new_insert);
5706       move_cursor (text_view, &new_insert, extend_selection);
5707     }
5708   else
5709     {
5710       gtk_text_view_get_virtual_cursor_pos (text_view, NULL, &cursor_x_pos, &cursor_y_pos);
5711
5712       oldval = adj->value;
5713       newval = adj->value;
5714
5715       newval += count * adj->page_increment;
5716
5717       set_adjustment_clamped (adj, newval);
5718       cursor_x_pos += adj->value - oldval;
5719
5720       gtk_text_layout_get_iter_at_pixel (priv->layout, &new_insert, cursor_x_pos, cursor_y_pos);
5721       clamp_iter_onscreen (text_view, &new_insert);
5722       move_cursor (text_view, &new_insert, extend_selection);
5723
5724       gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
5725     }
5726
5727   /*  FIXME for lines shorter than the overall widget width, this results in a
5728    *  "bounce" effect as we scroll to the right of the widget, then scroll
5729    *  back to get the end of the line onscreen.
5730    *      http://bugzilla.gnome.org/show_bug.cgi?id=68963
5731    */
5732   
5733   /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
5734    * only guarantees 1 pixel onscreen.
5735    */
5736   DV(g_print (G_STRLOC": scrolling onscreen\n"));
5737   gtk_text_view_scroll_mark_onscreen (text_view, insert_mark);
5738
5739   return !gtk_text_iter_equal (&old_insert, &new_insert);
5740 }
5741
5742 static gboolean
5743 whitespace (gunichar ch, gpointer user_data)
5744 {
5745   return (ch == ' ' || ch == '\t');
5746 }
5747
5748 static gboolean
5749 not_whitespace (gunichar ch, gpointer user_data)
5750 {
5751   return !whitespace (ch, user_data);
5752 }
5753
5754 static gboolean
5755 find_whitepace_region (const GtkTextIter *center,
5756                        GtkTextIter *start, GtkTextIter *end)
5757 {
5758   *start = *center;
5759   *end = *center;
5760
5761   if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL, NULL))
5762     gtk_text_iter_forward_char (start); /* we want the first whitespace... */
5763   if (whitespace (gtk_text_iter_get_char (end), NULL))
5764     gtk_text_iter_forward_find_char (end, not_whitespace, NULL, NULL);
5765
5766   return !gtk_text_iter_equal (start, end);
5767 }
5768
5769 static void
5770 gtk_text_view_insert_at_cursor (GtkTextView *text_view,
5771                                 const gchar *str)
5772 {
5773   if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
5774                                                      text_view->priv->editable))
5775     {
5776       gtk_widget_error_bell (GTK_WIDGET (text_view));
5777     }
5778 }
5779
5780 static void
5781 gtk_text_view_delete_from_cursor (GtkTextView   *text_view,
5782                                   GtkDeleteType  type,
5783                                   gint           count)
5784 {
5785   GtkTextViewPrivate *priv;
5786   GtkTextIter insert;
5787   GtkTextIter start;
5788   GtkTextIter end;
5789   gboolean leave_one = FALSE;
5790
5791   priv = text_view->priv;
5792
5793   gtk_text_view_reset_im_context (text_view);
5794
5795   if (type == GTK_DELETE_CHARS)
5796     {
5797       /* Char delete deletes the selection, if one exists */
5798       if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
5799                                             priv->editable))
5800         return;
5801     }
5802
5803   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
5804                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
5805
5806   start = insert;
5807   end = insert;
5808
5809   switch (type)
5810     {
5811     case GTK_DELETE_CHARS:
5812       gtk_text_iter_forward_cursor_positions (&end, count);
5813       break;
5814
5815     case GTK_DELETE_WORD_ENDS:
5816       if (count > 0)
5817         gtk_text_iter_forward_word_ends (&end, count);
5818       else if (count < 0)
5819         gtk_text_iter_backward_word_starts (&start, 0 - count);
5820       break;
5821
5822     case GTK_DELETE_WORDS:
5823       break;
5824
5825     case GTK_DELETE_DISPLAY_LINE_ENDS:
5826       break;
5827
5828     case GTK_DELETE_DISPLAY_LINES:
5829       break;
5830
5831     case GTK_DELETE_PARAGRAPH_ENDS:
5832       if (count > 0)
5833         {
5834           /* If we're already at a newline, we need to
5835            * simply delete that newline, instead of
5836            * moving to the next one.
5837            */
5838           if (gtk_text_iter_ends_line (&end))
5839             {
5840               gtk_text_iter_forward_line (&end);
5841               --count;
5842             }
5843
5844           while (count > 0)
5845             {
5846               if (!gtk_text_iter_forward_to_line_end (&end))
5847                 break;
5848
5849               --count;
5850             }
5851         }
5852       else if (count < 0)
5853         {
5854           if (gtk_text_iter_starts_line (&start))
5855             {
5856               gtk_text_iter_backward_line (&start);
5857               if (!gtk_text_iter_ends_line (&end))
5858                 gtk_text_iter_forward_to_line_end (&start);
5859             }
5860           else
5861             {
5862               gtk_text_iter_set_line_offset (&start, 0);
5863             }
5864           ++count;
5865
5866           gtk_text_iter_backward_lines (&start, -count);
5867         }
5868       break;
5869
5870     case GTK_DELETE_PARAGRAPHS:
5871       if (count > 0)
5872         {
5873           gtk_text_iter_set_line_offset (&start, 0);
5874           gtk_text_iter_forward_to_line_end (&end);
5875
5876           /* Do the lines beyond the first. */
5877           while (count > 1)
5878             {
5879               gtk_text_iter_forward_to_line_end (&end);
5880
5881               --count;
5882             }
5883         }
5884
5885       /* FIXME negative count? */
5886
5887       break;
5888
5889     case GTK_DELETE_WHITESPACE:
5890       {
5891         find_whitepace_region (&insert, &start, &end);
5892       }
5893       break;
5894
5895     default:
5896       break;
5897     }
5898
5899   if (!gtk_text_iter_equal (&start, &end))
5900     {
5901       gtk_text_buffer_begin_user_action (get_buffer (text_view));
5902
5903       if (gtk_text_buffer_delete_interactive (get_buffer (text_view), &start, &end,
5904                                               priv->editable))
5905         {
5906           if (leave_one)
5907             gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view),
5908                                                           " ", 1,
5909                                                           priv->editable);
5910         }
5911       else
5912         {
5913           gtk_widget_error_bell (GTK_WIDGET (text_view));
5914         }
5915
5916       gtk_text_buffer_end_user_action (get_buffer (text_view));
5917       gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
5918
5919       DV(g_print (G_STRLOC": scrolling onscreen\n"));
5920       gtk_text_view_scroll_mark_onscreen (text_view,
5921                                           gtk_text_buffer_get_insert (get_buffer (text_view)));
5922     }
5923   else
5924     {
5925       gtk_widget_error_bell (GTK_WIDGET (text_view));
5926     }
5927 }
5928
5929 static void
5930 gtk_text_view_backspace (GtkTextView *text_view)
5931 {
5932   GtkTextViewPrivate *priv;
5933   GtkTextIter insert;
5934
5935   priv = text_view->priv;
5936
5937   gtk_text_view_reset_im_context (text_view);
5938
5939   /* Backspace deletes the selection, if one exists */
5940   if (gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
5941                                         priv->editable))
5942     return;
5943
5944   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
5945                                     &insert,
5946                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
5947
5948   if (gtk_text_buffer_backspace (get_buffer (text_view), &insert,
5949                                  TRUE, priv->editable))
5950     {
5951       gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
5952       DV(g_print (G_STRLOC": scrolling onscreen\n"));
5953       gtk_text_view_scroll_mark_onscreen (text_view,
5954                                           gtk_text_buffer_get_insert (get_buffer (text_view)));
5955     }
5956   else
5957     {
5958       gtk_widget_error_bell (GTK_WIDGET (text_view));
5959     }
5960 }
5961
5962 static void
5963 gtk_text_view_cut_clipboard (GtkTextView *text_view)
5964 {
5965   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
5966                                                       GDK_SELECTION_CLIPBOARD);
5967   
5968   gtk_text_buffer_cut_clipboard (get_buffer (text_view),
5969                                  clipboard,
5970                                  text_view->priv->editable);
5971   DV(g_print (G_STRLOC": scrolling onscreen\n"));
5972   gtk_text_view_scroll_mark_onscreen (text_view,
5973                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
5974 }
5975
5976 static void
5977 gtk_text_view_copy_clipboard (GtkTextView *text_view)
5978 {
5979   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
5980                                                       GDK_SELECTION_CLIPBOARD);
5981   
5982   gtk_text_buffer_copy_clipboard (get_buffer (text_view),
5983                                   clipboard);
5984
5985   /* on copy do not scroll, we are already onscreen */
5986 }
5987
5988 static void
5989 gtk_text_view_paste_clipboard (GtkTextView *text_view)
5990 {
5991   GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view),
5992                                                       GDK_SELECTION_CLIPBOARD);
5993   
5994   gtk_text_buffer_paste_clipboard (get_buffer (text_view),
5995                                    clipboard,
5996                                    NULL,
5997                                    text_view->priv->editable);
5998 }
5999
6000 static void
6001 gtk_text_view_paste_done_handler (GtkTextBuffer *buffer,
6002                                   GtkClipboard  *clipboard,
6003                                   gpointer       data)
6004 {
6005   GtkTextView *text_view = data;
6006   GtkTextViewPrivate *priv;
6007
6008   priv = text_view->priv;
6009
6010   if (priv->scroll_after_paste)
6011     {
6012       DV(g_print (G_STRLOC": scrolling onscreen\n"));
6013       gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer));
6014     }
6015
6016   priv->scroll_after_paste = TRUE;
6017 }
6018
6019 static void
6020 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
6021 {
6022   GtkTextViewPrivate *priv = text_view->priv;
6023
6024   if (priv->text_window)
6025     text_window_invalidate_cursors (priv->text_window);
6026
6027   priv->overwrite_mode = !priv->overwrite_mode;
6028
6029   if (priv->layout)
6030     gtk_text_layout_set_overwrite_mode (priv->layout,
6031                                         priv->overwrite_mode && priv->editable);
6032
6033   if (priv->text_window)
6034     text_window_invalidate_cursors (priv->text_window);
6035
6036   gtk_text_view_pend_cursor_blink (text_view);
6037
6038   g_object_notify (G_OBJECT (text_view), "overwrite");
6039 }
6040
6041 /**
6042  * gtk_text_view_get_overwrite:
6043  * @text_view: a #GtkTextView
6044  *
6045  * Returns whether the #GtkTextView is in overwrite mode or not.
6046  *
6047  * Return value: whether @text_view is in overwrite mode or not.
6048  * 
6049  * Since: 2.4
6050  **/
6051 gboolean
6052 gtk_text_view_get_overwrite (GtkTextView *text_view)
6053 {
6054   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6055
6056   return text_view->priv->overwrite_mode;
6057 }
6058
6059 /**
6060  * gtk_text_view_set_overwrite:
6061  * @text_view: a #GtkTextView
6062  * @overwrite: %TRUE to turn on overwrite mode, %FALSE to turn it off
6063  *
6064  * Changes the #GtkTextView overwrite mode.
6065  *
6066  * Since: 2.4
6067  **/
6068 void
6069 gtk_text_view_set_overwrite (GtkTextView *text_view,
6070                              gboolean     overwrite)
6071 {
6072   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
6073   overwrite = overwrite != FALSE;
6074
6075   if (text_view->priv->overwrite_mode != overwrite)
6076     gtk_text_view_toggle_overwrite (text_view);
6077 }
6078
6079 /**
6080  * gtk_text_view_set_accepts_tab:
6081  * @text_view: A #GtkTextView
6082  * @accepts_tab: %TRUE if pressing the Tab key should insert a tab 
6083  *    character, %FALSE, if pressing the Tab key should move the 
6084  *    keyboard focus.
6085  * 
6086  * Sets the behavior of the text widget when the Tab key is pressed. 
6087  * If @accepts_tab is %TRUE, a tab character is inserted. If @accepts_tab 
6088  * is %FALSE the keyboard focus is moved to the next widget in the focus 
6089  * chain.
6090  * 
6091  * Since: 2.4
6092  **/
6093 void
6094 gtk_text_view_set_accepts_tab (GtkTextView *text_view,
6095                                gboolean     accepts_tab)
6096 {
6097   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
6098
6099   accepts_tab = accepts_tab != FALSE;
6100
6101   if (text_view->priv->accepts_tab != accepts_tab)
6102     {
6103       text_view->priv->accepts_tab = accepts_tab;
6104
6105       g_object_notify (G_OBJECT (text_view), "accepts-tab");
6106     }
6107 }
6108
6109 /**
6110  * gtk_text_view_get_accepts_tab:
6111  * @text_view: A #GtkTextView
6112  * 
6113  * Returns whether pressing the Tab key inserts a tab characters.
6114  * gtk_text_view_set_accepts_tab().
6115  * 
6116  * Return value: %TRUE if pressing the Tab key inserts a tab character, 
6117  *   %FALSE if pressing the Tab key moves the keyboard focus.
6118  * 
6119  * Since: 2.4
6120  **/
6121 gboolean
6122 gtk_text_view_get_accepts_tab (GtkTextView *text_view)
6123 {
6124   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6125
6126   return text_view->priv->accepts_tab;
6127 }
6128
6129 /*
6130  * Selections
6131  */
6132
6133 static void
6134 gtk_text_view_unselect (GtkTextView *text_view)
6135 {
6136   GtkTextIter insert;
6137
6138   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
6139                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
6140
6141   gtk_text_buffer_move_mark (get_buffer (text_view),
6142                              gtk_text_buffer_get_selection_bound (get_buffer (text_view)),
6143                              &insert);
6144 }
6145
6146 static void
6147 get_iter_at_pointer (GtkTextView *text_view,
6148                      GdkDevice   *device,
6149                      GtkTextIter *iter,
6150                      gint        *x,
6151                      gint        *y)
6152 {
6153   GtkTextViewPrivate *priv;
6154   gint xcoord, ycoord;
6155   GdkModifierType state;
6156
6157   priv = text_view->priv;
6158
6159   gdk_window_get_device_position (priv->text_window->bin_window,
6160                                   device, &xcoord, &ycoord, &state);
6161
6162   gtk_text_layout_get_iter_at_pixel (priv->layout,
6163                                      iter,
6164                                      xcoord + priv->xoffset,
6165                                      ycoord + priv->yoffset);
6166   if (x)
6167     *x = xcoord;
6168
6169   if (y)
6170     *y = ycoord;
6171 }
6172
6173 static void
6174 move_mark_to_pointer_and_scroll (GtkTextView *text_view,
6175                                  const gchar *mark_name,
6176                                  GdkDevice   *device)
6177 {
6178   GtkTextIter newplace;
6179   GtkTextMark *mark;
6180
6181   get_iter_at_pointer (text_view, device, &newplace, NULL, NULL);
6182   
6183   mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
6184   
6185   /* This may invalidate the layout */
6186   DV(g_print (G_STRLOC": move mark\n"));
6187   
6188   gtk_text_buffer_move_mark (get_buffer (text_view),
6189                              mark,
6190                              &newplace);
6191   
6192   DV(g_print (G_STRLOC": scrolling onscreen\n"));
6193   gtk_text_view_scroll_mark_onscreen (text_view, mark);
6194
6195   DV (g_print ("first validate idle leaving %s is %d\n",
6196                G_STRLOC, text_view->priv->first_validate_idle));
6197 }
6198
6199 static gboolean
6200 selection_scan_timeout (gpointer data)
6201 {
6202   GtkTextView *text_view;
6203
6204   text_view = GTK_TEXT_VIEW (data);
6205
6206   gtk_text_view_scroll_mark_onscreen (text_view, 
6207                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
6208
6209   return TRUE; /* remain installed. */
6210 }
6211
6212 #define UPPER_OFFSET_ANCHOR 0.8
6213 #define LOWER_OFFSET_ANCHOR 0.2
6214
6215 static gboolean
6216 check_scroll (gdouble offset, GtkAdjustment *adj)
6217 {
6218   if ((offset > UPPER_OFFSET_ANCHOR &&
6219        adj->value + adj->page_size < adj->upper) ||
6220       (offset < LOWER_OFFSET_ANCHOR &&
6221        adj->value > adj->lower))
6222     return TRUE;
6223
6224   return FALSE;
6225 }
6226
6227 static gint
6228 drag_scan_timeout (gpointer data)
6229 {
6230   GtkTextView *text_view;
6231   GtkTextViewPrivate *priv;
6232   GtkTextIter newplace;
6233   gint x, y;
6234   gdouble pointer_xoffset, pointer_yoffset;
6235
6236   text_view = GTK_TEXT_VIEW (data);
6237   priv = text_view->priv;
6238
6239   get_iter_at_pointer (text_view, priv->dnd_device, &newplace, &x, &y);
6240
6241   gtk_text_buffer_move_mark (get_buffer (text_view),
6242                              priv->dnd_mark,
6243                              &newplace);
6244
6245   pointer_xoffset = (gdouble) x / gdk_window_get_width (priv->text_window->bin_window);
6246   pointer_yoffset = (gdouble) y / gdk_window_get_height (priv->text_window->bin_window);
6247
6248   if (check_scroll (pointer_xoffset, priv->hadjustment) ||
6249       check_scroll (pointer_yoffset, priv->vadjustment))
6250     {
6251       /* do not make offsets surpass lower nor upper anchors, this makes
6252        * scrolling speed relative to the distance of the pointer to the
6253        * anchors when it moves beyond them.
6254        */
6255       pointer_xoffset = CLAMP (pointer_xoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);
6256       pointer_yoffset = CLAMP (pointer_yoffset, LOWER_OFFSET_ANCHOR, UPPER_OFFSET_ANCHOR);
6257
6258       gtk_text_view_scroll_to_mark (text_view,
6259                                     priv->dnd_mark,
6260                                     0., TRUE, pointer_xoffset, pointer_yoffset);
6261     }
6262
6263   return TRUE;
6264 }
6265
6266 typedef enum 
6267 {
6268   SELECT_CHARACTERS,
6269   SELECT_WORDS,
6270   SELECT_LINES
6271 } SelectionGranularity;
6272
6273 /*
6274  * Move @start and @end to the boundaries of the selection unit (indicated by 
6275  * @granularity) which contained @start initially.
6276  * If the selction unit is SELECT_WORDS and @start is not contained in a word
6277  * the selection is extended to all the white spaces between the end of the 
6278  * word preceding @start and the start of the one following.
6279  */
6280 static void
6281 extend_selection (GtkTextView *text_view, 
6282                   SelectionGranularity granularity, 
6283                   GtkTextIter *start, 
6284                   GtkTextIter *end)
6285 {
6286   *end = *start;
6287
6288   if (granularity == SELECT_WORDS) 
6289     {
6290       if (gtk_text_iter_inside_word (start))
6291         {
6292           if (!gtk_text_iter_starts_word (start))
6293             gtk_text_iter_backward_visible_word_start (start);
6294           
6295           if (!gtk_text_iter_ends_word (end))
6296             {
6297               if (!gtk_text_iter_forward_visible_word_end (end))
6298                 gtk_text_iter_forward_to_end (end);
6299             }
6300         }
6301       else
6302         {
6303           GtkTextIter tmp;
6304
6305           tmp = *start;
6306           if (gtk_text_iter_backward_visible_word_start (&tmp))
6307             gtk_text_iter_forward_visible_word_end (&tmp);
6308
6309           if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (start))
6310             *start = tmp;
6311           else
6312             gtk_text_iter_set_line_offset (start, 0);
6313
6314           tmp = *end;
6315           if (!gtk_text_iter_forward_visible_word_end (&tmp))
6316             gtk_text_iter_forward_to_end (&tmp);
6317
6318           if (gtk_text_iter_ends_word (&tmp))
6319             gtk_text_iter_backward_visible_word_start (&tmp);
6320
6321           if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (end))
6322             *end = tmp;
6323           else
6324             gtk_text_iter_forward_to_line_end (end);
6325         }
6326     }
6327   else if (granularity == SELECT_LINES) 
6328     {
6329       if (gtk_text_view_starts_display_line (text_view, start))
6330         {
6331           /* If on a display line boundary, we assume the user
6332            * clicked off the end of a line and we therefore select
6333            * the line before the boundary.
6334            */
6335           gtk_text_view_backward_display_line_start (text_view, start);
6336         }
6337       else
6338         {
6339           /* start isn't on the start of a line, so we move it to the
6340            * start, and move end to the end unless it's already there.
6341            */
6342           gtk_text_view_backward_display_line_start (text_view, start);
6343           
6344           if (!gtk_text_view_starts_display_line (text_view, end))
6345             gtk_text_view_forward_display_line_end (text_view, end);
6346         }
6347     }
6348 }
6349  
6350
6351 typedef struct
6352 {
6353   SelectionGranularity granularity;
6354   GtkTextMark *orig_start;
6355   GtkTextMark *orig_end;
6356 } SelectionData;
6357
6358 static void
6359 selection_data_free (SelectionData *data)
6360 {
6361   if (data->orig_start != NULL)
6362     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (data->orig_start),
6363                                  data->orig_start);
6364   if (data->orig_end != NULL)
6365     gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (data->orig_end),
6366                                  data->orig_end);
6367   g_free (data);
6368 }
6369
6370 static gint
6371 selection_motion_event_handler (GtkTextView    *text_view, 
6372                                 GdkEventMotion *event, 
6373                                 SelectionData  *data)
6374 {
6375   GtkTextViewPrivate *priv;
6376
6377   priv = text_view->priv;
6378   gdk_event_request_motions (event);
6379
6380   if (priv->grab_device != event->device)
6381     return FALSE;
6382
6383   if (data->granularity == SELECT_CHARACTERS) 
6384     {
6385       move_mark_to_pointer_and_scroll (text_view, "insert", event->device);
6386     }
6387   else 
6388     {
6389       GtkTextIter cursor, start, end;
6390       GtkTextIter orig_start, orig_end;
6391       GtkTextBuffer *buffer;
6392       
6393       buffer = get_buffer (text_view);
6394
6395       gtk_text_buffer_get_iter_at_mark (buffer, &orig_start, data->orig_start);
6396       gtk_text_buffer_get_iter_at_mark (buffer, &orig_end, data->orig_end);
6397
6398       get_iter_at_pointer (text_view, event->device, &cursor, NULL, NULL);
6399       
6400       start = cursor;
6401       extend_selection (text_view, data->granularity, &start, &end);
6402
6403       /* either the selection extends to the front, or end (or not) */
6404       if (gtk_text_iter_compare (&cursor, &orig_start) < 0)
6405         gtk_text_buffer_select_range (buffer, &start, &orig_end);
6406       else
6407         gtk_text_buffer_select_range (buffer, &end, &orig_start);
6408
6409       gtk_text_view_scroll_mark_onscreen (text_view, 
6410                                           gtk_text_buffer_get_insert (buffer));
6411     }
6412
6413   /* If we had to scroll offscreen, insert a timeout to do so
6414    * again. Note that in the timeout, even if the mouse doesn't
6415    * move, due to this scroll xoffset/yoffset will have changed
6416    * and we'll need to scroll again.
6417    */
6418   if (text_view->priv->scroll_timeout != 0) /* reset on every motion event */
6419     g_source_remove (text_view->priv->scroll_timeout);
6420   
6421   text_view->priv->scroll_timeout =
6422     gdk_threads_add_timeout (50, selection_scan_timeout, text_view);
6423
6424   return TRUE;
6425 }
6426
6427 static void
6428 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
6429                                     const GtkTextIter *iter,
6430                                     GdkEventButton    *button)
6431 {
6432   GtkTextViewPrivate *priv;
6433   GtkTextIter cursor, ins, bound;
6434   GtkTextIter orig_start, orig_end;
6435   GtkTextBuffer *buffer;
6436   SelectionData *data;
6437
6438   if (text_view->priv->selection_drag_handler != 0)
6439     return;
6440
6441   priv = text_view->priv;
6442   data = g_new0 (SelectionData, 1);
6443
6444   if (button->type == GDK_2BUTTON_PRESS)
6445     data->granularity = SELECT_WORDS;
6446   else if (button->type == GDK_3BUTTON_PRESS)
6447     data->granularity = SELECT_LINES;
6448   else 
6449     data->granularity = SELECT_CHARACTERS;
6450
6451   priv->grab_device = button->device;
6452   gtk_device_grab_add (GTK_WIDGET (text_view),
6453                        priv->grab_device,
6454                        TRUE);
6455
6456   buffer = get_buffer (text_view);
6457   
6458   cursor = *iter;
6459   ins = cursor;
6460   
6461   extend_selection (text_view, data->granularity, &ins, &bound);
6462   orig_start = ins;
6463   orig_end = bound;
6464
6465   if (button->state & GDK_SHIFT_MASK)
6466     {
6467       /* Extend selection */
6468       GtkTextIter old_ins, old_bound;
6469       GtkTextIter old_start, old_end;
6470
6471       gtk_text_buffer_get_iter_at_mark (buffer, &old_ins, gtk_text_buffer_get_insert (buffer));
6472       gtk_text_buffer_get_iter_at_mark (buffer, &old_bound, gtk_text_buffer_get_selection_bound (buffer));
6473       old_start = old_ins;
6474       old_end = old_bound;
6475       gtk_text_iter_order (&old_start, &old_end);
6476       
6477       /* move the front cursor, if the mouse is in front of the selection. Should the
6478        * cursor however be inside the selection (this happens on tripple click) then we
6479        * move the side which was last moved (current insert mark) */
6480       if (gtk_text_iter_compare (&cursor, &old_start) <= 0 ||
6481           (gtk_text_iter_compare (&cursor, &old_end) < 0 && 
6482            gtk_text_iter_compare (&old_ins, &old_bound) <= 0))
6483         {
6484           bound = old_end;
6485           orig_start = old_end;
6486           orig_end = old_end;
6487         }
6488       else
6489         {
6490           ins = bound;
6491           bound = old_start;
6492           orig_end = bound;
6493           orig_start = bound;
6494         }
6495     }
6496
6497   gtk_text_buffer_select_range (buffer, &ins, &bound);
6498
6499   gtk_text_iter_order (&orig_start, &orig_end);
6500   data->orig_start = gtk_text_buffer_create_mark (buffer, NULL,
6501                                                   &orig_start, TRUE);
6502   data->orig_end = gtk_text_buffer_create_mark (buffer, NULL,
6503                                                 &orig_end, TRUE);
6504   gtk_text_view_check_cursor_blink (text_view);
6505
6506   text_view->priv->selection_drag_handler = g_signal_connect_data (text_view,
6507                                                                    "motion-notify-event",
6508                                                                    G_CALLBACK (selection_motion_event_handler),
6509                                                                    data,
6510                                                                    (GClosureNotify) selection_data_free, 0);
6511 }
6512
6513 /* returns whether we were really dragging */
6514 static gboolean
6515 gtk_text_view_end_selection_drag (GtkTextView *text_view)
6516 {
6517   GtkTextViewPrivate *priv;
6518
6519   priv = text_view->priv;
6520
6521   if (!priv->grab_device)
6522     return FALSE;
6523
6524   if (priv->selection_drag_handler == 0)
6525     return FALSE;
6526
6527   g_signal_handler_disconnect (text_view, priv->selection_drag_handler);
6528   priv->selection_drag_handler = 0;
6529
6530   if (priv->scroll_timeout != 0)
6531     {
6532       g_source_remove (priv->scroll_timeout);
6533       priv->scroll_timeout = 0;
6534     }
6535
6536   gtk_device_grab_remove (GTK_WIDGET (text_view),
6537                           priv->grab_device);
6538   priv->grab_device = NULL;
6539
6540   return TRUE;
6541 }
6542
6543 /*
6544  * Layout utils
6545  */
6546
6547 static void
6548 gtk_text_view_set_attributes_from_style (GtkTextView        *text_view,
6549                                          GtkTextAttributes  *values,
6550                                          GtkStyle           *style)
6551 {
6552   values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
6553   values->appearance.fg_color = style->text[GTK_STATE_NORMAL];
6554
6555   if (values->font)
6556     pango_font_description_free (values->font);
6557
6558   values->font = pango_font_description_copy (style->font_desc);
6559 }
6560
6561 static void
6562 gtk_text_view_check_keymap_direction (GtkTextView *text_view)
6563 {
6564   GtkTextViewPrivate *priv = text_view->priv;
6565
6566   if (priv->layout)
6567     {
6568       GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (text_view));
6569       GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (text_view)));
6570       GtkTextDirection new_cursor_dir;
6571       GtkTextDirection new_keyboard_dir;
6572       gboolean split_cursor;
6573
6574       g_object_get (settings,
6575                     "gtk-split-cursor", &split_cursor,
6576                     NULL);
6577       
6578       if (gdk_keymap_get_direction (keymap) == PANGO_DIRECTION_RTL)
6579         new_keyboard_dir = GTK_TEXT_DIR_RTL;
6580       else
6581         new_keyboard_dir  = GTK_TEXT_DIR_LTR;
6582   
6583       if (split_cursor)
6584         new_cursor_dir = GTK_TEXT_DIR_NONE;
6585       else
6586         new_cursor_dir = new_keyboard_dir;
6587       
6588       gtk_text_layout_set_cursor_direction (priv->layout, new_cursor_dir);
6589       gtk_text_layout_set_keyboard_direction (priv->layout, new_keyboard_dir);
6590     }
6591 }
6592
6593 static void
6594 gtk_text_view_ensure_layout (GtkTextView *text_view)
6595 {
6596   GtkWidget *widget;
6597   GtkTextViewPrivate *priv;
6598
6599   widget = GTK_WIDGET (text_view);
6600   priv = text_view->priv;
6601
6602   if (priv->layout == NULL)
6603     {
6604       GtkTextAttributes *style;
6605       PangoContext *ltr_context, *rtl_context;
6606       GSList *tmp_list;
6607
6608       DV(g_print(G_STRLOC"\n"));
6609       
6610       priv->layout = gtk_text_layout_new ();
6611
6612       g_signal_connect (priv->layout,
6613                         "invalidated",
6614                         G_CALLBACK (invalidated_handler),
6615                         text_view);
6616
6617       g_signal_connect (priv->layout,
6618                         "changed",
6619                         G_CALLBACK (changed_handler),
6620                         text_view);
6621
6622       g_signal_connect (priv->layout,
6623                         "allocate-child",
6624                         G_CALLBACK (gtk_text_view_child_allocated),
6625                         text_view);
6626       
6627       if (get_buffer (text_view))
6628         gtk_text_layout_set_buffer (priv->layout, get_buffer (text_view));
6629
6630       if ((gtk_widget_has_focus (widget) && priv->cursor_visible))
6631         gtk_text_view_pend_cursor_blink (text_view);
6632       else
6633         gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
6634
6635       gtk_text_layout_set_overwrite_mode (priv->layout,
6636                                           priv->overwrite_mode && priv->editable);
6637
6638       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
6639       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
6640       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
6641       pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
6642
6643       gtk_text_layout_set_contexts (priv->layout, ltr_context, rtl_context);
6644
6645       g_object_unref (ltr_context);
6646       g_object_unref (rtl_context);
6647
6648       gtk_text_view_check_keymap_direction (text_view);
6649
6650       style = gtk_text_attributes_new ();
6651
6652       gtk_widget_ensure_style (widget);
6653       gtk_text_view_set_attributes_from_style (text_view,
6654                                                style,
6655                                                gtk_widget_get_style (widget));
6656
6657       style->pixels_above_lines = priv->pixels_above_lines;
6658       style->pixels_below_lines = priv->pixels_below_lines;
6659       style->pixels_inside_wrap = priv->pixels_inside_wrap;
6660       style->left_margin = priv->left_margin;
6661       style->right_margin = priv->right_margin;
6662       style->indent = priv->indent;
6663       style->tabs = priv->tabs ? pango_tab_array_copy (priv->tabs) : NULL;
6664
6665       style->wrap_mode = priv->wrap_mode;
6666       style->justification = priv->justify;
6667       style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
6668
6669       gtk_text_layout_set_default_style (priv->layout, style);
6670
6671       gtk_text_attributes_unref (style);
6672
6673       /* Set layout for all anchored children */
6674
6675       tmp_list = priv->children;
6676       while (tmp_list != NULL)
6677         {
6678           GtkTextViewChild *vc = tmp_list->data;
6679
6680           if (vc->anchor)
6681             {
6682               gtk_text_anchored_child_set_layout (vc->widget,
6683                                                   priv->layout);
6684               /* vc may now be invalid! */
6685             }
6686
6687           tmp_list = g_slist_next (tmp_list);
6688         }
6689
6690       gtk_text_view_invalidate (text_view);
6691     }
6692 }
6693
6694 /**
6695  * gtk_text_view_get_default_attributes:
6696  * @text_view: a #GtkTextView
6697  * 
6698  * Obtains a copy of the default text attributes. These are the
6699  * attributes used for text unless a tag overrides them.
6700  * You'd typically pass the default attributes in to
6701  * gtk_text_iter_get_attributes() in order to get the
6702  * attributes in effect at a given text position.
6703  *
6704  * The return value is a copy owned by the caller of this function,
6705  * and should be freed.
6706  * 
6707  * Return value: a new #GtkTextAttributes
6708  **/
6709 GtkTextAttributes*
6710 gtk_text_view_get_default_attributes (GtkTextView *text_view)
6711 {
6712   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
6713   
6714   gtk_text_view_ensure_layout (text_view);
6715
6716   return gtk_text_attributes_copy (text_view->priv->layout->default_style);
6717 }
6718
6719 static void
6720 gtk_text_view_destroy_layout (GtkTextView *text_view)
6721 {
6722   GtkTextViewPrivate *priv = text_view->priv;
6723
6724   if (priv->layout)
6725     {
6726       GSList *tmp_list;
6727
6728       gtk_text_view_remove_validate_idles (text_view);
6729
6730       g_signal_handlers_disconnect_by_func (priv->layout,
6731                                             invalidated_handler,
6732                                             text_view);
6733       g_signal_handlers_disconnect_by_func (priv->layout,
6734                                             changed_handler,
6735                                             text_view);
6736
6737       /* Remove layout from all anchored children */
6738       tmp_list = priv->children;
6739       while (tmp_list != NULL)
6740         {
6741           GtkTextViewChild *vc = tmp_list->data;
6742
6743           if (vc->anchor)
6744             {
6745               gtk_text_anchored_child_set_layout (vc->widget, NULL);
6746               /* vc may now be invalid! */
6747             }
6748
6749           tmp_list = g_slist_next (tmp_list);
6750         }
6751
6752       gtk_text_view_stop_cursor_blink (text_view);
6753       gtk_text_view_end_selection_drag (text_view);
6754
6755       g_object_unref (priv->layout);
6756       priv->layout = NULL;
6757     }
6758 }
6759
6760 /**
6761  * gtk_text_view_reset_im_context:
6762  * @text_view: a #GtkTextView
6763  *
6764  * Reset the input method context of the text view if needed.
6765  *
6766  * This can be necessary in the case where modifying the buffer
6767  * would confuse on-going input method behavior.
6768  *
6769  * Since: 2.22
6770  */
6771 void
6772 gtk_text_view_reset_im_context (GtkTextView *text_view)
6773 {
6774   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
6775
6776   if (text_view->priv->need_im_reset)
6777     {
6778       text_view->priv->need_im_reset = FALSE;
6779       gtk_im_context_reset (text_view->priv->im_context);
6780     }
6781 }
6782
6783 /**
6784  * gtk_text_view_im_context_filter_keypress:
6785  * @text_view: a #GtkTextView
6786  * @event: the key event
6787  *
6788  * Allow the #GtkTextView input method to internally handle key press
6789  * and release events. If this function returns %TRUE, then no further
6790  * processing should be done for this key event. See
6791  * gtk_im_context_filter_keypress().
6792  *
6793  * Note that you are expected to call this function from your handler
6794  * when overriding key event handling. This is needed in the case when
6795  * you need to insert your own key handling between the input method
6796  * and the default key event handling of the #GtkTextView.
6797  *
6798  * |[
6799  * static gboolean
6800  * gtk_foo_bar_key_press_event (GtkWidget   *widget,
6801  *                              GdkEventKey *event)
6802  * {
6803  *   if ((key->keyval == GDK_Return || key->keyval == GDK_KP_Enter))
6804  *     {
6805  *       if (gtk_text_view_im_context_filter_keypress (GTK_TEXT_VIEW (view), event))
6806  *         return TRUE;
6807  *     }
6808  *
6809  *     /&ast; Do some stuff &ast;/
6810  *
6811  *   return GTK_WIDGET_CLASS (gtk_foo_bar_parent_class)->key_press_event (widget, event);
6812  * }
6813  * ]|
6814  *
6815  * Return value: %TRUE if the input method handled the key event.
6816  *
6817  * Since: 2.22
6818  */
6819 gboolean
6820 gtk_text_view_im_context_filter_keypress (GtkTextView  *text_view,
6821                                           GdkEventKey  *event)
6822 {
6823   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
6824
6825   return gtk_im_context_filter_keypress (text_view->priv->im_context, event);
6826 }
6827
6828 /*
6829  * DND feature
6830  */
6831
6832 static void
6833 drag_begin_cb (GtkWidget      *widget,
6834                GdkDragContext *context,
6835                gpointer        data)
6836 {
6837   GtkTextView     *text_view = GTK_TEXT_VIEW (widget);
6838   GtkTextBuffer   *buffer = gtk_text_view_get_buffer (text_view);
6839   GtkTextIter      start;
6840   GtkTextIter      end;
6841   cairo_surface_t *surface = NULL;
6842
6843   g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
6844
6845   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
6846     surface = _gtk_text_util_create_rich_drag_icon (widget, buffer, &start, &end);
6847
6848   if (surface)
6849     {
6850       gtk_drag_set_icon_surface (context, surface);
6851       cairo_surface_destroy (surface);
6852     }
6853   else
6854     {
6855       gtk_drag_set_icon_default (context);
6856     }
6857 }
6858
6859 static void
6860 gtk_text_view_start_selection_dnd (GtkTextView       *text_view,
6861                                    const GtkTextIter *iter,
6862                                    GdkEventMotion    *event)
6863 {
6864   GtkTargetList *target_list;
6865
6866   text_view->priv->drag_start_x = -1;
6867   text_view->priv->drag_start_y = -1;
6868   text_view->priv->pending_place_cursor_button = 0;
6869
6870   target_list = gtk_text_buffer_get_copy_target_list (get_buffer (text_view));
6871
6872   g_signal_connect (text_view, "drag-begin",
6873                     G_CALLBACK (drag_begin_cb), NULL);
6874   gtk_drag_begin (GTK_WIDGET (text_view), target_list,
6875                   GDK_ACTION_COPY | GDK_ACTION_MOVE,
6876                   1, (GdkEvent*)event);
6877 }
6878
6879 static void
6880 gtk_text_view_drag_begin (GtkWidget        *widget,
6881                           GdkDragContext   *context)
6882 {
6883   /* do nothing */
6884 }
6885
6886 static void
6887 gtk_text_view_drag_end (GtkWidget        *widget,
6888                         GdkDragContext   *context)
6889 {
6890 }
6891
6892 static void
6893 gtk_text_view_drag_data_get (GtkWidget        *widget,
6894                              GdkDragContext   *context,
6895                              GtkSelectionData *selection_data,
6896                              guint             info,
6897                              guint             time)
6898 {
6899   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
6900   GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
6901
6902   if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
6903     {
6904       gtk_selection_data_set (selection_data,
6905                               gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
6906                               8, /* bytes */
6907                               (void*)&buffer,
6908                               sizeof (buffer));
6909     }
6910   else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
6911     {
6912       GtkTextIter start;
6913       GtkTextIter end;
6914       guint8 *str = NULL;
6915       gsize len;
6916
6917       if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
6918         {
6919           /* Extract the selected text */
6920           str = gtk_text_buffer_serialize (buffer, buffer,
6921                                            selection_data->target,
6922                                            &start, &end,
6923                                            &len);
6924         }
6925
6926       if (str)
6927         {
6928           gtk_selection_data_set (selection_data,
6929                                   selection_data->target,
6930                                   8, /* bytes */
6931                                   (guchar *) str, len);
6932           g_free (str);
6933         }
6934     }
6935   else
6936     {
6937       GtkTextIter start;
6938       GtkTextIter end;
6939       gchar *str = NULL;
6940
6941       if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
6942         {
6943           /* Extract the selected text */
6944           str = gtk_text_iter_get_visible_text (&start, &end);
6945         }
6946
6947       if (str)
6948         {
6949           gtk_selection_data_set_text (selection_data, str, -1);
6950           g_free (str);
6951         }
6952     }
6953 }
6954
6955 static void
6956 gtk_text_view_drag_data_delete (GtkWidget        *widget,
6957                                 GdkDragContext   *context)
6958 {
6959   gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->priv->buffer,
6960                                     TRUE, GTK_TEXT_VIEW (widget)->priv->editable);
6961 }
6962
6963 static void
6964 gtk_text_view_drag_leave (GtkWidget        *widget,
6965                           GdkDragContext   *context,
6966                           guint             time)
6967 {
6968   GtkTextView *text_view;
6969   GtkTextViewPrivate *priv;
6970
6971   text_view = GTK_TEXT_VIEW (widget);
6972   priv = text_view->priv;
6973
6974   gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
6975
6976   if (priv->dnd_device)
6977     priv->dnd_device = NULL;
6978
6979   if (priv->scroll_timeout != 0)
6980     g_source_remove (priv->scroll_timeout);
6981
6982   priv->scroll_timeout = 0;
6983 }
6984
6985 static gboolean
6986 gtk_text_view_drag_motion (GtkWidget        *widget,
6987                            GdkDragContext   *context,
6988                            gint              x,
6989                            gint              y,
6990                            guint             time)
6991 {
6992   GtkTextIter newplace;
6993   GtkTextView *text_view;
6994   GtkTextViewPrivate *priv;
6995   GtkTextIter start;
6996   GtkTextIter end;
6997   GdkRectangle target_rect;
6998   gint bx, by;
6999   GdkAtom target;
7000   GdkDragAction suggested_action = 0;
7001   
7002   text_view = GTK_TEXT_VIEW (widget);
7003   priv = text_view->priv;
7004
7005   target_rect = priv->text_window->allocation;
7006   
7007   if (x < target_rect.x ||
7008       y < target_rect.y ||
7009       x > (target_rect.x + target_rect.width) ||
7010       y > (target_rect.y + target_rect.height))
7011     return FALSE; /* outside the text window, allow parent widgets to handle event */
7012
7013   gtk_text_view_window_to_buffer_coords (text_view,
7014                                          GTK_TEXT_WINDOW_WIDGET,
7015                                          x, y,
7016                                          &bx, &by);
7017
7018   gtk_text_layout_get_iter_at_pixel (priv->layout,
7019                                      &newplace,
7020                                      bx, by);  
7021
7022   target = gtk_drag_dest_find_target (widget, context,
7023                                       gtk_drag_dest_get_target_list (widget));
7024
7025   if (target == GDK_NONE)
7026     {
7027       /* can't accept any of the offered targets */
7028     }                                 
7029   else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
7030                                                  &start, &end) &&
7031            gtk_text_iter_compare (&newplace, &start) >= 0 &&
7032            gtk_text_iter_compare (&newplace, &end) <= 0)
7033     {
7034       /* We're inside the selection. */
7035     }
7036   else
7037     {      
7038       if (gtk_text_iter_can_insert (&newplace, priv->editable))
7039         {
7040           GtkWidget *source_widget;
7041           
7042           suggested_action = context->suggested_action;
7043           
7044           source_widget = gtk_drag_get_source_widget (context);
7045           
7046           if (source_widget == widget)
7047             {
7048               /* Default to MOVE, unless the user has
7049                * pressed ctrl or alt to affect available actions
7050                */
7051               if ((context->actions & GDK_ACTION_MOVE) != 0)
7052                 suggested_action = GDK_ACTION_MOVE;
7053             }
7054         }
7055       else
7056         {
7057           /* Can't drop here. */
7058         }
7059     }
7060
7061   if (suggested_action != 0)
7062     {
7063       gtk_text_mark_set_visible (priv->dnd_mark,
7064                                  priv->cursor_visible);
7065       
7066       gdk_drag_status (context, suggested_action, time);
7067     }
7068   else
7069     {
7070       gdk_drag_status (context, 0, time);
7071       gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
7072     }
7073
7074   priv->dnd_device = gdk_drag_context_get_device (context);
7075
7076   if (!priv->scroll_timeout)
7077     priv->scroll_timeout =
7078       gdk_threads_add_timeout (100, drag_scan_timeout, text_view);
7079
7080   /* TRUE return means don't propagate the drag motion to parent
7081    * widgets that may also be drop sites.
7082    */
7083   return TRUE;
7084 }
7085
7086 static gboolean
7087 gtk_text_view_drag_drop (GtkWidget        *widget,
7088                          GdkDragContext   *context,
7089                          gint              x,
7090                          gint              y,
7091                          guint             time)
7092 {
7093   GtkTextView *text_view;
7094   GtkTextViewPrivate *priv;
7095   GtkTextIter drop_point;
7096   GdkAtom target = GDK_NONE;
7097
7098   text_view = GTK_TEXT_VIEW (widget);
7099   priv = text_view->priv;
7100
7101   if (priv->scroll_timeout != 0)
7102     g_source_remove (priv->scroll_timeout);
7103
7104   priv->scroll_timeout = 0;
7105
7106   gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
7107
7108   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
7109                                     &drop_point,
7110                                     priv->dnd_mark);
7111
7112   if (gtk_text_iter_can_insert (&drop_point, priv->editable))
7113     target = gtk_drag_dest_find_target (widget, context, NULL);
7114
7115   if (target != GDK_NONE)
7116     gtk_drag_get_data (widget, context, target, time);
7117   else
7118     gtk_drag_finish (context, FALSE, FALSE, time);
7119
7120   return TRUE;
7121 }
7122
7123 static void
7124 insert_text_data (GtkTextView      *text_view,
7125                   GtkTextIter      *drop_point,
7126                   GtkSelectionData *selection_data)
7127 {
7128   guchar *str;
7129
7130   str = gtk_selection_data_get_text (selection_data);
7131
7132   if (str)
7133     {
7134       if (!gtk_text_buffer_insert_interactive (get_buffer (text_view),
7135                                                drop_point, (gchar *) str, -1,
7136                                                text_view->priv->editable))
7137         {
7138           gtk_widget_error_bell (GTK_WIDGET (text_view));
7139         }
7140
7141       g_free (str);
7142     }
7143 }
7144
7145 static void
7146 gtk_text_view_drag_data_received (GtkWidget        *widget,
7147                                   GdkDragContext   *context,
7148                                   gint              x,
7149                                   gint              y,
7150                                   GtkSelectionData *selection_data,
7151                                   guint             info,
7152                                   guint             time)
7153 {
7154   GtkTextIter drop_point;
7155   GtkTextView *text_view;
7156   GtkTextViewPrivate *priv;
7157   gboolean success = FALSE;
7158   GtkTextBuffer *buffer = NULL;
7159
7160   text_view = GTK_TEXT_VIEW (widget);
7161   priv = text_view->priv;
7162
7163   if (!priv->dnd_mark)
7164     goto done;
7165
7166   buffer = get_buffer (text_view);
7167
7168   gtk_text_buffer_get_iter_at_mark (buffer,
7169                                     &drop_point,
7170                                     priv->dnd_mark);
7171   
7172   if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
7173     goto done;
7174
7175   success = TRUE;
7176
7177   gtk_text_buffer_begin_user_action (buffer);
7178
7179   if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
7180     {
7181       GtkTextBuffer *src_buffer = NULL;
7182       GtkTextIter start, end;
7183       gboolean copy_tags = TRUE;
7184
7185       if (selection_data->length != sizeof (src_buffer))
7186         return;
7187
7188       memcpy (&src_buffer, selection_data->data, sizeof (src_buffer));
7189
7190       if (src_buffer == NULL)
7191         return;
7192
7193       g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
7194
7195       if (gtk_text_buffer_get_tag_table (src_buffer) !=
7196           gtk_text_buffer_get_tag_table (buffer))
7197         {
7198           /*  try to find a suitable rich text target instead  */
7199           GdkAtom *atoms;
7200           gint     n_atoms;
7201           GList   *list;
7202           GdkAtom  target = GDK_NONE;
7203
7204           copy_tags = FALSE;
7205
7206           atoms = gtk_text_buffer_get_deserialize_formats (buffer, &n_atoms);
7207
7208           for (list = context->targets; list; list = g_list_next (list))
7209             {
7210               gint i;
7211
7212               for (i = 0; i < n_atoms; i++)
7213                 if (GUINT_TO_POINTER (atoms[i]) == list->data)
7214                   {
7215                     target = atoms[i];
7216                     break;
7217                   }
7218             }
7219
7220           g_free (atoms);
7221
7222           if (target != GDK_NONE)
7223             {
7224               gtk_drag_get_data (widget, context, target, time);
7225               gtk_text_buffer_end_user_action (buffer);
7226               return;
7227             }
7228         }
7229
7230       if (gtk_text_buffer_get_selection_bounds (src_buffer,
7231                                                 &start,
7232                                                 &end))
7233         {
7234           if (copy_tags)
7235             gtk_text_buffer_insert_range_interactive (buffer,
7236                                                       &drop_point,
7237                                                       &start,
7238                                                       &end,
7239                                                       priv->editable);
7240           else
7241             {
7242               gchar *str;
7243
7244               str = gtk_text_iter_get_visible_text (&start, &end);
7245               gtk_text_buffer_insert_interactive (buffer,
7246                                                   &drop_point, str, -1,
7247                                                   priv->editable);
7248               g_free (str);
7249             }
7250         }
7251     }
7252   else if (selection_data->length > 0 &&
7253            info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
7254     {
7255       gboolean retval;
7256       GError *error = NULL;
7257
7258       retval = gtk_text_buffer_deserialize (buffer, buffer,
7259                                             selection_data->target,
7260                                             &drop_point,
7261                                             (guint8 *) selection_data->data,
7262                                             selection_data->length,
7263                                             &error);
7264
7265       if (!retval)
7266         {
7267           g_warning ("error pasting: %s\n", error->message);
7268           g_clear_error (&error);
7269         }
7270     }
7271   else
7272     insert_text_data (text_view, &drop_point, selection_data);
7273
7274  done:
7275   gtk_drag_finish (context, success,
7276                    success && context->action == GDK_ACTION_MOVE,
7277                    time);
7278
7279   if (success)
7280     {
7281       gtk_text_buffer_get_iter_at_mark (buffer,
7282                                         &drop_point,
7283                                         priv->dnd_mark);
7284       gtk_text_buffer_place_cursor (buffer, &drop_point);
7285
7286       gtk_text_buffer_end_user_action (buffer);
7287     }
7288 }
7289
7290 /**
7291  * gtk_text_view_get_hadjustment:
7292  * @text_view: a #GtkTextView
7293  *
7294  * Gets the horizontal-scrolling #GtkAdjustment.
7295  *
7296  * Returns: (transfer none): pointer to the horizontal #GtkAdjustment
7297  *
7298  * Since: 2.22
7299  **/
7300 GtkAdjustment*
7301 gtk_text_view_get_hadjustment (GtkTextView *text_view)
7302 {
7303   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
7304
7305   return get_hadjustment (text_view);
7306 }
7307
7308 /**
7309  * gtk_text_view_get_vadjustment:
7310  * @text_view: a #GtkTextView
7311  *
7312  * Gets the vertical-scrolling #GtkAdjustment.
7313  *
7314  * Returns: (transfer none): pointer to the vertical #GtkAdjustment
7315  *
7316  * Since: 2.22
7317  **/
7318 GtkAdjustment*
7319 gtk_text_view_get_vadjustment (GtkTextView *text_view)
7320 {
7321   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
7322
7323   return get_vadjustment (text_view);
7324 }
7325
7326 static GtkAdjustment*
7327 get_hadjustment (GtkTextView *text_view)
7328 {
7329   if (text_view->priv->hadjustment == NULL)
7330     gtk_text_view_set_scroll_adjustments (text_view,
7331                                           NULL, /* forces creation */
7332                                           text_view->priv->vadjustment);
7333
7334   return text_view->priv->hadjustment;
7335 }
7336
7337 static GtkAdjustment*
7338 get_vadjustment (GtkTextView *text_view)
7339 {
7340   if (text_view->priv->vadjustment == NULL)
7341     gtk_text_view_set_scroll_adjustments (text_view,
7342                                           text_view->priv->hadjustment,
7343                                           NULL); /* forces creation */
7344   return text_view->priv->vadjustment;
7345 }
7346
7347
7348 static void
7349 gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
7350                                       GtkAdjustment *hadj,
7351                                       GtkAdjustment *vadj)
7352 {
7353   GtkTextViewPrivate *priv = text_view->priv;
7354   gboolean need_adjust = FALSE;
7355
7356   if (hadj)
7357     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7358   else
7359     hadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
7360   if (vadj)
7361     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7362   else
7363     vadj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
7364
7365   if (priv->hadjustment && (priv->hadjustment != hadj))
7366     {
7367       g_signal_handlers_disconnect_by_func (priv->hadjustment,
7368                                             gtk_text_view_value_changed,
7369                                             text_view);
7370       g_object_unref (priv->hadjustment);
7371     }
7372
7373   if (priv->vadjustment && (priv->vadjustment != vadj))
7374     {
7375       g_signal_handlers_disconnect_by_func (priv->vadjustment,
7376                                             gtk_text_view_value_changed,
7377                                             text_view);
7378       g_object_unref (priv->vadjustment);
7379     }
7380
7381   if (priv->hadjustment != hadj)
7382     {
7383       priv->hadjustment = hadj;
7384       g_object_ref_sink (priv->hadjustment);
7385       
7386       g_signal_connect (priv->hadjustment, "value-changed",
7387                         G_CALLBACK (gtk_text_view_value_changed),
7388                         text_view);
7389       need_adjust = TRUE;
7390     }
7391
7392   if (priv->vadjustment != vadj)
7393     {
7394       priv->vadjustment = vadj;
7395       g_object_ref_sink (priv->vadjustment);
7396       
7397       g_signal_connect (priv->vadjustment, "value-changed",
7398                         G_CALLBACK (gtk_text_view_value_changed),
7399                         text_view);
7400       need_adjust = TRUE;
7401     }
7402
7403   if (need_adjust)
7404     gtk_text_view_value_changed (NULL, text_view);
7405 }
7406
7407 /* FIXME this adjust_allocation is a big cut-and-paste from
7408  * GtkCList, needs to be some "official" way to do this
7409  * factored out.
7410  */
7411 typedef struct
7412 {
7413   GdkWindow *window;
7414   int dx;
7415   int dy;
7416 } ScrollData;
7417
7418 /* The window to which widget->window is relative */
7419 #define ALLOCATION_WINDOW(widget)               \
7420    (!gtk_widget_get_has_window (widget) ?                   \
7421     gtk_widget_get_window (widget) :                        \
7422     gdk_window_get_parent (gtk_widget_get_window (widget)))
7423
7424 static void
7425 adjust_allocation_recurse (GtkWidget *widget,
7426                            gpointer   data)
7427 {
7428   GtkAllocation allocation;
7429   ScrollData *scroll_data = data;
7430
7431   /* Need to really size allocate instead of just poking
7432    * into widget->allocation if the widget is not realized.
7433    * FIXME someone figure out why this was.
7434    */
7435   if (!gtk_widget_get_realized (widget))
7436     {
7437       if (gtk_widget_get_visible (widget))
7438         {
7439           GdkRectangle tmp_rectangle;
7440
7441           tmp_rectangle = allocation;
7442           tmp_rectangle.x += scroll_data->dx;
7443           tmp_rectangle.y += scroll_data->dy;
7444           
7445           gtk_widget_size_allocate (widget, &tmp_rectangle);
7446         }
7447     }
7448   else
7449     {
7450       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
7451         {
7452           allocation.x += scroll_data->dx;
7453           allocation.y += scroll_data->dy;
7454           gtk_widget_set_allocation (widget, &allocation);
7455
7456           if (GTK_IS_CONTAINER (widget))
7457             gtk_container_forall (GTK_CONTAINER (widget),
7458                                   adjust_allocation_recurse,
7459                                   data);
7460         }
7461     }
7462 }
7463
7464 static void
7465 adjust_allocation (GtkWidget *widget,
7466                    int        dx,
7467                    int        dy)
7468 {
7469   ScrollData scroll_data;
7470
7471   if (gtk_widget_get_realized (widget))
7472     scroll_data.window = ALLOCATION_WINDOW (widget);
7473   else
7474     scroll_data.window = NULL;
7475     
7476   scroll_data.dx = dx;
7477   scroll_data.dy = dy;
7478   
7479   adjust_allocation_recurse (widget, &scroll_data);
7480 }
7481             
7482 static void
7483 gtk_text_view_value_changed (GtkAdjustment *adj,
7484                              GtkTextView   *text_view)
7485 {
7486   GtkTextViewPrivate *priv;
7487   GtkTextIter iter;
7488   gint line_top;
7489   gint dx = 0;
7490   gint dy = 0;
7491
7492   priv = text_view->priv;
7493
7494   /* Note that we oddly call this function with adj == NULL
7495    * sometimes
7496    */
7497   
7498   priv->onscreen_validated = FALSE;
7499
7500   DV(g_print(">Scroll offset changed %s/%g, onscreen_validated = FALSE ("G_STRLOC")\n",
7501              adj == priv->hadjustment ? "hadj" : adj == priv->vadjustment ? "vadj" : "none",
7502              adj ? adj->value : 0.0));
7503   
7504   if (adj == priv->hadjustment)
7505     {
7506       dx = priv->xoffset - (gint)adj->value;
7507       priv->xoffset = adj->value;
7508
7509       /* If the change is due to a size change we need 
7510        * to invalidate the entire text window because there might be
7511        * right-aligned or centered text 
7512        */
7513       if (priv->width_changed)
7514         {
7515           if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
7516             gdk_window_invalidate_rect (priv->text_window->bin_window, NULL, FALSE);
7517           
7518           priv->width_changed = FALSE;
7519         }
7520     }
7521   else if (adj == priv->vadjustment)
7522     {
7523       dy = priv->yoffset - (gint)adj->value;
7524       priv->yoffset = adj->value;
7525
7526       if (priv->layout)
7527         {
7528           gtk_text_layout_get_line_at_y (priv->layout, &iter, adj->value, &line_top);
7529
7530           gtk_text_buffer_move_mark (get_buffer (text_view), priv->first_para_mark, &iter);
7531
7532           priv->first_para_pixels = adj->value - line_top;
7533         }
7534     }
7535   
7536   if (dx != 0 || dy != 0)
7537     {
7538       GSList *tmp_list;
7539
7540       if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
7541         {
7542           if (dy != 0)
7543             {
7544               if (priv->left_window)
7545                 text_window_scroll (priv->left_window, 0, dy);
7546               if (priv->right_window)
7547                 text_window_scroll (priv->right_window, 0, dy);
7548             }
7549       
7550           if (dx != 0)
7551             {
7552               if (priv->top_window)
7553                 text_window_scroll (priv->top_window, dx, 0);
7554               if (priv->bottom_window)
7555                 text_window_scroll (priv->bottom_window, dx, 0);
7556             }
7557       
7558           /* It looks nicer to scroll the main area last, because
7559            * it takes a while, and making the side areas update
7560            * afterward emphasizes the slowness of scrolling the
7561            * main area.
7562            */
7563           text_window_scroll (priv->text_window, dx, dy);
7564         }
7565       
7566       /* Children are now "moved" in the text window, poke
7567        * into widget->allocation for each child
7568        */
7569       tmp_list = priv->children;
7570       while (tmp_list != NULL)
7571         {
7572           GtkTextViewChild *child = tmp_list->data;
7573           
7574           if (child->anchor)
7575             adjust_allocation (child->widget, dx, dy);
7576           
7577           tmp_list = g_slist_next (tmp_list);
7578         }
7579     }
7580
7581   /* This could result in invalidation, which would install the
7582    * first_validate_idle, which would validate onscreen;
7583    * but we're going to go ahead and validate here, so
7584    * first_validate_idle shouldn't have anything to do.
7585    */
7586   gtk_text_view_update_layout_width (text_view);
7587   
7588   /* We also update the IM spot location here, since the IM context
7589    * might do something that leads to validation.
7590    */
7591   gtk_text_view_update_im_spot_location (text_view);
7592
7593   /* note that validation of onscreen could invoke this function
7594    * recursively, by scrolling to maintain first_para, or in response
7595    * to updating the layout width, however there is no problem with
7596    * that, or shouldn't be.
7597    */
7598   gtk_text_view_validate_onscreen (text_view);
7599   
7600   /* process exposes */
7601   if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
7602     {
7603       DV (g_print ("Processing updates (%s)\n", G_STRLOC));
7604       
7605       if (priv->left_window)
7606         gdk_window_process_updates (priv->left_window->bin_window, TRUE);
7607
7608       if (priv->right_window)
7609         gdk_window_process_updates (priv->right_window->bin_window, TRUE);
7610
7611       if (priv->top_window)
7612         gdk_window_process_updates (priv->top_window->bin_window, TRUE);
7613       
7614       if (priv->bottom_window)
7615         gdk_window_process_updates (priv->bottom_window->bin_window, TRUE);
7616   
7617       gdk_window_process_updates (priv->text_window->bin_window, TRUE);
7618     }
7619
7620   /* If this got installed, get rid of it, it's just a waste of time. */
7621   if (priv->first_validate_idle != 0)
7622     {
7623       g_source_remove (priv->first_validate_idle);
7624       priv->first_validate_idle = 0;
7625     }
7626
7627   /* Finally we update the IM cursor location again, to ensure any
7628    * changes made by the validation are pushed through.
7629    */
7630   gtk_text_view_update_im_spot_location (text_view);
7631   
7632   DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n"));
7633 }
7634
7635 static void
7636 gtk_text_view_commit_handler (GtkIMContext  *context,
7637                               const gchar   *str,
7638                               GtkTextView   *text_view)
7639 {
7640   gtk_text_view_commit_text (text_view, str);
7641 }
7642
7643 static void
7644 gtk_text_view_commit_text (GtkTextView   *text_view,
7645                            const gchar   *str)
7646 {
7647   GtkTextViewPrivate *priv;
7648   gboolean had_selection;
7649
7650   priv = text_view->priv;
7651
7652   gtk_text_buffer_begin_user_action (get_buffer (text_view));
7653
7654   had_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
7655                                                         NULL, NULL);
7656   
7657   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
7658                                     priv->editable);
7659
7660   if (!strcmp (str, "\n"))
7661     {
7662       if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), "\n", 1,
7663                                                          priv->editable))
7664         {
7665           gtk_widget_error_bell (GTK_WIDGET (text_view));
7666         }
7667     }
7668   else
7669     {
7670       if (!had_selection && priv->overwrite_mode)
7671         {
7672           GtkTextIter insert;
7673
7674           gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
7675                                             &insert,
7676                                             gtk_text_buffer_get_insert (get_buffer (text_view)));
7677           if (!gtk_text_iter_ends_line (&insert))
7678             gtk_text_view_delete_from_cursor (text_view, GTK_DELETE_CHARS, 1);
7679         }
7680
7681       if (!gtk_text_buffer_insert_interactive_at_cursor (get_buffer (text_view), str, -1,
7682                                                          priv->editable))
7683         {
7684           gtk_widget_error_bell (GTK_WIDGET (text_view));
7685         }
7686     }
7687
7688   gtk_text_buffer_end_user_action (get_buffer (text_view));
7689
7690   gtk_text_view_set_virtual_cursor_pos (text_view, -1, -1);
7691   DV(g_print (G_STRLOC": scrolling onscreen\n"));
7692   gtk_text_view_scroll_mark_onscreen (text_view,
7693                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
7694 }
7695
7696 static void
7697 gtk_text_view_preedit_changed_handler (GtkIMContext *context,
7698                                        GtkTextView  *text_view)
7699 {
7700   GtkTextViewPrivate *priv;
7701   gchar *str;
7702   PangoAttrList *attrs;
7703   gint cursor_pos;
7704   GtkTextIter iter;
7705
7706   priv = text_view->priv;
7707
7708   gtk_text_buffer_get_iter_at_mark (priv->buffer, &iter,
7709                                     gtk_text_buffer_get_insert (priv->buffer));
7710
7711   /* Keypress events are passed to input method even if cursor position is
7712    * not editable; so beep here if it's multi-key input sequence, input
7713    * method will be reset in key-press-event handler.
7714    */
7715   gtk_im_context_get_preedit_string (context, &str, &attrs, &cursor_pos);
7716
7717   if (str && str[0] && !gtk_text_iter_can_insert (&iter, priv->editable))
7718     {
7719       gtk_widget_error_bell (GTK_WIDGET (text_view));
7720       goto out;
7721     }
7722
7723   g_signal_emit (text_view, signals[PREEDIT_CHANGED], 0, str);
7724
7725   if (priv->layout)
7726     gtk_text_layout_set_preedit_string (priv->layout, str, attrs, cursor_pos);
7727   if (gtk_widget_has_focus (GTK_WIDGET (text_view)))
7728     gtk_text_view_scroll_mark_onscreen (text_view,
7729                                         gtk_text_buffer_get_insert (get_buffer (text_view)));
7730
7731 out:
7732   pango_attr_list_unref (attrs);
7733   g_free (str);
7734 }
7735
7736 static gboolean
7737 gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
7738                                             GtkTextView   *text_view)
7739 {
7740   GtkTextIter start;
7741   GtkTextIter end;
7742   gint pos;
7743   gchar *text;
7744
7745   gtk_text_buffer_get_iter_at_mark (text_view->priv->buffer, &start,
7746                                     gtk_text_buffer_get_insert (text_view->priv->buffer));
7747   end = start;
7748
7749   pos = gtk_text_iter_get_line_index (&start);
7750   gtk_text_iter_set_line_offset (&start, 0);
7751   gtk_text_iter_forward_to_line_end (&end);
7752
7753   text = gtk_text_iter_get_slice (&start, &end);
7754   gtk_im_context_set_surrounding (context, text, -1, pos);
7755   g_free (text);
7756
7757   return TRUE;
7758 }
7759
7760 static gboolean
7761 gtk_text_view_delete_surrounding_handler (GtkIMContext  *context,
7762                                           gint           offset,
7763                                           gint           n_chars,
7764                                           GtkTextView   *text_view)
7765 {
7766   GtkTextViewPrivate *priv;
7767   GtkTextIter start;
7768   GtkTextIter end;
7769
7770   priv = text_view->priv;
7771
7772   gtk_text_buffer_get_iter_at_mark (priv->buffer, &start,
7773                                     gtk_text_buffer_get_insert (priv->buffer));
7774   end = start;
7775
7776   gtk_text_iter_forward_chars (&start, offset);
7777   gtk_text_iter_forward_chars (&end, offset + n_chars);
7778
7779   gtk_text_buffer_delete_interactive (priv->buffer, &start, &end,
7780                                       priv->editable);
7781
7782   return TRUE;
7783 }
7784
7785 static void
7786 gtk_text_view_mark_set_handler (GtkTextBuffer     *buffer,
7787                                 const GtkTextIter *location,
7788                                 GtkTextMark       *mark,
7789                                 gpointer           data)
7790 {
7791   GtkTextView *text_view = GTK_TEXT_VIEW (data);
7792   gboolean need_reset = FALSE;
7793
7794   if (mark == gtk_text_buffer_get_insert (buffer))
7795     {
7796       text_view->priv->virtual_cursor_x = -1;
7797       text_view->priv->virtual_cursor_y = -1;
7798       gtk_text_view_update_im_spot_location (text_view);
7799       need_reset = TRUE;
7800     }
7801   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
7802     {
7803       need_reset = TRUE;
7804     }
7805
7806   if (need_reset)
7807     gtk_text_view_reset_im_context (text_view);
7808 }
7809
7810 static void
7811 gtk_text_view_target_list_notify (GtkTextBuffer    *buffer,
7812                                   const GParamSpec *pspec,
7813                                   gpointer          data)
7814 {
7815   GtkWidget     *widget = GTK_WIDGET (data);
7816   GtkTargetList *view_list;
7817   GtkTargetList *buffer_list;
7818   GList         *list;
7819
7820   view_list = gtk_drag_dest_get_target_list (widget);
7821   buffer_list = gtk_text_buffer_get_paste_target_list (buffer);
7822
7823   if (view_list)
7824     gtk_target_list_ref (view_list);
7825   else
7826     view_list = gtk_target_list_new (NULL, 0);
7827
7828   list = view_list->list;
7829   while (list)
7830     {
7831       GtkTargetPair *pair = list->data;
7832
7833       list = g_list_next (list); /* get next element before removing */
7834
7835       if (pair->info >= GTK_TEXT_BUFFER_TARGET_INFO_TEXT &&
7836           pair->info <= GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
7837         {
7838           gtk_target_list_remove (view_list, pair->target);
7839         }
7840     }
7841
7842   for (list = buffer_list->list; list; list = g_list_next (list))
7843     {
7844       GtkTargetPair *pair = list->data;
7845
7846       gtk_target_list_add (view_list, pair->target, pair->flags, pair->info);
7847     }
7848
7849   gtk_drag_dest_set_target_list (widget, view_list);
7850   gtk_target_list_unref (view_list);
7851 }
7852
7853 static void
7854 gtk_text_view_get_cursor_location  (GtkTextView   *text_view,
7855                                     GdkRectangle  *pos)
7856 {
7857   GtkTextIter insert;
7858   
7859   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
7860                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
7861
7862   gtk_text_layout_get_cursor_locations (text_view->priv->layout, &insert, pos, NULL);
7863 }
7864
7865 static void
7866 gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
7867                                       GtkTextIter *cursor,
7868                                       gint        *x,
7869                                       gint        *y)
7870 {
7871   GtkTextViewPrivate *priv;
7872   GtkTextIter insert;
7873   GdkRectangle pos;
7874
7875   priv = text_view->priv;
7876
7877   if (cursor)
7878     insert = *cursor;
7879   else
7880     gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &insert,
7881                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
7882
7883   if ((x && priv->virtual_cursor_x == -1) ||
7884       (y && priv->virtual_cursor_y == -1))
7885     gtk_text_layout_get_cursor_locations (priv->layout, &insert, &pos, NULL);
7886
7887   if (x)
7888     {
7889       if (priv->virtual_cursor_x != -1)
7890         *x = priv->virtual_cursor_x;
7891       else
7892         *x = pos.x;
7893     }
7894
7895   if (y)
7896     {
7897       if (priv->virtual_cursor_x != -1)
7898         *y = priv->virtual_cursor_y;
7899       else
7900         *y = pos.y + pos.height / 2;
7901     }
7902 }
7903
7904 static void
7905 gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
7906                                       gint         x,
7907                                       gint         y)
7908 {
7909   GdkRectangle pos;
7910
7911   if (!text_view->priv->layout)
7912     return;
7913
7914   if (x == -1 || y == -1)
7915     gtk_text_view_get_cursor_location (text_view, &pos);
7916
7917   text_view->priv->virtual_cursor_x = (x == -1) ? pos.x : x;
7918   text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y;
7919 }
7920
7921 /* Quick hack of a popup menu
7922  */
7923 static void
7924 activate_cb (GtkWidget   *menuitem,
7925              GtkTextView *text_view)
7926 {
7927   const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
7928   g_signal_emit_by_name (text_view, signal);
7929 }
7930
7931 static void
7932 append_action_signal (GtkTextView  *text_view,
7933                       GtkWidget    *menu,
7934                       const gchar  *stock_id,
7935                       const gchar  *signal,
7936                       gboolean      sensitive)
7937 {
7938   GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
7939
7940   g_object_set_data (G_OBJECT (menuitem), I_("gtk-signal"), (char *)signal);
7941   g_signal_connect (menuitem, "activate",
7942                     G_CALLBACK (activate_cb), text_view);
7943
7944   gtk_widget_set_sensitive (menuitem, sensitive);
7945   
7946   gtk_widget_show (menuitem);
7947   gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
7948 }
7949
7950 static void
7951 gtk_text_view_select_all (GtkWidget *widget,
7952                           gboolean select)
7953 {
7954   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
7955   GtkTextBuffer *buffer;
7956   GtkTextIter start_iter, end_iter, insert;
7957
7958   buffer = text_view->priv->buffer;
7959   if (select) 
7960     {
7961       gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
7962       gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
7963     }
7964   else 
7965     {
7966       gtk_text_buffer_get_iter_at_mark (buffer, &insert,
7967                                         gtk_text_buffer_get_insert (buffer));
7968       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &insert);
7969     }
7970 }
7971
7972 static void
7973 select_all_cb (GtkWidget   *menuitem,
7974                GtkTextView *text_view)
7975 {
7976   gtk_text_view_select_all (GTK_WIDGET (text_view), TRUE);
7977 }
7978
7979 static void
7980 delete_cb (GtkTextView *text_view)
7981 {
7982   gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
7983                                     text_view->priv->editable);
7984 }
7985
7986 static void
7987 popup_menu_detach (GtkWidget *attach_widget,
7988                    GtkMenu   *menu)
7989 {
7990   GTK_TEXT_VIEW (attach_widget)->priv->popup_menu = NULL;
7991 }
7992
7993 static void
7994 popup_position_func (GtkMenu   *menu,
7995                      gint      *x,
7996                      gint      *y,
7997                      gboolean  *push_in,
7998                      gpointer   user_data)
7999 {
8000   GtkAllocation allocation;
8001   GtkTextView *text_view;
8002   GtkWidget *widget;
8003   GdkRectangle cursor_rect;
8004   GdkRectangle onscreen_rect;
8005   gint root_x, root_y;
8006   GtkTextIter iter;
8007   GtkRequisition req;      
8008   GdkScreen *screen;
8009   gint monitor_num;
8010   GdkRectangle monitor;
8011       
8012   text_view = GTK_TEXT_VIEW (user_data);
8013   widget = GTK_WIDGET (text_view);
8014   
8015   g_return_if_fail (gtk_widget_get_realized (widget));
8016   
8017   screen = gtk_widget_get_screen (widget);
8018
8019   gdk_window_get_origin (gtk_widget_get_window (widget),
8020                          &root_x, &root_y);
8021
8022   gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
8023                                     &iter,
8024                                     gtk_text_buffer_get_insert (get_buffer (text_view)));
8025
8026   gtk_text_view_get_iter_location (text_view,
8027                                    &iter,
8028                                    &cursor_rect);
8029
8030   gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
8031
8032   gtk_widget_get_preferred_size (text_view->priv->popup_menu,
8033                                  &req, NULL);
8034
8035   gtk_widget_get_allocation (widget, &allocation);
8036
8037   /* can't use rectangle_intersect since cursor rect can have 0 width */
8038   if (cursor_rect.x >= onscreen_rect.x &&
8039       cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
8040       cursor_rect.y >= onscreen_rect.y &&
8041       cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
8042     {    
8043       gtk_text_view_buffer_to_window_coords (text_view,
8044                                              GTK_TEXT_WINDOW_WIDGET,
8045                                              cursor_rect.x, cursor_rect.y,
8046                                              &cursor_rect.x, &cursor_rect.y);
8047
8048       *x = root_x + cursor_rect.x + cursor_rect.width;
8049       *y = root_y + cursor_rect.y + cursor_rect.height;
8050     }
8051   else
8052     {
8053       /* Just center the menu, since cursor is offscreen. */
8054       *x = root_x + (allocation.width / 2 - req.width / 2);
8055       *y = root_y + (allocation.height / 2 - req.height / 2);
8056     }
8057
8058   /* Ensure sanity */
8059   *x = CLAMP (*x, root_x, (root_x + allocation.width));
8060   *y = CLAMP (*y, root_y, (root_y + allocation.height));
8061
8062   monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
8063   gtk_menu_set_monitor (menu, monitor_num);
8064   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
8065
8066   *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
8067   *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
8068
8069   *push_in = FALSE;
8070 }
8071
8072 typedef struct
8073 {
8074   GtkTextView *text_view;
8075   gint button;
8076   guint time;
8077 } PopupInfo;
8078
8079 static gboolean
8080 range_contains_editable_text (const GtkTextIter *start,
8081                               const GtkTextIter *end,
8082                               gboolean default_editability)
8083 {
8084   GtkTextIter iter = *start;
8085
8086   while (gtk_text_iter_compare (&iter, end) < 0)
8087     {
8088       if (gtk_text_iter_editable (&iter, default_editability))
8089         return TRUE;
8090       
8091       gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
8092     }
8093
8094   return FALSE;
8095 }                             
8096
8097 static void
8098 unichar_chosen_func (const char *text,
8099                      gpointer    data)
8100 {
8101   GtkTextView *text_view = GTK_TEXT_VIEW (data);
8102
8103   gtk_text_view_commit_text (text_view, text);
8104 }
8105
8106 static void
8107 popup_targets_received (GtkClipboard     *clipboard,
8108                         GtkSelectionData *data,
8109                         gpointer          user_data)
8110 {
8111   PopupInfo *info = user_data;
8112   GtkTextView *text_view;
8113   GtkTextViewPrivate *priv;
8114
8115   text_view = info->text_view;
8116   priv = text_view->priv;
8117
8118   if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
8119     {
8120       /* We implicitely rely here on the fact that if we are pasting ourself, we'll
8121        * have text targets as well as the private GTK_TEXT_BUFFER_CONTENTS target.
8122        */
8123       gboolean clipboard_contains_text;
8124       GtkWidget *menuitem;
8125       GtkWidget *submenu;
8126       gboolean have_selection;
8127       gboolean can_insert;
8128       GtkTextIter iter;
8129       GtkTextIter sel_start, sel_end;
8130       gboolean show_input_method_menu;
8131       gboolean show_unicode_menu;
8132       
8133       clipboard_contains_text = gtk_selection_data_targets_include_text (data);
8134
8135       if (priv->popup_menu)
8136         gtk_widget_destroy (priv->popup_menu);
8137
8138       priv->popup_menu = gtk_menu_new ();
8139       
8140       gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
8141                                  GTK_WIDGET (text_view),
8142                                  popup_menu_detach);
8143       
8144       have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
8145                                                              &sel_start, &sel_end);
8146       
8147       gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
8148                                         &iter,
8149                                         gtk_text_buffer_get_insert (get_buffer (text_view)));
8150       
8151       can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
8152       
8153       append_action_signal (text_view, priv->popup_menu, GTK_STOCK_CUT, "cut-clipboard",
8154                             have_selection &&
8155                             range_contains_editable_text (&sel_start, &sel_end,
8156                                                           priv->editable));
8157       append_action_signal (text_view, priv->popup_menu, GTK_STOCK_COPY, "copy-clipboard",
8158                             have_selection);
8159       append_action_signal (text_view, priv->popup_menu, GTK_STOCK_PASTE, "paste-clipboard",
8160                             can_insert && clipboard_contains_text);
8161       
8162       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
8163       gtk_widget_set_sensitive (menuitem, 
8164                                 have_selection &&
8165                                 range_contains_editable_text (&sel_start, &sel_end,
8166                                                               priv->editable));
8167       g_signal_connect_swapped (menuitem, "activate",
8168                                 G_CALLBACK (delete_cb), text_view);
8169       gtk_widget_show (menuitem);
8170       gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8171
8172       menuitem = gtk_separator_menu_item_new ();
8173       gtk_widget_show (menuitem);
8174       gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8175
8176       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
8177       g_signal_connect (menuitem, "activate",
8178                         G_CALLBACK (select_all_cb), text_view);
8179       gtk_widget_show (menuitem);
8180       gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8181
8182       g_object_get (gtk_widget_get_settings (GTK_WIDGET (text_view)),
8183                     "gtk-show-input-method-menu", &show_input_method_menu,
8184                     "gtk-show-unicode-menu", &show_unicode_menu,
8185                     NULL);
8186       
8187       if (show_input_method_menu || show_unicode_menu)
8188         {
8189           menuitem = gtk_separator_menu_item_new ();
8190           gtk_widget_show (menuitem);
8191           gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8192         }
8193
8194       if (show_input_method_menu)
8195         {
8196           menuitem = gtk_menu_item_new_with_mnemonic (_("Input _Methods"));
8197           gtk_widget_show (menuitem);
8198           gtk_widget_set_sensitive (menuitem, can_insert);
8199
8200           submenu = gtk_menu_new ();
8201           gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
8202           gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8203           
8204           gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (priv->im_context),
8205                                                 GTK_MENU_SHELL (submenu));
8206         }
8207
8208       if (show_unicode_menu)
8209         {
8210           menuitem = gtk_menu_item_new_with_mnemonic (_("_Insert Unicode Control Character"));
8211           gtk_widget_show (menuitem);
8212           gtk_widget_set_sensitive (menuitem, can_insert);
8213       
8214           submenu = gtk_menu_new ();
8215           gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
8216           gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
8217           
8218           _gtk_text_util_append_special_char_menuitems (GTK_MENU_SHELL (submenu),
8219                                                         unichar_chosen_func,
8220                                                         text_view);
8221         }
8222           
8223       g_signal_emit (text_view,
8224                      signals[POPULATE_POPUP],
8225                      0,
8226                      priv->popup_menu);
8227       
8228       if (info->button)
8229         gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
8230                         NULL, NULL,
8231                         info->button, info->time);
8232       else
8233         {
8234           gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
8235                           popup_position_func, text_view,
8236                           0, gtk_get_current_event_time ());
8237           gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
8238         }
8239     }
8240
8241   g_object_unref (text_view);
8242   g_free (info);
8243 }
8244
8245 static void
8246 gtk_text_view_do_popup (GtkTextView    *text_view,
8247                         GdkEventButton *event)
8248 {
8249   PopupInfo *info = g_new (PopupInfo, 1);
8250
8251   /* In order to know what entries we should make sensitive, we
8252    * ask for the current targets of the clipboard, and when
8253    * we get them, then we actually pop up the menu.
8254    */
8255   info->text_view = g_object_ref (text_view);
8256   
8257   if (event)
8258     {
8259       info->button = event->button;
8260       info->time = event->time;
8261     }
8262   else
8263     {
8264       info->button = 0;
8265       info->time = gtk_get_current_event_time ();
8266     }
8267
8268   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
8269                                                             GDK_SELECTION_CLIPBOARD),
8270                                   gdk_atom_intern_static_string ("TARGETS"),
8271                                   popup_targets_received,
8272                                   info);
8273 }
8274
8275 static gboolean
8276 gtk_text_view_popup_menu (GtkWidget *widget)
8277 {
8278   gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);  
8279   return TRUE;
8280 }
8281
8282 /* Child GdkWindows */
8283
8284
8285 static GtkTextWindow*
8286 text_window_new (GtkTextWindowType  type,
8287                  GtkWidget         *widget,
8288                  gint               width_request,
8289                  gint               height_request)
8290 {
8291   GtkTextWindow *win;
8292
8293   win = g_new (GtkTextWindow, 1);
8294
8295   win->type = type;
8296   win->widget = widget;
8297   win->window = NULL;
8298   win->bin_window = NULL;
8299   win->requisition.width = width_request;
8300   win->requisition.height = height_request;
8301   win->allocation.width = width_request;
8302   win->allocation.height = height_request;
8303   win->allocation.x = 0;
8304   win->allocation.y = 0;
8305
8306   return win;
8307 }
8308
8309 static void
8310 text_window_free (GtkTextWindow *win)
8311 {
8312   if (win->window)
8313     text_window_unrealize (win);
8314
8315   g_free (win);
8316 }
8317
8318 static void
8319 text_window_realize (GtkTextWindow *win,
8320                      GtkWidget     *widget)
8321 {
8322   GdkWindow *window;
8323   GdkWindowAttr attributes;
8324   gint attributes_mask;
8325   GdkCursor *cursor;
8326
8327   attributes.window_type = GDK_WINDOW_CHILD;
8328   attributes.x = win->allocation.x;
8329   attributes.y = win->allocation.y;
8330   attributes.width = win->allocation.width;
8331   attributes.height = win->allocation.height;
8332   attributes.wclass = GDK_INPUT_OUTPUT;
8333   attributes.visual = gtk_widget_get_visual (win->widget);
8334   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
8335
8336   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
8337
8338   window = gtk_widget_get_window (widget);
8339
8340   win->window = gdk_window_new (window,
8341                                 &attributes, attributes_mask);
8342
8343   gdk_window_show (win->window);
8344   gdk_window_set_user_data (win->window, win->widget);
8345   gdk_window_lower (win->window);
8346
8347   attributes.x = 0;
8348   attributes.y = 0;
8349   attributes.width = win->allocation.width;
8350   attributes.height = win->allocation.height;
8351   attributes.event_mask = (GDK_EXPOSURE_MASK            |
8352                            GDK_SCROLL_MASK              |
8353                            GDK_KEY_PRESS_MASK           |
8354                            GDK_BUTTON_PRESS_MASK        |
8355                            GDK_BUTTON_RELEASE_MASK      |
8356                            GDK_POINTER_MOTION_MASK      |
8357                            GDK_POINTER_MOTION_HINT_MASK |
8358                            gtk_widget_get_events (win->widget));
8359
8360   win->bin_window = gdk_window_new (win->window,
8361                                     &attributes,
8362                                     attributes_mask);
8363
8364   gdk_window_show (win->bin_window);
8365   gdk_window_set_user_data (win->bin_window, win->widget);
8366
8367   if (win->type == GTK_TEXT_WINDOW_TEXT)
8368     {
8369       if (gtk_widget_is_sensitive (widget))
8370         {
8371           /* I-beam cursor */
8372           cursor = gdk_cursor_new_for_display (gdk_window_get_display (window),
8373                                                GDK_XTERM);
8374           gdk_window_set_cursor (win->bin_window, cursor);
8375           gdk_cursor_unref (cursor);
8376         } 
8377
8378       gtk_im_context_set_client_window (GTK_TEXT_VIEW (widget)->priv->im_context,
8379                                         win->window);
8380
8381
8382       gdk_window_set_background (win->bin_window,
8383                                  &gtk_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
8384     }
8385   else
8386     {
8387       gdk_window_set_background (win->bin_window,
8388                                  &gtk_widget_get_style (widget)->bg[gtk_widget_get_state (widget)]);
8389     }
8390
8391   g_object_set_qdata (G_OBJECT (win->window),
8392                       g_quark_from_static_string ("gtk-text-view-text-window"),
8393                       win);
8394
8395   g_object_set_qdata (G_OBJECT (win->bin_window),
8396                       g_quark_from_static_string ("gtk-text-view-text-window"),
8397                       win);
8398 }
8399
8400 static void
8401 text_window_unrealize (GtkTextWindow *win)
8402 {
8403   if (win->type == GTK_TEXT_WINDOW_TEXT)
8404     {
8405       gtk_im_context_set_client_window (GTK_TEXT_VIEW (win->widget)->priv->im_context,
8406                                         NULL);
8407     }
8408
8409   gdk_window_set_user_data (win->window, NULL);
8410   gdk_window_set_user_data (win->bin_window, NULL);
8411   gdk_window_destroy (win->bin_window);
8412   gdk_window_destroy (win->window);
8413   win->window = NULL;
8414   win->bin_window = NULL;
8415 }
8416
8417 static void
8418 text_window_size_allocate (GtkTextWindow *win,
8419                            GdkRectangle  *rect)
8420 {
8421   win->allocation = *rect;
8422
8423   if (win->window)
8424     {
8425       gdk_window_move_resize (win->window,
8426                               rect->x, rect->y,
8427                               rect->width, rect->height);
8428
8429       gdk_window_resize (win->bin_window,
8430                          rect->width, rect->height);
8431     }
8432 }
8433
8434 static void
8435 text_window_scroll        (GtkTextWindow *win,
8436                            gint           dx,
8437                            gint           dy)
8438 {
8439   if (dx != 0 || dy != 0)
8440     {
8441       gdk_window_scroll (win->bin_window, dx, dy);
8442     }
8443 }
8444
8445 static void
8446 text_window_invalidate_rect (GtkTextWindow *win,
8447                              GdkRectangle  *rect)
8448 {
8449   GdkRectangle window_rect;
8450
8451   gtk_text_view_buffer_to_window_coords (GTK_TEXT_VIEW (win->widget),
8452                                          win->type,
8453                                          rect->x,
8454                                          rect->y,
8455                                          &window_rect.x,
8456                                          &window_rect.y);
8457
8458   window_rect.width = rect->width;
8459   window_rect.height = rect->height;
8460   
8461   /* Adjust the rect as appropriate */
8462   
8463   switch (win->type)
8464     {
8465     case GTK_TEXT_WINDOW_TEXT:
8466       break;
8467
8468     case GTK_TEXT_WINDOW_LEFT:
8469     case GTK_TEXT_WINDOW_RIGHT:
8470       window_rect.x = 0;
8471       window_rect.width = win->allocation.width;
8472       break;
8473
8474     case GTK_TEXT_WINDOW_TOP:
8475     case GTK_TEXT_WINDOW_BOTTOM:
8476       window_rect.y = 0;
8477       window_rect.height = win->allocation.height;
8478       break;
8479
8480     default:
8481       g_warning ("%s: bug!", G_STRFUNC);
8482       return;
8483       break;
8484     }
8485           
8486   gdk_window_invalidate_rect (win->bin_window, &window_rect, FALSE);
8487
8488 #if 0
8489   {
8490     cairo_t *cr = gdk_cairo_create (win->bin_window);
8491     gdk_cairo_rectangle (cr, &window_rect);
8492     cairo_set_source_rgb  (cr, 1.0, 0.0, 0.0);  /* red */
8493     cairo_fill (cr);
8494     cairo_destroy (cr);
8495   }
8496 #endif
8497 }
8498
8499 static void
8500 text_window_invalidate_cursors (GtkTextWindow *win)
8501 {
8502   GtkTextView *text_view;
8503   GtkTextViewPrivate *priv;
8504   GtkTextIter  iter;
8505   GdkRectangle strong;
8506   GdkRectangle weak;
8507   gboolean     draw_arrow;
8508   gfloat       cursor_aspect_ratio;
8509   gint         stem_width;
8510   gint         arrow_width;
8511
8512   text_view = GTK_TEXT_VIEW (win->widget);
8513   priv = text_view->priv;
8514
8515   gtk_text_buffer_get_iter_at_mark (priv->buffer, &iter,
8516                                     gtk_text_buffer_get_insert (priv->buffer));
8517
8518   if (_gtk_text_layout_get_block_cursor (priv->layout, &strong))
8519     {
8520       text_window_invalidate_rect (win, &strong);
8521       return;
8522     }
8523
8524   gtk_text_layout_get_cursor_locations (priv->layout, &iter,
8525                                         &strong, &weak);
8526
8527   /* cursor width calculation as in gtkstyle.c:draw_insertion_cursor(),
8528    * ignoring the text direction be exposing both sides of the cursor
8529    */
8530
8531   draw_arrow = (strong.x != weak.x || strong.y != weak.y);
8532
8533   gtk_widget_style_get (win->widget,
8534                         "cursor-aspect-ratio", &cursor_aspect_ratio,
8535                         NULL);
8536   
8537   stem_width = strong.height * cursor_aspect_ratio + 1;
8538   arrow_width = stem_width + 1;
8539
8540   strong.width = stem_width;
8541
8542   /* round up to the next even number */
8543   if (stem_width & 1)
8544     stem_width++;
8545
8546   strong.x     -= stem_width / 2;
8547   strong.width += stem_width;
8548
8549   if (draw_arrow)
8550     {
8551       strong.x     -= arrow_width;
8552       strong.width += arrow_width * 2;
8553     }
8554
8555   text_window_invalidate_rect (win, &strong);
8556
8557   if (draw_arrow) /* == have weak */
8558     {
8559       stem_width = weak.height * cursor_aspect_ratio + 1;
8560       arrow_width = stem_width + 1;
8561
8562       weak.width = stem_width;
8563
8564       /* round up to the next even number */
8565       if (stem_width & 1)
8566         stem_width++;
8567
8568       weak.x     -= stem_width / 2;
8569       weak.width += stem_width;
8570
8571       weak.x     -= arrow_width;
8572       weak.width += arrow_width * 2;
8573
8574       text_window_invalidate_rect (win, &weak);
8575     }
8576 }
8577
8578 static gint
8579 text_window_get_width (GtkTextWindow *win)
8580 {
8581   return win->allocation.width;
8582 }
8583
8584 static gint
8585 text_window_get_height (GtkTextWindow *win)
8586 {
8587   return win->allocation.height;
8588 }
8589
8590 /* Windows */
8591
8592
8593 /**
8594  * gtk_text_view_get_window:
8595  * @text_view: a #GtkTextView
8596  * @win: window to get
8597  *
8598  * Retrieves the #GdkWindow corresponding to an area of the text view;
8599  * possible windows include the overall widget window, child windows
8600  * on the left, right, top, bottom, and the window that displays the
8601  * text buffer. Windows are %NULL and nonexistent if their width or
8602  * height is 0, and are nonexistent before the widget has been
8603  * realized.
8604  *
8605  * Return value: (transfer none): a #GdkWindow, or %NULL
8606  **/
8607 GdkWindow*
8608 gtk_text_view_get_window (GtkTextView *text_view,
8609                           GtkTextWindowType win)
8610 {
8611   GtkTextViewPrivate *priv = text_view->priv;
8612
8613   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
8614
8615   switch (win)
8616     {
8617     case GTK_TEXT_WINDOW_WIDGET:
8618       return gtk_widget_get_window (GTK_WIDGET (text_view));
8619       break;
8620
8621     case GTK_TEXT_WINDOW_TEXT:
8622       return priv->text_window->bin_window;
8623       break;
8624
8625     case GTK_TEXT_WINDOW_LEFT:
8626       if (priv->left_window)
8627         return priv->left_window->bin_window;
8628       else
8629         return NULL;
8630       break;
8631
8632     case GTK_TEXT_WINDOW_RIGHT:
8633       if (priv->right_window)
8634         return priv->right_window->bin_window;
8635       else
8636         return NULL;
8637       break;
8638
8639     case GTK_TEXT_WINDOW_TOP:
8640       if (priv->top_window)
8641         return priv->top_window->bin_window;
8642       else
8643         return NULL;
8644       break;
8645
8646     case GTK_TEXT_WINDOW_BOTTOM:
8647       if (priv->bottom_window)
8648         return priv->bottom_window->bin_window;
8649       else
8650         return NULL;
8651       break;
8652
8653     case GTK_TEXT_WINDOW_PRIVATE:
8654       g_warning ("%s: You can't get GTK_TEXT_WINDOW_PRIVATE, it has \"PRIVATE\" in the name because it is private.", G_STRFUNC);
8655       return NULL;
8656       break;
8657     }
8658
8659   g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
8660   return NULL;
8661 }
8662
8663 /**
8664  * gtk_text_view_get_window_type:
8665  * @text_view: a #GtkTextView
8666  * @window: a window type
8667  *
8668  * Usually used to find out which window an event corresponds to.
8669  * If you connect to an event signal on @text_view, this function
8670  * should be called on <literal>event-&gt;window</literal> to
8671  * see which window it was.
8672  *
8673  * Return value: the window type.
8674  **/
8675 GtkTextWindowType
8676 gtk_text_view_get_window_type (GtkTextView *text_view,
8677                                GdkWindow   *window)
8678 {
8679   GtkTextWindow *win;
8680
8681   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
8682   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
8683
8684   if (window == gtk_widget_get_window (GTK_WIDGET (text_view)))
8685     return GTK_TEXT_WINDOW_WIDGET;
8686
8687   win = g_object_get_qdata (G_OBJECT (window),
8688                             g_quark_try_string ("gtk-text-view-text-window"));
8689
8690   if (win)
8691     return win->type;
8692   else
8693     {
8694       return GTK_TEXT_WINDOW_PRIVATE;
8695     }
8696 }
8697
8698 static void
8699 buffer_to_widget (GtkTextView      *text_view,
8700                   gint              buffer_x,
8701                   gint              buffer_y,
8702                   gint             *window_x,
8703                   gint             *window_y)
8704 {
8705   GtkTextViewPrivate *priv = text_view->priv;
8706
8707   if (window_x)
8708     {
8709       *window_x = buffer_x - priv->xoffset;
8710       *window_x += priv->text_window->allocation.x;
8711     }
8712
8713   if (window_y)
8714     {
8715       *window_y = buffer_y - priv->yoffset;
8716       *window_y += priv->text_window->allocation.y;
8717     }
8718 }
8719
8720 static void
8721 widget_to_text_window (GtkTextWindow *win,
8722                        gint           widget_x,
8723                        gint           widget_y,
8724                        gint          *window_x,
8725                        gint          *window_y)
8726 {
8727   if (window_x)
8728     *window_x = widget_x - win->allocation.x;
8729
8730   if (window_y)
8731     *window_y = widget_y - win->allocation.y;
8732 }
8733
8734 static void
8735 buffer_to_text_window (GtkTextView   *text_view,
8736                        GtkTextWindow *win,
8737                        gint           buffer_x,
8738                        gint           buffer_y,
8739                        gint          *window_x,
8740                        gint          *window_y)
8741 {
8742   if (win == NULL)
8743     {
8744       g_warning ("Attempt to convert text buffer coordinates to coordinates "
8745                  "for a nonexistent or private child window of GtkTextView");
8746       return;
8747     }
8748
8749   buffer_to_widget (text_view,
8750                     buffer_x, buffer_y,
8751                     window_x, window_y);
8752
8753   widget_to_text_window (win,
8754                          window_x ? *window_x : 0,
8755                          window_y ? *window_y : 0,
8756                          window_x,
8757                          window_y);
8758 }
8759
8760 /**
8761  * gtk_text_view_buffer_to_window_coords:
8762  * @text_view: a #GtkTextView
8763  * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
8764  * @buffer_x: buffer x coordinate
8765  * @buffer_y: buffer y coordinate
8766  * @window_x: (out) (allow-none): window x coordinate return location or %NULL
8767  * @window_y: (out) (allow-none): window y coordinate return location or %NULL
8768  *
8769  * Converts coordinate (@buffer_x, @buffer_y) to coordinates for the window
8770  * @win, and stores the result in (@window_x, @window_y). 
8771  *
8772  * Note that you can't convert coordinates for a nonexisting window (see 
8773  * gtk_text_view_set_border_window_size()).
8774  **/
8775 void
8776 gtk_text_view_buffer_to_window_coords (GtkTextView      *text_view,
8777                                        GtkTextWindowType win,
8778                                        gint              buffer_x,
8779                                        gint              buffer_y,
8780                                        gint             *window_x,
8781                                        gint             *window_y)
8782 {
8783   GtkTextViewPrivate *priv = text_view->priv;
8784
8785   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
8786
8787   switch (win)
8788     {
8789     case GTK_TEXT_WINDOW_WIDGET:
8790       buffer_to_widget (text_view,
8791                         buffer_x, buffer_y,
8792                         window_x, window_y);
8793       break;
8794
8795     case GTK_TEXT_WINDOW_TEXT:
8796       if (window_x)
8797         *window_x = buffer_x - priv->xoffset;
8798       if (window_y)
8799         *window_y = buffer_y - priv->yoffset;
8800       break;
8801
8802     case GTK_TEXT_WINDOW_LEFT:
8803       buffer_to_text_window (text_view,
8804                              priv->left_window,
8805                              buffer_x, buffer_y,
8806                              window_x, window_y);
8807       break;
8808
8809     case GTK_TEXT_WINDOW_RIGHT:
8810       buffer_to_text_window (text_view,
8811                              priv->right_window,
8812                              buffer_x, buffer_y,
8813                              window_x, window_y);
8814       break;
8815
8816     case GTK_TEXT_WINDOW_TOP:
8817       buffer_to_text_window (text_view,
8818                              priv->top_window,
8819                              buffer_x, buffer_y,
8820                              window_x, window_y);
8821       break;
8822
8823     case GTK_TEXT_WINDOW_BOTTOM:
8824       buffer_to_text_window (text_view,
8825                              priv->bottom_window,
8826                              buffer_x, buffer_y,
8827                              window_x, window_y);
8828       break;
8829
8830     case GTK_TEXT_WINDOW_PRIVATE:
8831       g_warning ("%s: can't get coords for private windows", G_STRFUNC);
8832       break;
8833
8834     default:
8835       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
8836       break;
8837     }
8838 }
8839
8840 static void
8841 widget_to_buffer (GtkTextView *text_view,
8842                   gint         widget_x,
8843                   gint         widget_y,
8844                   gint        *buffer_x,
8845                   gint        *buffer_y)
8846 {
8847   GtkTextViewPrivate *priv = text_view->priv;
8848
8849   if (buffer_x)
8850     {
8851       *buffer_x = widget_x + priv->xoffset;
8852       *buffer_x -= priv->text_window->allocation.x;
8853     }
8854
8855   if (buffer_y)
8856     {
8857       *buffer_y = widget_y + priv->yoffset;
8858       *buffer_y -= priv->text_window->allocation.y;
8859     }
8860 }
8861
8862 static void
8863 text_window_to_widget (GtkTextWindow *win,
8864                        gint           window_x,
8865                        gint           window_y,
8866                        gint          *widget_x,
8867                        gint          *widget_y)
8868 {
8869   if (widget_x)
8870     *widget_x = window_x + win->allocation.x;
8871
8872   if (widget_y)
8873     *widget_y = window_y + win->allocation.y;
8874 }
8875
8876 static void
8877 text_window_to_buffer (GtkTextView   *text_view,
8878                        GtkTextWindow *win,
8879                        gint           window_x,
8880                        gint           window_y,
8881                        gint          *buffer_x,
8882                        gint          *buffer_y)
8883 {
8884   if (win == NULL)
8885     {
8886       g_warning ("Attempt to convert GtkTextView buffer coordinates into "
8887                  "coordinates for a nonexistent child window.");
8888       return;
8889     }
8890
8891   text_window_to_widget (win,
8892                          window_x,
8893                          window_y,
8894                          buffer_x,
8895                          buffer_y);
8896
8897   widget_to_buffer (text_view,
8898                     buffer_x ? *buffer_x : 0,
8899                     buffer_y ? *buffer_y : 0,
8900                     buffer_x,
8901                     buffer_y);
8902 }
8903
8904 /**
8905  * gtk_text_view_window_to_buffer_coords:
8906  * @text_view: a #GtkTextView
8907  * @win: a #GtkTextWindowType except #GTK_TEXT_WINDOW_PRIVATE
8908  * @window_x: window x coordinate
8909  * @window_y: window y coordinate
8910  * @buffer_x: (out) (allow-none): buffer x coordinate return location or %NULL
8911  * @buffer_y: (out) (allow-none): buffer y coordinate return location or %NULL
8912  *
8913  * Converts coordinates on the window identified by @win to buffer
8914  * coordinates, storing the result in (@buffer_x,@buffer_y).
8915  *
8916  * Note that you can't convert coordinates for a nonexisting window (see 
8917  * gtk_text_view_set_border_window_size()).
8918  **/
8919 void
8920 gtk_text_view_window_to_buffer_coords (GtkTextView      *text_view,
8921                                        GtkTextWindowType win,
8922                                        gint              window_x,
8923                                        gint              window_y,
8924                                        gint             *buffer_x,
8925                                        gint             *buffer_y)
8926 {
8927   GtkTextViewPrivate *priv = text_view->priv;
8928
8929   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
8930
8931   switch (win)
8932     {
8933     case GTK_TEXT_WINDOW_WIDGET:
8934       widget_to_buffer (text_view,
8935                         window_x, window_y,
8936                         buffer_x, buffer_y);
8937       break;
8938
8939     case GTK_TEXT_WINDOW_TEXT:
8940       if (buffer_x)
8941         *buffer_x = window_x + priv->xoffset;
8942       if (buffer_y)
8943         *buffer_y = window_y + priv->yoffset;
8944       break;
8945
8946     case GTK_TEXT_WINDOW_LEFT:
8947       text_window_to_buffer (text_view,
8948                              priv->left_window,
8949                              window_x, window_y,
8950                              buffer_x, buffer_y);
8951       break;
8952
8953     case GTK_TEXT_WINDOW_RIGHT:
8954       text_window_to_buffer (text_view,
8955                              priv->right_window,
8956                              window_x, window_y,
8957                              buffer_x, buffer_y);
8958       break;
8959
8960     case GTK_TEXT_WINDOW_TOP:
8961       text_window_to_buffer (text_view,
8962                              priv->top_window,
8963                              window_x, window_y,
8964                              buffer_x, buffer_y);
8965       break;
8966
8967     case GTK_TEXT_WINDOW_BOTTOM:
8968       text_window_to_buffer (text_view,
8969                              priv->bottom_window,
8970                              window_x, window_y,
8971                              buffer_x, buffer_y);
8972       break;
8973
8974     case GTK_TEXT_WINDOW_PRIVATE:
8975       g_warning ("%s: can't get coords for private windows", G_STRFUNC);
8976       break;
8977
8978     default:
8979       g_warning ("%s: Unknown GtkTextWindowType", G_STRFUNC);
8980       break;
8981     }
8982 }
8983
8984 static void
8985 set_window_width (GtkTextView      *text_view,
8986                   gint              width,
8987                   GtkTextWindowType type,
8988                   GtkTextWindow   **winp)
8989 {
8990   if (width == 0)
8991     {
8992       if (*winp)
8993         {
8994           text_window_free (*winp);
8995           *winp = NULL;
8996           gtk_widget_queue_resize (GTK_WIDGET (text_view));
8997         }
8998     }
8999   else
9000     {
9001       if (*winp == NULL)
9002         {
9003           *winp = text_window_new (type,
9004                                    GTK_WIDGET (text_view),
9005                                    width, 0);
9006           /* if the widget is already realized we need to realize the child manually */
9007           if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
9008             text_window_realize (*winp, GTK_WIDGET (text_view));
9009         }
9010       else
9011         {
9012           if ((*winp)->requisition.width == width)
9013             return;
9014
9015           (*winp)->requisition.width = width;
9016         }
9017
9018       gtk_widget_queue_resize (GTK_WIDGET (text_view));
9019     }
9020 }
9021
9022
9023 static void
9024 set_window_height (GtkTextView      *text_view,
9025                    gint              height,
9026                    GtkTextWindowType type,
9027                    GtkTextWindow   **winp)
9028 {
9029   if (height == 0)
9030     {
9031       if (*winp)
9032         {
9033           text_window_free (*winp);
9034           *winp = NULL;
9035           gtk_widget_queue_resize (GTK_WIDGET (text_view));
9036         }
9037     }
9038   else
9039     {
9040       if (*winp == NULL)
9041         {
9042           *winp = text_window_new (type,
9043                                    GTK_WIDGET (text_view),
9044                                    0, height);
9045
9046           /* if the widget is already realized we need to realize the child manually */
9047           if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
9048             text_window_realize (*winp, GTK_WIDGET (text_view));
9049         }
9050       else
9051         {
9052           if ((*winp)->requisition.height == height)
9053             return;
9054
9055           (*winp)->requisition.height = height;
9056         }
9057
9058       gtk_widget_queue_resize (GTK_WIDGET (text_view));
9059     }
9060 }
9061
9062 /**
9063  * gtk_text_view_set_border_window_size:
9064  * @text_view: a #GtkTextView
9065  * @type: window to affect
9066  * @size: width or height of the window
9067  *
9068  * Sets the width of %GTK_TEXT_WINDOW_LEFT or %GTK_TEXT_WINDOW_RIGHT,
9069  * or the height of %GTK_TEXT_WINDOW_TOP or %GTK_TEXT_WINDOW_BOTTOM.
9070  * Automatically destroys the corresponding window if the size is set
9071  * to 0, and creates the window if the size is set to non-zero.  This
9072  * function can only be used for the "border windows," it doesn't work
9073  * with #GTK_TEXT_WINDOW_WIDGET, #GTK_TEXT_WINDOW_TEXT, or
9074  * #GTK_TEXT_WINDOW_PRIVATE.
9075  **/
9076 void
9077 gtk_text_view_set_border_window_size (GtkTextView      *text_view,
9078                                       GtkTextWindowType type,
9079                                       gint              size)
9080 {
9081   GtkTextViewPrivate *priv = text_view->priv;
9082
9083   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9084   g_return_if_fail (size >= 0);
9085
9086   switch (type)
9087     {
9088     case GTK_TEXT_WINDOW_LEFT:
9089       set_window_width (text_view, size, GTK_TEXT_WINDOW_LEFT,
9090                         &priv->left_window);
9091       break;
9092
9093     case GTK_TEXT_WINDOW_RIGHT:
9094       set_window_width (text_view, size, GTK_TEXT_WINDOW_RIGHT,
9095                         &priv->right_window);
9096       break;
9097
9098     case GTK_TEXT_WINDOW_TOP:
9099       set_window_height (text_view, size, GTK_TEXT_WINDOW_TOP,
9100                          &priv->top_window);
9101       break;
9102
9103     case GTK_TEXT_WINDOW_BOTTOM:
9104       set_window_height (text_view, size, GTK_TEXT_WINDOW_BOTTOM,
9105                          &priv->bottom_window);
9106       break;
9107
9108     default:
9109       g_warning ("Can only set size of left/right/top/bottom border windows with gtk_text_view_set_border_window_size()");
9110       break;
9111     }
9112 }
9113
9114 /**
9115  * gtk_text_view_get_border_window_size:
9116  * @text_view: a #GtkTextView
9117  * @type: window to return size from
9118  *
9119  * Gets the width of the specified border window. See
9120  * gtk_text_view_set_border_window_size().
9121  *
9122  * Return value: width of window
9123  **/
9124 gint
9125 gtk_text_view_get_border_window_size (GtkTextView       *text_view,
9126                                       GtkTextWindowType  type)
9127 {
9128   GtkTextViewPrivate *priv = text_view->priv;
9129
9130   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), 0);
9131   
9132   switch (type)
9133     {
9134     case GTK_TEXT_WINDOW_LEFT:
9135       if (priv->left_window)
9136         return priv->left_window->requisition.width;
9137       break;
9138       
9139     case GTK_TEXT_WINDOW_RIGHT:
9140       if (priv->right_window)
9141         return priv->right_window->requisition.width;
9142       break;
9143       
9144     case GTK_TEXT_WINDOW_TOP:
9145       if (priv->top_window)
9146         return priv->top_window->requisition.height;
9147       break;
9148
9149     case GTK_TEXT_WINDOW_BOTTOM:
9150       if (priv->bottom_window)
9151         return priv->bottom_window->requisition.height;
9152       break;
9153       
9154     default:
9155       g_warning ("Can only get size of left/right/top/bottom border windows with gtk_text_view_get_border_window_size()");
9156       break;
9157     }
9158
9159   return 0;
9160 }
9161
9162 /*
9163  * Child widgets
9164  */
9165
9166 static GtkTextViewChild*
9167 text_view_child_new_anchored (GtkWidget          *child,
9168                               GtkTextChildAnchor *anchor,
9169                               GtkTextLayout      *layout)
9170 {
9171   GtkTextViewChild *vc;
9172
9173   vc = g_new (GtkTextViewChild, 1);
9174
9175   vc->type = GTK_TEXT_WINDOW_PRIVATE;
9176   vc->widget = child;
9177   vc->anchor = anchor;
9178
9179   vc->from_top_of_line = 0;
9180   vc->from_left_of_buffer = 0;
9181   
9182   g_object_ref (vc->widget);
9183   g_object_ref (vc->anchor);
9184
9185   g_object_set_data (G_OBJECT (child),
9186                      I_("gtk-text-view-child"),
9187                      vc);
9188
9189   gtk_text_child_anchor_register_child (anchor, child, layout);
9190   
9191   return vc;
9192 }
9193
9194 static GtkTextViewChild*
9195 text_view_child_new_window (GtkWidget          *child,
9196                             GtkTextWindowType   type,
9197                             gint                x,
9198                             gint                y)
9199 {
9200   GtkTextViewChild *vc;
9201
9202   vc = g_new (GtkTextViewChild, 1);
9203
9204   vc->widget = child;
9205   vc->anchor = NULL;
9206
9207   vc->from_top_of_line = 0;
9208   vc->from_left_of_buffer = 0;
9209  
9210   g_object_ref (vc->widget);
9211
9212   vc->type = type;
9213   vc->x = x;
9214   vc->y = y;
9215
9216   g_object_set_data (G_OBJECT (child),
9217                      I_("gtk-text-view-child"),
9218                      vc);
9219   
9220   return vc;
9221 }
9222
9223 static void
9224 text_view_child_free (GtkTextViewChild *child)
9225 {
9226   g_object_set_data (G_OBJECT (child->widget),
9227                      I_("gtk-text-view-child"), NULL);
9228
9229   if (child->anchor)
9230     {
9231       gtk_text_child_anchor_unregister_child (child->anchor,
9232                                               child->widget);
9233       g_object_unref (child->anchor);
9234     }
9235
9236   g_object_unref (child->widget);
9237
9238   g_free (child);
9239 }
9240
9241 static void
9242 text_view_child_set_parent_window (GtkTextView      *text_view,
9243                                    GtkTextViewChild *vc)
9244 {
9245   if (vc->anchor)
9246     gtk_widget_set_parent_window (vc->widget,
9247                                   text_view->priv->text_window->bin_window);
9248   else
9249     {
9250       GdkWindow *window;
9251       window = gtk_text_view_get_window (text_view,
9252                                          vc->type);
9253       gtk_widget_set_parent_window (vc->widget, window);
9254     }
9255 }
9256
9257 static void
9258 add_child (GtkTextView      *text_view,
9259            GtkTextViewChild *vc)
9260 {
9261   text_view->priv->children = g_slist_prepend (text_view->priv->children,
9262                                                vc);
9263
9264   if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
9265     text_view_child_set_parent_window (text_view, vc);
9266   
9267   gtk_widget_set_parent (vc->widget, GTK_WIDGET (text_view));
9268 }
9269
9270 /**
9271  * gtk_text_view_add_child_at_anchor:
9272  * @text_view: a #GtkTextView
9273  * @child: a #GtkWidget
9274  * @anchor: a #GtkTextChildAnchor in the #GtkTextBuffer for @text_view
9275  * 
9276  * Adds a child widget in the text buffer, at the given @anchor.
9277  **/
9278 void
9279 gtk_text_view_add_child_at_anchor (GtkTextView          *text_view,
9280                                    GtkWidget            *child,
9281                                    GtkTextChildAnchor   *anchor)
9282 {
9283   GtkTextViewChild *vc;
9284
9285   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9286   g_return_if_fail (GTK_IS_WIDGET (child));
9287   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
9288   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
9289
9290   gtk_text_view_ensure_layout (text_view);
9291
9292   vc = text_view_child_new_anchored (child, anchor,
9293                                      text_view->priv->layout);
9294
9295   add_child (text_view, vc);
9296
9297   g_assert (vc->widget == child);
9298   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
9299 }
9300
9301 /**
9302  * gtk_text_view_add_child_in_window:
9303  * @text_view: a #GtkTextView
9304  * @child: a #GtkWidget
9305  * @which_window: which window the child should appear in
9306  * @xpos: X position of child in window coordinates
9307  * @ypos: Y position of child in window coordinates
9308  *
9309  * Adds a child at fixed coordinates in one of the text widget's
9310  * windows. The window must have nonzero size (see
9311  * gtk_text_view_set_border_window_size()). Note that the child
9312  * coordinates are given relative to the #GdkWindow in question, and
9313  * that these coordinates have no sane relationship to scrolling. When
9314  * placing a child in #GTK_TEXT_WINDOW_WIDGET, scrolling is
9315  * irrelevant, the child floats above all scrollable areas. But when
9316  * placing a child in one of the scrollable windows (border windows or
9317  * text window), you'll need to compute the child's correct position
9318  * in buffer coordinates any time scrolling occurs or buffer changes
9319  * occur, and then call gtk_text_view_move_child() to update the
9320  * child's position. Unfortunately there's no good way to detect that
9321  * scrolling has occurred, using the current API; a possible hack
9322  * would be to update all child positions when the scroll adjustments
9323  * change or the text buffer changes. See bug 64518 on
9324  * bugzilla.gnome.org for status of fixing this issue.
9325  **/
9326 void
9327 gtk_text_view_add_child_in_window (GtkTextView       *text_view,
9328                                    GtkWidget         *child,
9329                                    GtkTextWindowType  which_window,
9330                                    gint               xpos,
9331                                    gint               ypos)
9332 {
9333   GtkTextViewChild *vc;
9334
9335   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9336   g_return_if_fail (GTK_IS_WIDGET (child));
9337   g_return_if_fail (gtk_widget_get_parent (child) == NULL);
9338
9339   vc = text_view_child_new_window (child, which_window,
9340                                    xpos, ypos);
9341
9342   add_child (text_view, vc);
9343
9344   g_assert (vc->widget == child);
9345   g_assert (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
9346 }
9347
9348 /**
9349  * gtk_text_view_move_child:
9350  * @text_view: a #GtkTextView
9351  * @child: child widget already added to the text view
9352  * @xpos: new X position in window coordinates
9353  * @ypos: new Y position in window coordinates
9354  *
9355  * Updates the position of a child, as for gtk_text_view_add_child_in_window().
9356  **/
9357 void
9358 gtk_text_view_move_child (GtkTextView *text_view,
9359                           GtkWidget   *child,
9360                           gint         xpos,
9361                           gint         ypos)
9362 {
9363   GtkTextViewChild *vc;
9364
9365   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
9366   g_return_if_fail (GTK_IS_WIDGET (child));
9367   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view));
9368
9369   vc = g_object_get_data (G_OBJECT (child),
9370                           "gtk-text-view-child");
9371
9372   g_assert (vc != NULL);
9373
9374   if (vc->x == xpos &&
9375       vc->y == ypos)
9376     return;
9377   
9378   vc->x = xpos;
9379   vc->y = ypos;
9380
9381   if (gtk_widget_get_visible (child) &&
9382       gtk_widget_get_visible (GTK_WIDGET (text_view)))
9383     gtk_widget_queue_resize (child);
9384 }
9385
9386
9387 /* Iterator operations */
9388
9389 /**
9390  * gtk_text_view_forward_display_line:
9391  * @text_view: a #GtkTextView
9392  * @iter: a #GtkTextIter
9393  * 
9394  * Moves the given @iter forward by one display (wrapped) line.
9395  * A display line is different from a paragraph. Paragraphs are
9396  * separated by newlines or other paragraph separator characters.
9397  * Display lines are created by line-wrapping a paragraph. If
9398  * wrapping is turned off, display lines and paragraphs will be the
9399  * same. Display lines are divided differently for each view, since
9400  * they depend on the view's width; paragraphs are the same in all
9401  * views, since they depend on the contents of the #GtkTextBuffer.
9402  * 
9403  * Return value: %TRUE if @iter was moved and is not on the end iterator
9404  **/
9405 gboolean
9406 gtk_text_view_forward_display_line (GtkTextView *text_view,
9407                                     GtkTextIter *iter)
9408 {
9409   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9410   g_return_val_if_fail (iter != NULL, FALSE);
9411
9412   gtk_text_view_ensure_layout (text_view);
9413
9414   return gtk_text_layout_move_iter_to_next_line (text_view->priv->layout, iter);
9415 }
9416
9417 /**
9418  * gtk_text_view_backward_display_line:
9419  * @text_view: a #GtkTextView
9420  * @iter: a #GtkTextIter
9421  * 
9422  * Moves the given @iter backward by one display (wrapped) line.
9423  * A display line is different from a paragraph. Paragraphs are
9424  * separated by newlines or other paragraph separator characters.
9425  * Display lines are created by line-wrapping a paragraph. If
9426  * wrapping is turned off, display lines and paragraphs will be the
9427  * same. Display lines are divided differently for each view, since
9428  * they depend on the view's width; paragraphs are the same in all
9429  * views, since they depend on the contents of the #GtkTextBuffer.
9430  * 
9431  * Return value: %TRUE if @iter was moved and is not on the end iterator
9432  **/
9433 gboolean
9434 gtk_text_view_backward_display_line (GtkTextView *text_view,
9435                                      GtkTextIter *iter)
9436 {
9437   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9438   g_return_val_if_fail (iter != NULL, FALSE);
9439
9440   gtk_text_view_ensure_layout (text_view);
9441
9442   return gtk_text_layout_move_iter_to_previous_line (text_view->priv->layout, iter);
9443 }
9444
9445 /**
9446  * gtk_text_view_forward_display_line_end:
9447  * @text_view: a #GtkTextView
9448  * @iter: a #GtkTextIter
9449  * 
9450  * Moves the given @iter forward to the next display line end.
9451  * A display line is different from a paragraph. Paragraphs are
9452  * separated by newlines or other paragraph separator characters.
9453  * Display lines are created by line-wrapping a paragraph. If
9454  * wrapping is turned off, display lines and paragraphs will be the
9455  * same. Display lines are divided differently for each view, since
9456  * they depend on the view's width; paragraphs are the same in all
9457  * views, since they depend on the contents of the #GtkTextBuffer.
9458  * 
9459  * Return value: %TRUE if @iter was moved and is not on the end iterator
9460  **/
9461 gboolean
9462 gtk_text_view_forward_display_line_end (GtkTextView *text_view,
9463                                         GtkTextIter *iter)
9464 {
9465   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9466   g_return_val_if_fail (iter != NULL, FALSE);
9467
9468   gtk_text_view_ensure_layout (text_view);
9469
9470   return gtk_text_layout_move_iter_to_line_end (text_view->priv->layout, iter, 1);
9471 }
9472
9473 /**
9474  * gtk_text_view_backward_display_line_start:
9475  * @text_view: a #GtkTextView
9476  * @iter: a #GtkTextIter
9477  * 
9478  * Moves the given @iter backward to the next display line start.
9479  * A display line is different from a paragraph. Paragraphs are
9480  * separated by newlines or other paragraph separator characters.
9481  * Display lines are created by line-wrapping a paragraph. If
9482  * wrapping is turned off, display lines and paragraphs will be the
9483  * same. Display lines are divided differently for each view, since
9484  * they depend on the view's width; paragraphs are the same in all
9485  * views, since they depend on the contents of the #GtkTextBuffer.
9486  * 
9487  * Return value: %TRUE if @iter was moved and is not on the end iterator
9488  **/
9489 gboolean
9490 gtk_text_view_backward_display_line_start (GtkTextView *text_view,
9491                                            GtkTextIter *iter)
9492 {
9493   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9494   g_return_val_if_fail (iter != NULL, FALSE);
9495
9496   gtk_text_view_ensure_layout (text_view);
9497
9498   return gtk_text_layout_move_iter_to_line_end (text_view->priv->layout, iter, -1);
9499 }
9500
9501 /**
9502  * gtk_text_view_starts_display_line:
9503  * @text_view: a #GtkTextView
9504  * @iter: a #GtkTextIter
9505  * 
9506  * Determines whether @iter is at the start of a display line.
9507  * See gtk_text_view_forward_display_line() for an explanation of
9508  * display lines vs. paragraphs.
9509  * 
9510  * Return value: %TRUE if @iter begins a wrapped line
9511  **/
9512 gboolean
9513 gtk_text_view_starts_display_line (GtkTextView       *text_view,
9514                                    const GtkTextIter *iter)
9515 {
9516   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9517   g_return_val_if_fail (iter != NULL, FALSE);
9518
9519   gtk_text_view_ensure_layout (text_view);
9520
9521   return gtk_text_layout_iter_starts_line (text_view->priv->layout, iter);
9522 }
9523
9524 /**
9525  * gtk_text_view_move_visually:
9526  * @text_view: a #GtkTextView
9527  * @iter: a #GtkTextIter
9528  * @count: number of characters to move (negative moves left, 
9529  *    positive moves right)
9530  *
9531  * Move the iterator a given number of characters visually, treating
9532  * it as the strong cursor position. If @count is positive, then the
9533  * new strong cursor position will be @count positions to the right of
9534  * the old cursor position. If @count is negative then the new strong
9535  * cursor position will be @count positions to the left of the old
9536  * cursor position.
9537  *
9538  * In the presence of bi-directional text, the correspondence
9539  * between logical and visual order will depend on the direction
9540  * of the current run, and there may be jumps when the cursor
9541  * is moved off of the end of a run.
9542  * 
9543  * Return value: %TRUE if @iter moved and is not on the end iterator
9544  **/
9545 gboolean
9546 gtk_text_view_move_visually (GtkTextView *text_view,
9547                              GtkTextIter *iter,
9548                              gint         count)
9549 {
9550   g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
9551   g_return_val_if_fail (iter != NULL, FALSE);
9552
9553   gtk_text_view_ensure_layout (text_view);
9554
9555   return gtk_text_layout_move_iter_visually (text_view->priv->layout, iter, count);
9556 }